码迷,mamicode.com
首页 > 其他好文 > 详细

RecyclerView学习笔记

时间:2016-07-17 00:08:50      阅读:266      评论:0      收藏:0      [点我收藏+]

标签:

自学习android以来,其实一直都有接触到 RecyclerView,今天便总结一下关于RecyclerView的相关知识,并不是非常全面。主要从以下几个方面:

  1. RecyclerView概述
  2. RecyclerView与ListView区别
  3. RecyclerView基本使用
  4. RecyclerView item单击与长按事件
  5. RecyclerView item长按拖拽和侧滑删除

源码地址:https://github.com/Ti2Yuan/RecyclerViewDemo


1. RecyclerView概述

2014年Google IO的召开,Android L Preview版发布,对于开发者而言,它带来了性能上的改善。其中,一个全新的控件也进入开发者的视野中,并得到越来越多的使用,大有取代ListView的趋势,它就是RecyclerView。

    A flexible view for providing a limited window into a large data set.        

上面那句话是官网中对RecyclerView的描述:能在有限的窗口中显示大数据集的灵活视图。
RecyclerView是Google support-v7包下新增的控件,用来替代ListView的使用,RecyclerView标准化了ViewHolder类似于ListView中convertView用来做视图缓存。但是它却是ListView的增强版。


2.RecyclerView与ListView区别

正如官方文档所言,RecyclerView是ListView的豪华增强版。它主要包含以下几处新的特性,如ViewHolder,ItemDecorator,LayoutManager,SmoothScroller以及增加或删除item时item动画等。官方推荐我们采用RecyclerView来取代ListView。

ViewHolder
ViewHolder是用来保存视图引用的类,在ListView中,ViewHolder需要自己来定义,但只是一种推荐的使用方式,不是必须要使用的。取而代之的是ListView性能的迟缓,因为ListView每次getView的时候都会调用findViewById(int)方法。而在RecyclerView中则强调必须使用RecyclerView.ViewHolder,否则代码将不能运行。虽然这个过程实现起来稍显复杂,但是却避免了ListView不使用ViewHolder而带来的性能问题。

LayoutManager
ListView只能在垂直方向上滚动,google并没有给出ListView在水平方向上面滚动的Android API支持。但是RecyclerView相较于ListView,在滚动上可以支持多种类型列表,例如:

LinearLayoutManager,可以支持水平和竖直方向上滚动的列表。

StaggeredGridLayoutManager,可以支持交叉网格风格的列表,类似于瀑布流或者Pinterest。

GridLayoutManager,支持网格展示,可以水平或者竖直滚动,如展示图片的画廊。

ItemAnimator
列表动画是一个全新的、拥有无限可能的维度。起初的Android API中,删除或添加item时,item是无法产生动画效果的。后面随着Android的进化,Google的Chat Hasse推荐使用ViewPropertyAnimator属性动画来实现上述需求。
RecyclerView.ItemAnimator用于在RecyclerView中添加、删除或移动item时处理动画效果。同时也存在一个默认的动画效果DefaultItemAnimator。而ListView则不具备这种API。

Adapter
在ListView的Adapter中,getView方法将视图跟position绑定在一起。同时我们能通过registerDataObserver在Adapter中注册一个观察者。在RecyclerView中,我们也可以通过RecyclerView.AdapterDataObserver观察。ListView有三个Adapter的默认实现,分别是ArrayAdapter、CursorAdapter和SimpleCursorAdapter。然而,RecyclerView的Adapter则拥有除了内置的内DB游标和ArrayList的支持之外的所有功能。RecyclerView.Adapter的实现中,我们必须采取措施将数据提供给Adapter。

ItemDecoration
在ListView中我们可以通过divider和dividerHeight这些相关属性为item添加间隔符。但是在RecyclerView中可通过RecyclerView.ItemDecoration类来实现自定义间隔符。默认情况下item之间不会展示间隔符。这的确增加了开发人员的负担,如果你想要添加间隔符的话。你还可以参考官方示例中的DividerItemDecoration.java文件。

