RecyclerView的 item侧滑拖动、添加head、footview、下拉刷新。

来源:互联网 发布:python 模拟火势蔓延 编辑:程序博客网 时间:2024/05/23 23:43

越来越喜欢RecylerView了,尤其配合上CardView,界面简直就瞬间高大上了。。。大笑

先说个题外话,我的Android studio2.1.1,电脑还算可以,不过用AS经常电脑卡死!对,卡到任务管理器都开不了,点什么都没用。网上查了查。解决方法:setting--System setting--Updates--然后把两个选择框都取消了。果然解决了! 当然日常卡的话,不要管,喝点水回来就好了。。。安静

(公司电脑不能联网。。小代码就先直接贴出来了,以后把代码都搞Github上。看着不爽,还见谅了。。)

进入主题:本篇博文 分享的内容是用ItemTouchHelp类实现侧滑、拖动,headview和footview的添加(三种layoutmanager)、下拉刷新

先来张效果图:



1.添加headerView、footerView

使用RecyclerView发现没有和listView一样的设置头,尾项的方法了。那好,咱们自己搞!条条大路通罗马。。

RecyclerView中有Type机制。有个type的回调方法--getItemViewType(position);


那好我们就来用Type机制。我们以添加个FooterView为例吧。

(1)思路:

1.再写一个ViewHolder,作为FooterView的Holder。

2.重写public int getItemViewType(int position);方法。这个方法中,根据position返回不同的Type。比如position为数据的最后一项时,返回Type为FooterView。

3.重写的public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)方法。这个方法中根据参数viewType,来返回不同的ViewHolder。

4.重写的public void onBindViewHolder(RecyclerView.ViewHolder holder, int position)方法。通过holder instanceof XXViewHolder,来进行不同的item数据显示。

5.getItemCount()方法。可别忘记把新增的数量加上去。比如加了N个FooterView,就要+N;


好了,贴下几个方法具体代码了:

 @Override    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {        if (viewType == TYPE_ITEM) {            View inflate = LayoutInflater.from(mContext).inflate(R.layout.list_item, parent, false);            return new MyViewHolder(inflate);        } else {            View inflate = LayoutInflater.from(mContext).inflate(R.layout.footer_item, parent, false);            return new FooterHolder(inflate);        }    }    @Override    public int getItemViewType(int position) {        if (position == getItemCount() - 1) {            return TYPE_FOOTER;        }        return TYPE_ITEM;    }    @Override    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {        if (holder instanceof MyViewHolder) {            ((MyViewHolder) holder).setContentText(datas.get(position));            ((MyViewHolder) holder).setDateText("2020-09-0" + position);            ((MyViewHolder) holder).setItemClick();            ((MyViewHolder) holder).setItemLongClick();            ((MyViewHolder) holder).setImage();        } else if (holder instanceof FooterHolder) {        }    }    @Override    public int getItemCount() {        return datas.size() + 1;    }

(2)兼容各种layoutManager:

还没结束。当我们把RecyclerView切换成 GridLayout或瀑布显示模式时。。。你发现当RecyclerView多列显示时,FooterView的宽只是占了一列,而讲道理宽不应该是整个列表的宽吗。。

1.解决瀑布layoutmanager:

我是先解决的GridLayoutManager,瀑布的 这个解决方法我是参考其他博客的,但是我个人觉得那种解决方法有待改善(当然个人技术有限,不一定就是好的,大家可以一起反馈下)。
a.首先说说我在网上看到的解决方法:
   @Override    public void onViewAttachedToWindow(RecyclerView.ViewHolder holder) {        super.onViewAttachedToWindow(holder);        Log.e("TAG","onViewAttachedToWindow");        ViewGroup.LayoutParams layoutParams = holder.itemView.getLayoutParams();        if (holder.getItemViewType() == TYPE_FOOTER && layoutParams instanceof StaggeredGridLayoutManager.LayoutParams) {            ((StaggeredGridLayoutManager.LayoutParams) layoutParams).setFullSpan(true);       }    }
