android 使用recyclerview实现Gallery画廊效果(二)
一、概述
项目中有需要画廊的展示效果,传统通过Viewpager 设置属性clipChildren为false实现,我的上一篇文章做了详细的介绍,但是根据ViewPager实现的画廊效果一次只可以滑动一个,滑动流畅性不是很好,但是使用RecyclerView可以一次性滑动多个,并且可以居中显示。如图:
源码地址:https://github.com/FollowExcellence/RecyclerView_Gallery
二、实现原理
1.重写RecyclerView的fling(int velocityX, int velocityY)方法,控制RecyclerView的滑动速度。
2.support:recyclerview-v7:24包中增加一个非常重要的类SnapHelper,SnapHelper的实现原理就是通过方法mLinearSnapHelper.attachToRecyclerView(mRecyclerView)监听RecyclerView.OnFlingListener中的onFling接口,他的作用是让RecyclerView滑动视图后让当前item停留的位置正好在当前页的正中间。
3.滑动过程中进行缩放,RecyclerView的滑动缩放必须监听RecyclerView的滑动,mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener()。
三、实现步骤
1.新建类继承RecyclerView,重写fling(int velocityX, int velocityY)方法,控制RecyclerView的滑动速度。
public class SpeedRecyclerView extends RecyclerView {
private static final float FLING_SCALE_DOWN_FACTOR = 0.5f; // 减速因子
private static final int FLING_MAX_VELOCITY = 8000; // 最大顺时滑动速度
public SpeedRecyclerView(Context context) {
super(context);
}
public SpeedRecyclerView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public SpeedRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
public boolean fling(int velocityX, int velocityY) {
velocityX = solveVelocity(velocityX);
velocityY = solveVelocity(velocityY);
return super.fling(velocityX, velocityY);
}
private int solveVelocity(int velocity) {
if (velocity > 0) {
return Math.min(velocity, FLING_MAX_VELOCITY);
} else {
return Math.max(velocity, -FLING_MAX_VELOCITY);
}
}
}
2.实现类SnapHelper 监听RecyclerView.OnFlingListener中的onFling接口,mLinearSnapHelper绑定RecyclerView监听
mLinearSnapHelper.attachToRecyclerView(mRecyclerView);
通过上面的方法找到居中View和居中的位置,监听RecyclerView.OnFling方法,显示在居中的位置。
3.滑动过程进行缩放,必须监听RecyclerView的addOnScrollListener(new RecyclerView.OnScrollListener()方法,在滑动过程进行计算和缩放
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
// dx>0则表示右滑, dx<0表示左滑, dy<0表示上滑, dy>0表示下滑
if(dx != 0){//去掉奇怪的内存疯涨问题
mCurrentItemOffset += dx;
computeCurrentItemPos();
// LogUtils.v(String.format("dx=%s, dy=%s, mScrolledX=%s", dx, dy, mCurrentItemOffset));
onScrolledChangedCallback();
}
}
});
mCurrentItemOffset为滑动总距离,根据间距mPagePadding和左右卡片可见的大小来计算卡片实际显示宽度,Card每页滑动的距离是固定的,根据这个可以计算出当前页面显示的位置。
有了滑动位置就能实时计算滑动某页的百分比,得到percent,再获取当前位置相邻的视图调用setScaleY函数实现缩放。
/**
* RecyclerView位移事件监听, view大小随位移事件变化
*/
private void onScrolledChangedCallback() {
int offset = mCurrentItemOffset - mCurrentItemPos * mOnePageWidth;
float percent = (float) Math.max(Math.abs(offset) * 1.0 / mOnePageWidth, 0.0001);
// LogUtils.d(String.format("offset=%s, percent=%s", offset, percent));
View leftView = null;
View currentView;
View rightView = null;
if (mCurrentItemPos > 0) {
leftView = mRecyclerView.getLayoutManager().findViewByPosition(mCurrentItemPos - 1);
}
currentView = mRecyclerView.getLayoutManager().findViewByPosition(mCurrentItemPos);
if (mCurrentItemPos < mRecyclerView.getAdapter().getItemCount() - 1) {
rightView = mRecyclerView.getLayoutManager().findViewByPosition(mCurrentItemPos + 1);
}
if (leftView != null) {
// y = (1 - mScale)x + mScale
leftView.setScaleY((1 - mScale) * percent + mScale);
}
if (currentView != null) {
// y = (mScale - 1)x + 1
currentView.setScaleY((mScale - 1) * percent + 1);
}
if (rightView != null) {
// y = (1 - mScale)x + mScale
rightView.setScaleY((1 - mScale) * percent + mScale);
}
}
4.设置数据和计算大小,在adapter中RecyclerView创建View的时候onCreateViewHolder(ViewGroup parent, int viewType)
计算View的宽度
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false);
mCardAdapterHelper.onCreateViewHolder(parent, itemView);
return new ViewHolder(itemView);
}
public void onCreateViewHolder(ViewGroup parent, View itemView) {
RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) itemView.getLayoutParams();
lp.width = parent.getWidth() - ScreenUtil.dip2px(itemView.getContext(), 2 * (mPagePadding + mShowLeftCardWidth));
itemView.setLayoutParams(lp);
}
在onBindViewHolder(View itemView, final int position, int itemCount)计算padding的大小
@Override
public void onBindViewHolder(final ViewHolder holder, final int position) {
mCardAdapterHelper.onBindViewHolder(holder.itemView, position, getItemCount());
}
public void onBindViewHolder(View itemView, final int position, int itemCount) {
int padding = ScreenUtil.dip2px(itemView.getContext(), mPagePadding);
itemView.setPadding(padding, 0, padding, 0);
int leftMarin = position == 0 ? padding + ScreenUtil.dip2px(itemView.getContext(), mShowLeftCardWidth) : 0;
int rightMarin = position == itemCount - 1 ? padding + ScreenUtil.dip2px(itemView.getContext(), mShowLeftCardWidth) : 0;
setViewMargin(itemView, leftMarin, 0, rightMarin, 0);
}
private void setViewMargin(View view, int left, int top, int right, int bottom) {
ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) view.getLayoutParams();
if (lp.leftMargin != left || lp.topMargin != top || lp.rightMargin != right || lp.bottomMargin != bottom) {
lp.setMargins(left, top, right, bottom);
view.setLayoutParams(lp);
}
}
adapter设置数据和方向性
private void initRecyclerView() {
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false);
mRecyclerView.setLayoutManager(linearLayoutManager);
mRecyclerView.setAdapter(new MyAdapter(this, mPics));
// mRecyclerView绑定scale效果
mCardScaleHelper = new CardScaleHelper();
mCardScaleHelper.setCurrentItemPos(1);
mCardScaleHelper.attachToRecyclerView(mRecyclerView);
}
具体细节和其他代码可以参考源码
欢迎留言评论!
上一篇: 为什么说秦桓公被晋国坑惨了?真相是什么
下一篇: Vue.js的基本常用指令
推荐阅读
-
Android中RecyclerView实现多级折叠列表效果(二)
-
Android 使用自定义RecyclerView控件实现Gallery效果
-
Android使用Recyclerview实现图片水平自动循环滚动效果
-
Android 使用Gallery实现3D相册(附效果图+Demo源码)
-
Android中RecyclerView实现多级折叠列表效果(二)
-
Android开发实现Gallery画廊效果的方法
-
Android使用Recyclerview实现图片水平自动循环滚动效果
-
Android 使用Gallery实现3D相册(附效果图+Demo源码)
-
Android 使用viewpager实现画廊式效果(展示图片)
-
实现拖拽效果二: 使用RecyclerView + ItemTouchHelper 实现拖拽、删除,滑动删除效果