封装RecyclerView添加头的适配器与嵌套ScrollView时的滑动冲突卡顿以及动画的添加
1.当RecyclerView嵌套在ScrollView里,滑动的时候会比较卡顿,解决办法
linearLayoutManager = new LinearLayoutManager(getActivity()) { @Override public boolean canScrollVertically() { return false;//解决RecyclerView嵌套ScrollView时滑动卡顿的问题 } }; linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL); recyclerView.setLayoutManager(linearLayoutManager);
然后在recyclerview的父布局加上:android:descendantFocusability="blocksDescendants"
<RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:descendantFocusability="blocksDescendants"> <android.support.v7.widget.RecyclerView android:background="@color/white" android:id="@+id/recyclerView_single" android:layout_width="match_parent" android:layout_height="match_parent" /> </RelativeLayout>
2.对RecyclerView添加头的封装,当然,你可以同理,继续增加添加尾的封装
/** * RecyclerView的 基本适配器 */ public abstract class MyBaseAdapter extends RecyclerView.Adapter{ /** * 头 */ public View headerView; /** * 头的类型 */ private final int HEADER_TYPE = 10000; private OnRecyclerItemClickListener mOnItemClickListener; /** * 添加头 * @param header */ public void addHeaderView(View header){ this.headerView = header; } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { if (viewType == HEADER_TYPE){ return new HeaderViewHolder(headerView); }else{ return onCreateViewHolder_my(parent, viewType); } } @Override public void onBindViewHolder(final RecyclerView.ViewHolder holder, int position) { int itemViewType = getItemViewType(position); if(itemViewType == HEADER_TYPE){ return; } if(headerView == null){ onBindViewHolder_my(holder,position); }else{ onBindViewHolder_my(holder,position - 1); } holder.itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { if(mOnItemClickListener != null){ if(headerView == null){ mOnItemClickListener.onItemClick(view,holder.getLayoutPosition()); }else{ mOnItemClickListener.onItemClick(view,holder.getLayoutPosition() - 1); } } } }); } @Override public int getItemCount() { if(headerView == null){ return getData().size(); } return getData().size() + 1; } @Override public int getItemViewType(int position) { if(headerView == null){ return getItemViewType_my(position); }else { //有头 if(position == 0){ return HEADER_TYPE; }else{ return getItemViewType_my(position - 1);//除去头 } } } /** * 头 */ private class HeaderViewHolder extends RecyclerView.ViewHolder { public HeaderViewHolder(View itemView) { super(itemView); } } /** * 针对GridLayoutManager,解决添加的头不会单独占据一行 * @param recyclerView */ @Override public void onAttachedToRecyclerView(RecyclerView recyclerView) { RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager(); if (layoutManager instanceof GridLayoutManager) { final GridLayoutManager gridLayoutManager = (GridLayoutManager) layoutManager; gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup(){ @Override public int getSpanSize(int position) { int viewType = getItemViewType(position); if (viewType == HEADER_TYPE) { return gridLayoutManager.getSpanCount();//跨距整个列数 } return 1;//正常情况为1列 } }); gridLayoutManager.setSpanCount(gridLayoutManager.getSpanCount()); } } public abstract List getData(); /** * 头类型 使用的10000 不同就行 * @param position 不用管包不包括头 * @return */ public abstract int getItemViewType_my(int position); public abstract RecyclerView.ViewHolder onCreateViewHolder_my(ViewGroup parent, int viewType); public abstract void onBindViewHolder_my(RecyclerView.ViewHolder holder, int position); /** * 处理item的点击事件 */ public interface OnRecyclerItemClickListener { void onItemClick(View view, int position);//点击的View,适配器中的索引position } /** * 暴露给外面的设置单击事件 */ public void setOnItemClickListener(OnRecyclerItemClickListener onItemClickListener){ mOnItemClickListener = onItemClickListener; } }
使用方法还是跟之前一样,不需要管有没有头对position的影响,测试适配器如下
public class TestAdapter extends MyBaseAdapter { private List<String> data; private Context context; public TestAdapter(List<String> data, Context context) { this.data = data; this.context = context; } @Override public List getData() { return data; } @Override public int getItemViewType_my(int position) { return 0;//如果没有多布局,返回0就行了 } @Override public RecyclerView.ViewHolder onCreateViewHolder_my(ViewGroup parent, int viewType) { View inflate = LayoutInflater.from(context).inflate(R.layout.recycler_item_layout, parent, false); return new MyViewHolder(inflate); } @Override public void onBindViewHolder_my(RecyclerView.ViewHolder holder, int position) { MyViewHolder myHolder = (MyViewHolder) holder; myHolder.textView.setText(data.get(position)); } class MyViewHolder extends RecyclerView.ViewHolder{ TextView textView; public MyViewHolder(View itemView) { super(itemView); textView = (TextView) itemView.findViewById(R.id.textView); } } }
使用:
mDatas = new ArrayList<String>(); testAdapter = new TestAdapter(mDatas,this); View headerView = View.inflate(this, R.layout.recycler_item_layout, null); TextView textView = headerView.findViewById(R.id.textView); textView.setText("我是头啊"); testAdapter.addHeaderView(headerView);//添加头 testAdapter.setOnItemClickListener(new MyBaseAdapter.OnRecyclerItemClickListener() { @Override public void onItemClick(View view, int position) { ToastUtils.showToast("点击了=" + mDatas.get(position)); } }); //recyclerView.setLayoutManager(new LinearLayoutManager(this)); recyclerView.setLayoutManager(new GridLayoutManager(this,3)); recyclerView.setAdapter(testAdapter);
3.为RecyclerView添加 滑动时,item进入屏幕的动画 跟上面相比,其实也就是增加了三个方法
public class TestAdapter extends MyBaseAdapter { private List<String> data; private Context context; private LinearLayoutManager linearLayoutManager; /** * 用来记录最后一个执行动画的索引 当刷新数据的时候,需要将该值值零(不然动画将不一致) */ private int mLastPosition = 0; public TestAdapter(List<String> data, Context context,LinearLayoutManager linearLayoutManager) { this.data = data; this.context = context; this.linearLayoutManager = linearLayoutManager; } @Override public List getData() { return data; } @Override public int getItemViewType_my(int position) { return 0;//如果没有多布局,返回0就行了 } @Override public RecyclerView.ViewHolder onCreateViewHolder_my(ViewGroup parent, int viewType) { View inflate = LayoutInflater.from(context).inflate(R.layout.recycler_item_layout, parent, false); return new MyViewHolder(inflate); } @Override public void onBindViewHolder_my(RecyclerView.ViewHolder holder, int position) { MyViewHolder myHolder = (MyViewHolder) holder; myHolder.textView.setText(data.get(position)); int firstVisibleItemPosition = linearLayoutManager.findFirstVisibleItemPosition(); if(position < mLastPosition && position > firstVisibleItemPosition){ LogUtil.e("上拉加载更新notifyDataSetChanged()后,之前显示的item不再执行动画"); }else{ //添加动画 setAnimation(holder.itemView, position); } } /** * 将动画对象加入集合中 根据左右滑动加入不同 */ private void setAnimation(View view, int position) { if (position >= mLastPosition) { // 执行底部的item进入屏幕的动画 Animation animation = AnimationUtils.loadAnimation(view.getContext(), R.anim.item_slide_bottom_up); view.startAnimation(animation); mLastPosition = position; }else{ // 执行顶部的item进入屏幕的动画 Animation animation = AnimationUtils.loadAnimation(view.getContext(), R.anim.item_slide_top_down);//上面进入屏幕的动画 view.startAnimation(animation); mLastPosition = position; } } /** * 将离开屏幕的item,清除动画,防止快速滑动时数据错乱与卡屏 * @param holder */ @Override public void onViewDetachedFromWindow(RecyclerView.ViewHolder holder) { super.onViewDetachedFromWindow(holder); holder.itemView.clearAnimation(); } class MyViewHolder extends RecyclerView.ViewHolder{ TextView textView; public MyViewHolder(View itemView) { super(itemView); textView = (TextView) itemView.findViewById(R.id.textView); } } }
使用如下:
final LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this); testAdapter = new TestAdapter(mDatas,this,linearLayoutManager); View headerView = View.inflate(this, R.layout.recycler_item_layout, null); TextView textView = (TextView) headerView.findViewById(R.id.textView); textView.setText("我是头啊"); testAdapter.addHeaderView(headerView); testAdapter.setOnItemClickListener(new MyBaseAdapter.OnRecyclerItemClickListener() { @Override public void onItemClick(View view, int position) { ToastUtils.showToast("点击了=" + mDatas.get(position)); } }); recyclerView.setLayoutManager(linearLayoutManager); recyclerView.setAdapter(testAdapter);
动画文件:
item_slide_bottom_up.xml
<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android"> <translate android:fromXDelta="100%" android:toXDelta="0%" android:fromYDelta="100%" android:toYDelta="0%" android:duration="1000"/> <alpha android:fromAlpha="0" android:toAlpha="1" android:duration="1000"/> </set>
item_slide_top_down.xml
<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android"> <translate android:fromXDelta="-100%" android:toXDelta="0%" android:fromYDelta="-100%" android:toYDelta="0%" android:duration="1000"/> <alpha android:fromAlpha="0" android:toAlpha="1" android:duration="1000"/> </set>
效果图:当第二次刷新时,第一条item的动画是从上往下的,所以为了避免这个问题,在刷新的时候需要手动将 mLastPosition=0 (切记)