是在onViewAttachedToWindow方法下,将对应的一项的layoutParams设置为全宽。
b.思考:
onViewAttachedToWindow这个方法会在滚动中只要一个item出现就会执行一次,它的执行次数远大于onCreateViewHolder(ViewGroup parent, int viewType)。而viewHolder的特性就是保存个别item的状态,重用,layoutParams自然也只需要设置一次。所以我们只需要在onCreateViewHolder方法中,判断此item是否是FooterView,如果是则设置全宽。这样设置params的次数就大大降低了。
c.我的解决方法:
在onAttachedToRecyclerView(RecyclerView recyclerView) 方法中,用一个boolean成员变量记录是否是瀑布layoutmanager。
看码:
    @Override    public void onAttachedToRecyclerView(RecyclerView recyclerView) {        Log.e("TAG", "onAttachedToRecyclerView");        super.onAttachedToRecyclerView(recyclerView);        isStaggerid = false;//重置标记位        final RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();        if (layoutManager instanceof GridLayoutManager) {            final GridLayoutManager manager = (GridLayoutManager) layoutManager;            manager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {                @Override                //该方法返回此position要占用的span.                public int getSpanSize(int position) {                    int viewType = getItemViewType(position);                    if (viewType == TYPE_FOOTER) {                        //分两列,则return 2;表示占两个                        return ((GridLayoutManager) layoutManager).getSpanCount();                    } else {                        //其余正常项,占用一个                        return 1;                    }                }            });        } else if (layoutManager instanceof StaggeredGridLayoutManager) {            isStaggerid = true;        }    }
在onCreateViewHolder中判断并设置params:
@Override    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {        Log.e("TAG", "onCreateViewHolder");        RecyclerView.ViewHolder myViewHolder = null;        if (viewType == TYPE_ITEM) {            View inflate = LayoutInflater.from(mContext).inflate(R.layout.list_item, parent, false);            myViewHolder = new MyViewHolder(inflate);        } else if (viewType == TYPE_FOOTER) {            View inflate = LayoutInflater.from(mContext).inflate(R.layout.footer_item, parent, false);            myViewHolder = new FooterHolder(inflate);        }        if(myViewHolder!=null){            ViewGroup.LayoutParams layoutParams = myViewHolder.itemView.getLayoutParams();            //如果type是FooterView,并且是瀑布流manager,就改变其layoutParams为全宽            if (viewType == TYPE_FOOTER && isStaggerid) {                ((StaggeredGridLayoutManager.LayoutParams) layoutParams).setFullSpan(true);            }        }        return myViewHolder;    }

OK!这么就解决了!

2.解决GridLayoutManager:

那好,我们看看这个adpater里面还有什么可以重写的方法。

发现嫌疑方法:

1.public void onAttachedToRecyclerView(RecyclerView recyclerView):看方法名,大概明了了。并且参数是recyclerView,这样就可以获取到当前的layoutManger了。

再看看源码,发现有好多span相关的方法。GridLayoutManager类里面有一个这方法:

public void setSpanSizeLookup(GridLayoutManager.SpanSizeLookup spanSizeLookup)Sets the source to get the number of spans occupied by each item in the adapter.

不管怎样,试试吧。需要实现getSpanSize(int position)方法。此方法注释:Returns the number of span occupied by the item at position.(所占据的span数量。。)

先根据position获取到Type,如果type是footerView等,则返回占据全部span。反之,返回正常item占据的span数量。

该方法代码:

@Override    public void onAttachedToRecyclerView(RecyclerView recyclerView) {        Log.e("TAG", "onAttachedToRecyclerView");        super.onAttachedToRecyclerView(recyclerView);        isStaggerid = false;//重置标记位        final RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();        if (layoutManager instanceof GridLayoutManager) {            final GridLayoutManager manager = (GridLayoutManager) layoutManager;            manager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {                @Override                //该方法返回此position要占用的span.                public int getSpanSize(int position) {                    int viewType = getItemViewType(position);                    if (viewType == TYPE_FOOTER) {                        //分两列,则return 2;表示占两个                        return ((GridLayoutManager) layoutManager).getSpanCount();                    } else {                        //其余正常项,占用一个                        return 1;                    }                }            });        } else if (layoutManager instanceof StaggeredGridLayoutManager) {            isStaggerid = true;        }    }

GridLayoutManager解决!

(3)总结:

这中type方法,不仅仅是HeaderView,FooterView,分组功能也是可以的。很方便的设计!

2.ItemTouchHelp实现item的拖动,侧滑

如果使用listView实现拖动侧滑那可是令人头疼的事。但RecyclerView要实现这效果只需要用ItemTouchHelp类,在Adapter构造器中初始化这个类:
 public MyRecyclerViewAdapter(Context context, final ArrayList<String> datas, RecyclerView recyclerView) {        this.mContext = context;        this.datas = datas;        this.view = recyclerView;        //手势帮助类        ItemTouchHelper touchHelper = new ItemTouchHelper(new ItemTouchHelper.Callback() {            private RecyclerView.ViewHolder vh;            @Override            public boolean isItemViewSwipeEnabled() {                return true;            }            /**             * 在某个Item被拖动和移动时回调,这里用来播放动画,当viewHolder不为空时为选中状态,否则为释放状态。             * @param viewHolder             * @param actionState             */            @Override            public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {                super.onSelectedChanged(viewHolder, actionState);                if (viewHolder != null) {                    pickUpAnimation(viewHolder.itemView);                    vh = viewHolder;                } else {                    if (vh != null) {                        putDownAnimation(vh.itemView);                    }                }            }            /**             * 设置滑动,移动方向             * @param recyclerView             * @param viewHolder             * @return             */            @Override            public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {                if (viewHolder instanceof FooterHolder) {                    return 0;                }                //拖动标记方向                int dragDirection = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;                //swipe 的标记方向                int swipeDirection = ItemTouchHelper.START | ItemTouchHelper.END;             //   int swipeDirection = 0;                return makeMovementFlags(dragDirection, swipeDirection);            }            /**             * 当移动至要交换的程度,执行此方法             * @param recyclerView             * @param viewHolder             * @param target             * @return             */            @Override            public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {                if (!(target instanceof FooterHolder)) {                    Collections.swap(datas, viewHolder.getAdapterPosition(), target.getAdapterPosition());                    return true;                }                return false;            }            /**             * 在onMove后马上执行此方法             */            @Override            public void onMoved(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, int fromPos, RecyclerView.ViewHolder target, int toPos, int x, int y){                Log.e("TAG", "onMoved");                super.onMoved(recyclerView, viewHolder, fromPos, target, toPos, x, y);                MyRecyclerViewAdapter.this.notifyItemMoved(viewHolder.getAdapterPosition(), target.getAdapterPosition());            }            /**             * 滑动之后回调             * @param viewHolder             * @param direction             */            @Override            public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {                int layoutPosition = viewHolder.getAdapterPosition();                notifyItemRemoved(layoutPosition);                MyRecyclerViewAdapter.this.datas.remove(layoutPosition);            }            @Override            public float getSwipeEscapeVelocity(float defaultValue) {                return super.getSwipeEscapeVelocity(defaultValue);            }        });        touchHelper.attachToRecyclerView(view);    }
private void pickUpAnimation(View view) {        ObjectAnimator animator = ObjectAnimator.ofFloat(view, "translationZ", 5f, 20f);        animator.setInterpolator(new DecelerateInterpolator());        animator.setDuration(300);        animator.start();    }    private void putDownAnimation(View view) {        ObjectAnimator animator = ObjectAnimator.ofFloat(view, "translationZ", 20f, 5f);        animator.setInterpolator(new DecelerateInterpolator());        animator.setDuration(300);        animator.start();    }

(1) 几行代码重点:

1.public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState)
这个方法会在 触发侧滑和拖动之前回调。参数viewHolder当选中时不为null,反之为null。当不为null时,则执行动画,提高CardView 的高度,并记录下来操作的viewholder。当参数为null时,将上次记录的viewholder的cardView 的高度回归原位。
2. public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) 
这个方法用来设置 可以触发侧滑和拖动 的方向。 return makeMovementFlags(dragDirection, swipeDirection);参数为0的动作,将无法触发。直接return0,则两个动作都无法触发。当然,headerView和footerView要排除掉。
3.public booleanonMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) 
这个方法会在动作程度到达系统 认定为拖动交换成功 时调用。这里要做的是将数据 进行交换。
4.public voidonMoved(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, int fromPos, RecyclerView.ViewHolder target, int toPos, int x, int y)
这个方法会在onMove后马上调用,这里要做的是通知RecyclerView数据发生了交换。
5. touchHelper.attachToRecyclerView(view);
最后不要忘记将touchHelper绑定到RecyclerView上。

(2)注意点

1.默认的长按item执行拖动。
2.注意在getMovementFlags方法中将不能动的item返回0(比如FooterView等)。在onMove方法中,控制不能与FooterView等交换。
3.删除,添加交换等要使用notifyItemRemoved()等notifyItemXXX()方法,否则没有动画效果。(当然要提前给RecyclerView设置动画-->recyclerView.setItemAnimator(new DefaultItemAnimator());

3.下拉刷新:

在项目没有特殊需求下,使用support-v4中 SwipRefreshLayout类,就可以实现简单的下拉刷新,效果还可以吧。

(1)用SwipRefreshLayout包裹RecyclerView就OK了。

     <android.support.v4.widget.SwipeRefreshLayout            android:id="@+id/swiperefresh"            android:layout_width="match_parent"            android:layout_height="match_parent"            android:layout_below="@+id/toolbar">            <android.support.v7.widget.RecyclerView                android:id="@+id/recyclerview"                android:layout_width="match_parent"                android:layout_height="match_parent"                android:scrollbars="vertical"></android.support.v7.widget.RecyclerView>        </android.support.v4.widget.SwipeRefreshLayout>

2)可以设置一下监听:

public void setOnRefreshListener(SwipeRefreshLayout.OnRefreshListener listener);需要实现onRefresh()方法;通过swipelayout.setRefreshing(boolean),方法来控制控件的状态:
/**     * 下拉刷新的回调     */    @Override    public void onRefresh() {        swipLayout.setRefreshing(true);        new Handler().postDelayed(new Runnable() {            @Override            public void run() {                String s = "Refreshing!!!!!!!!!!!!!";                datas.set(0, s);                swipLayout.setRefreshing(false);                adapter.notifyDataSetChanged();            }        }, 4000);    }

3)设置其他效果

 swipLayout.setColorSchemeColors(Color.YELLOW, Color.BLACK, Color.BLUE, Color.RED);
加载时,小圆环会顺序变色
swipLayout.setProgressBackgroundColorSchemeColor(Color.DKGRAY);
圆盘的背景色
等等。。。
 

4.总结

总体来说,RecyclerView还是不错的,注意的地方不少:数据动态变化时,要用getLayoutPosition(),adapter代码有点冗余等。新的support包,design包中神奇的东西真是不少。自己在学习研究中觉得可能有用的知识都会写出来,也算笔记,天天网上看大神的分享,自己也该出点货回馈下了。希望能对大家有所帮助吧。有什么问题,欢迎反馈。ThanX!
1 0
原创粉丝点击