OnItemTouchListener
ListView通过AdapterView.OnItemClickListener接口来探测点击事件。而RecyclerView则可以通过RecyclerView.OnItemTouchListener接口来探测触摸事件。这种探测方式虽然增加了实现的难度,但是却给予开发人员拦截触摸事件更多的控制权限。

还有就是ListView可以添加MultiChoiceModeListener来设置选择模式,但是RecyclerView却没有。

总结:
recyclerView自定义强,可以实现复杂的布局,但过程稍显复杂。


3.RecyclerView基本使用

首先,在moudle下的build.gradle导入包

dependencies {
    compile ‘com.android.support:recyclerview-v7:23.2.0‘
}

然后在布局中

<android.support.v7.widget.RecyclerView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/recycleView"
        android:layout_margin="10dp"
        android:scrollbars="vertical"/>`

然后就是为RecyclerView设置LayoutManager,Adapter,ItemAnimator和ItemDecoration。而自定义的RecyclerViewAdapter中必须继承RecyclerView.Adapter,并且实现需要重写的方法,如onCreateViewHolder,onBindViewHolder,getItemCount()等等。并且可通过getItemViewType(int position)方法返回item 类型,可用于返回headItem和FootItem类型。

recyclerView =(RecyclerView)findViewById(R.id.recycleView);
mLayoutManager = new LinearLayoutManager(this);
adapter = new RecyclerViewAdapter(this, list);

recyclerView.setAdapter(adapter);
recyclerView.setLayoutManager(mLayoutManager);
recyclerView.setItemAnimator(new DefaultItemAnimator());
recyclerView.addItemDecoration(new RecyclerView.ItemDecoration() {
            @Override
      public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
                super.onDraw(c, parent, state);
            }
        });
@Override
    public int getItemViewType(int position) {
        if (position == 0) {
            return TYPE_HEAD;
        } else if (position + 1 == getItemCount()) {
            return TYPE_FOOT;
        } else {
            return TYPE_ITEM;
        }
    }

4. RecyclerView item单击与长按事件

一般情况下,我们可以在自定义RecyclerView.Adapter的Adapter类中为ViewHolder的rootView设置OnClickListener()来监听item的点击事件,但这种方式或多或少浪费性能,而存在一种更加高逼格的方式来实现诸如单击和长按事件,就是通过探测手势触摸的方式。一下是使用方法:

 recyclerView.addOnItemTouchListener(new OnRecyclerViewItemTouchListener(recyclerView) {
            @Override
            protected void onItemClick(RecyclerView.ViewHolder vh) {

            }

            @Override
            protected void onItemLongClick(RecyclerView.ViewHolder vh) {

            }
        });
public abstract class OnRecyclerViewItemTouchListener implements RecyclerView.OnItemTouchListener {

    private RecyclerView recyclerView;
    private GestureDetectorCompat gestureDetector;

    public OnRecyclerViewItemTouchListener(RecyclerView recyclerView) {
        this.recyclerView = recyclerView;
        gestureDetector = new GestureDetectorCompat(recyclerView.getContext(),
                new ItemTouchHelperGestureListener());
    }

    @Override
    public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
        gestureDetector.onTouchEvent(e);
        return false;
    }

    @Override
    public void onTouchEvent(RecyclerView rv, MotionEvent e) {
        gestureDetector.onTouchEvent(e);
    }

    @Override
    public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {

    }

    private class ItemTouchHelperGestureListener extends GestureDetector.SimpleOnGestureListener {
        @Override
        public boolean onDown(MotionEvent motionEvent) {
            return false;
        }

        @Override
        public void onShowPress(MotionEvent motionEvent) {

        }

        /**
         * 单击事件
         * @param motionEvent
         * @return
         */
        @Override
        public boolean onSingleTapUp(MotionEvent motionEvent) {
            View child = recyclerView.findChildViewUnder(motionEvent.getX(),motionEvent.getY());
            if(child != null){
                RecyclerView.ViewHolder vh = recyclerView.getChildViewHolder(child);
                onItemClick(vh);
            }
            return true;
        }

        @Override
        public boolean onScroll(MotionEvent motionEvent, MotionEvent motionEvent1, float v, float v1) {
            return false;
        }

        /**
         * 长按事件
         * @param motionEvent
         */
        @Override
        public void onLongPress(MotionEvent motionEvent) {
            View child = recyclerView.findChildViewUnder(motionEvent.getX(),motionEvent.getY());
            if(child != null){
                RecyclerView.ViewHolder vh = recyclerView.getChildViewHolder(child);
                onItemLongClick(vh);
            }
        }

        @Override
        public boolean onFling(MotionEvent motionEvent, MotionEvent motionEvent1, float v, float v1) {
            return false;
        }
    }

    protected abstract void onItemClick(RecyclerView.ViewHolder vh);
    protected abstract void onItemLongClick(RecyclerView.ViewHolder vh);

}

RecyclerView提供了设置触摸监听的方法,那么我们定义一个类OnRecyclerViewItemTouchListener实现OnItemTouchListener。重写3个方法OnInterceptTouchEvent,onTouchEvent,onRequestDisallowInterceptTouchEvent。其中第三个方法是处理触摸事件冲突的,前两个方法就是View的事件分发机制里面的事件拦截和事件处理的两个方法,参数里为我们提供了触摸事件的数据MotionEvent,我们要做的就是去解析坐标点和触摸规律来识别触摸手势,然后获取触摸的是哪一个item,sdk已经为我们实现了手势的识别:

GestureDetectorCompat 就是处理手势的类:手势探测器,它比GestureDetector能更好兼容低版本的api,但使用方法是一致的,我们实例化一个手势探测器:

gestureDetector = new GestureDetectorCompat(recyclerView.getContext(),
                new ItemTouchHelperGestureListener());

实例化手势探测器的时候需要提供一个手势监听器:OnGestureListener,探测器识别出手势后就会回调手势监听器中对应的方法,我们就可以在回调方法中做我们想做的事情了。

sdk为我们提供了两个手势监听器:OnGestureListener,OnDoubleTapListener。

OnGestureListener主要回调各种单击事件,而OnDoubleTapListener回调各种双击事件。而我们需要处理的点击事件其实就是上面的:onSingleTapUp()。而sdk 还提供了一个外部类SimpleOnGestureListener,这个类实现了上面两个接口的所有方法,但全都是空实现,函数体里什么也没写,其中就是把上面两个接口合并一下,给出默认的空实现,这样继承SimpleOnGestureListener的时候就不用实现每一个方法了。RecyclerView已经为我们提供了findChildViewUnder(),我们可以通过这个方法获得点击的item,同时我们调用RecyclerView的另一个方法getChildViewHolder(),可以获得该item的ViewHolder,最后再回调我们定义的虚方法onItemClick()就ok了,这样我们就可以在外部实现该方法来获得item的点击事件了。


5. RecyclerView item长按拖拽和侧滑删除

可以通过ItemTouchHelper这个类来实现RecyclerView item的拖拽和滑动。

 This is a utility class to add swipe to dismiss and drag & drop support to RecyclerView. 

这是Google官方文档描述这个类的话:这是一个支持RecyclerView滑动删除和拖拽的实用工具类。

具体用法如下:

 itemTouchHelper = new ItemTouchHelper(new ItemTouchHelpCallback(this,list) {
            @Override
            public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
                int position = viewHolder.getLayoutPosition();
                if(position != 0 && position != recyclerView.getAdapter().getItemCount() - 1) {
                    adapter.notifyItemRemoved(position);
                    list.remove(position-1);
                }
            }
        });

        itemTouchHelper.attachToRecyclerView(recyclerView);
public abstract class ItemTouchHelpCallback extends ItemTouchHelper.Callback {

    private List<String> list;
    private Context context;

    public ItemTouchHelpCallback(Context context, List<String> list) {
        this.list = list;
        this.context = context;
    }

    @Override
    public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        final int dragFlags, swipeFlags;
        if (recyclerView.getLayoutManager() instanceof GridLayoutManager) {
            dragFlags = ItemTouchHelper.UP |
                    ItemTouchHelper.DOWN |
                    ItemTouchHelper.LEFT |
                    ItemTouchHelper.RIGHT;
            swipeFlags = 0;
        } else {
            dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
            swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END;
        }
        return makeMovementFlags(dragFlags, swipeFlags);
    }

    //上下移动item
    @Override
    public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
        //得到拖动viewHolder的position
        int fromPosition = viewHolder.getAdapterPosition();
        //得到目标viewHolder的position
        int toPosition = target.getAdapterPosition();
        if (fromPosition != 0 && fromPosition != recyclerView.getAdapter().getItemCount() - 1 &&
                toPosition != 0 && toPosition != recyclerView.getAdapter().getItemCount() - 1)
        {
            if (fromPosition < toPosition) {
                for (int i = fromPosition - 1; i < toPosition - 1; ++i) {
                    Collections.swap(list, i, i + 1); //改变实际的数据集
                }
            } else {
                for (int i = fromPosition - 1; i > toPosition - 1; i--) {
                    Collections.swap(list, i, i - 1); //改变实际的数据集
                }
            }
        }
        recyclerView.getAdapter().notifyItemMoved(fromPosition, toPosition);
        return true;
    }


    //当长按选中item的时候(拖拽开始的时候)调用
    @Override
    public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
        if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) {
            viewHolder.itemView.setBackgroundColor(context.getResources().
                    getColor(R.color.colorPrimary));
        }
        Vibrator vib = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
        vib.vibrate(70); //震动70毫秒
        super.onSelectedChanged(viewHolder, actionState);
    }

    //当手指松开的时候(拖拽完成的时候)调用
    @Override
    public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        super.clearView(recyclerView, viewHolder);
        viewHolder.itemView.setBackgroundColor(Color.WHITE);
    }

    @Override
    public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
        super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
        if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {
            //滑动时改变Item的透明度
            final float alpha = 1 - Math.abs(dX) / (float) viewHolder.itemView.getWidth();
            viewHolder.itemView.setAlpha(alpha);
            viewHolder.itemView.setTranslationX(dX);
        }
    }
}

要使用ItemTouchHelper,你需要创建一个ItemTouchHelper.Callback。这个接口可以让你监听“move”与 “swipe”事件。自定义类继承它时需要实现一些方法:getMovementFlags,onMove,onSwiped。另外还有一些方法如onSelectedChanged,clearView可以控制这个item被选中和被释放的状态,还有onChildDraw方法可以重写item滑动的默认动画。

ItemTouchHelper可以让你轻易得到一个事件的方向。重写getMovementFlags()方法来指定可以支持的拖放和滑动的方向。使用helperItemTouchHelper.makeMovementFlags(int, int)来构造返回的flag。

@Override
public boolean isLongPressDragEnabled() {
    return true;
}

ItemTouchHelper可以用于没有滑动的拖动操作(或者反过来),你必须指明你到底要支持哪一种。要支持长按RecyclerView item进入拖动操作,你必须在isLongPressDragEnabled()方法中返回true。或者,也可以调用ItemTouchHelper.startDrag(RecyclerView.ViewHolder) 方法来开始一个拖动。这会在后面讲到。

@Override
public boolean isItemViewSwipeEnabled() {
    return true;
}

而要在view任意位置触摸事件发生时启用滑动操作,则直接在sItemViewSwipeEnabled()中返回true就可以了。或者,你也主动调用ItemTouchHelper.startSwipe(RecyclerView.ViewHolder) 来开始滑动操作。

源码地址:https://github.com/Ti2Yuan/RecyclerViewDemo

参考:
http://www.jianshu.com/p/16712681731e
http://blog.csdn.net/u014497502/article/details/50917321
http://www.cnblogs.com/littlepanpc/p/4497290.html
http://www.voidcn.com/blog/liaoinstan/article/p-5785579.html
http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0630/3123.html

RecyclerView学习笔记

标签:

原文地址:http://blog.csdn.net/t465882689/article/details/51924200

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!