android中RecyclerView

来源:互联网 发布:蜻蜓fm电脑软件 编辑:程序博客网 时间:2024/04/29 18:04

这是官网对support.v4,support.v7等的一个说明:点击这里

那今天就先来讲RecyclerView。在RecyclerView中有几项比较重要

  • ItemDecoration:这个一看就知道是对每个item进行装饰,也就是在每个item的外围对其进行装饰
  • ItemAnimator:这个是说明每个item在删除、增加等时进行的动画。
  • ItemTouchHelper:给每个item增加了drag、drop、swipe的手势,可以上下拉,也可以水平被滑出
  • LayoutManager:该类是说明RecyclerView的布局方式,该类是一个抽象类,其子类有LinearLayoutManager和StaggeredGridLayoutManager,如果给RecyclerView设置LinearLayoutManager的话,效果就和普通的listView一样,不过还可以给其设置Horizontal和Vertical。如果给RecyclerView设置StaggeredGridLayoutManager的话,效果是错列的网格,和Grid的区别就是每个item的高度可以不一样,达到错列的效果。LinearLayoutManager的子类是GridLayoutManager,效果就是每个item等高的网格。
先讲下RecyclerView的基本用法
RecyclerView rv = (RecyclerView) findViewById(R.id.lv);GridLayoutManager layoutManager=new GridLayoutManager(this,3);rv.setLayoutManager(layoutManager);RecyclerViewAdapter adapter = new RecyclerViewAdapter();rv.setAdapter(adapter);
你会发现初始化RecyclerView比ListView要稍微繁琐点,要为其设置LayoutManager,这是因为ListView默认是垂直排列的,但是RecyclerView需要给他设置LayoutManager,它才知道如何绘制Item,否则界面上什么也不显示。
接下来逐个讲解刚刚讲到的几个类。

ItemDecoration:
该类为抽象函数,看下该类的几个函数

    public static abstract class ItemDecoration {        /**         * Draw any appropriate decorations into the Canvas supplied to the RecyclerView.         * Any content drawn by this method will be drawn before the item views are drawn,         * and will thus appear underneath the views.         *         * @param c Canvas to draw into         * @param parent RecyclerView this ItemDecoration is drawing into         * @param state The current state of RecyclerView         */        public void onDraw(Canvas c, RecyclerView parent, State state) {            onDraw(c, parent);        }        /**         * Draw any appropriate decorations into the Canvas supplied to the RecyclerView.         * Any content drawn by this method will be drawn after the item views are drawn         * and will thus appear over the views.         *         * @param c Canvas to draw into         * @param parent RecyclerView this ItemDecoration is drawing into         * @param state The current state of RecyclerView.         */        public void onDrawOver(Canvas c, RecyclerView parent, State state) {            onDrawOver(c, parent);        }        /**         * Retrieve any offsets for the given item. Each field of <code>outRect</code> specifies         * the number of pixels that the item view should be inset by, similar to padding or margin.         * The default implementation sets the bounds of outRect to 0 and returns.         *         * <p>         * If this ItemDecoration does not affect the positioning of item views, it should set         * all four fields of <code>outRect</code> (left, top, right, bottom) to zero         * before returning.         *         * <p>         * If you need to access Adapter for additional data, you can call         * {@link RecyclerView#getChildAdapterPosition(View)} to get the adapter position of the         * View.         *         * @param outRect Rect to receive the output.         * @param view    The child view to decorate         * @param parent  RecyclerView this ItemDecoration is decorating         * @param state   The current state of RecyclerView.         */        public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state) {            getItemOffsets(outRect, ((LayoutParams) view.getLayoutParams()).getViewLayoutPosition(),                    parent);        }    }

以上是api中的说明,还有三个方法没列出来,但声明为deprecated,所以也就不列了。

public void onDraw(Canvas c,RecyclerView parent,State state)该函数为item绘制底部,该函数中的内容会在item绘制前先被绘制。

public void onDrawOver(Canvas c,RecyclerView parent,State state)该函数中的内容会在item绘制后再被绘制,可能会导致遮住item的一部分。

public void getItemOffSet(Rect outRect, View view, RecyclerView parent, State state)该函数指定了为item的周围留出多少的空白来绘制decoration。如果想要为Item四周各空出10px的距离,只要在该函数中添加该句:outRect.set(10,10,10,10); item周围留出的空白需要根据读者需要自行设置。

使用RecyclerView会发现,不能在xml中为其设置divider,导致显示起来不那么好看,那我们现在就可以用ItemDecoration来为其添加divider。

public class GridDividerItemDecoration extends RecyclerView.ItemDecoration {    //获取系统的属性    private final int[] ATTRS = new int[]{android.R.attr.listDivider};    private Drawable mDivider;    public GridDividerItemDecoration(Context context) {        final TypedArray a = context.obtainStyledAttributes(ATTRS);        mDivider = a.getDrawable(0);        a.recycle();    }    @Override    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {        drawVertical(c, parent);        drawHorizontal(c, parent);    }    public void drawVertical(Canvas c, RecyclerView parent) {        int left, right, top, bottom;        for (int i = 0; i < parent.getChildCount(); i++) {            final View child = parent.getChildAt(i);            final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();            left = child.getLeft() - params.leftMargin;            top = child.getBottom() + params.bottomMargin;            right = child.getRight() + params.rightMargin;            bottom = top + mDivider.getIntrinsicHeight();            mDivider.setBounds(left, top, right, bottom);            mDivider.draw(c);        }    }    public void drawHorizontal(Canvas c, RecyclerView parent) {        int top, bottom, left, right;        for (int i = 0; i < parent.getChildCount(); i++) {            final View child = parent.getChildAt(i);            final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child                    .getLayoutParams();            left = child.getRight() + params.rightMargin;            top = child.getTop() - params.topMargin;            right = left + mDivider.getIntrinsicHeight();            bottom = child.getBottom() + params.bottomMargin;            mDivider.setBounds(left, top, right, bottom);            mDivider.draw(c);        }    }    @Override    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {        //getIntrinsicHeight()和getIntrinsicWidth()获取drawable的高度和宽度        outRect.set(10, 10, mDivider.getIntrinsicWidth(), mDivider.getIntrinsicHeight());    }}
这是实现的一个ItemDecoration,并添加在RecyclerView上。

ItemDecoration decoration = new GridDividerITemDecoration(this);rc.addItemDecoration(decoration);
在该实现中,是为网格的RecyclerView绘制divider,画出四周的格子,所以只用在右边和底部画divider即可。其中使用了系统资源的divider,在getItemOffsets函数中设置了右边和底部的空白处,宽度为divider的宽(高)。在onDraw函数中先纵向绘制(绘制底部的divider),再横向绘制(绘制右边的divider)。

在onDraw函数中,传入的参数RecyclerView parent,parent代表的是一行的所有item的总和,所以在绘制时,需要去获取parent中的各个child,去获取left,top,bottom,right,该left,right,bottom,top代表的都是item本身,并不包括周围留白处,例如在绘制底部时,取出child的bottom作为divider绘制的顶部,再加上divider的高度,计算出divier绘制的底部即可。


ItemAnimator:
如果你想要为item添加增删的动画的话,就要重写该类,而google已经为我们提供了几个默认的itemAnimator,分别是DefaultItemAnimator。使用如下

rv.setItemAnimator(new DefaultItemAnimator())
有人会推荐github中的一些关于ItemAnimator的项目,我就用了一个项目,RecyclerViewItemAnimators ,我试用了其中一个animator,是ScaleInOutAnimator,效果的确不错,但是存在一定的bug,在添加和删除item的时候,因为该动画是放大和缩小,所以在绘制时,发现有些item绘制错误,被缩小了,所以使用github上的一些ItemAnimator的时候要慎重。

给各位看下其他博客上的效果



ItemTouchHelper

该类为item增加了一些手势,包括拖拉和横向滑动,现在我们做一种效果,可以长按上下拖动item,改变item在RecyclerView中的位置。也可以横向滑动item,将其删除。

Google已经为我们写好了ItemTouchHelper,其构造函数中需要传入Callback作为参数,Callback是抽象类,需要我们重写其中的一些方法,来告诉ItemTouchHelper是否需要拖拉或者滑动的手势,如果需要,在不同的手势需要有怎么样的效果,这些都是Callback来实现的。

下面是一个例子

public interface ItemTouchHelperViewHolder {    /**     * Called when the {@link ItemTouchHelper} first registers an item as being moved or swiped.     * Implementations should update the item view to indicate it's active state.     */    void onItemSelected();    /**     * Called when the {@link ItemTouchHelper} has completed the move or swipe, and the active item     * state should be cleared.     */    void onItemClear();}

public interface ItemTouchHelperAdapter {    /**     * Called when an item has been dragged far enough to trigger a move. This is called every time     * an item is shifted, and <strong>not</strong> at the end of a "drop" event.<br/>     * <br/>     * Implementations should call {@link RecyclerView.Adapter#notifyItemMoved(int, int)} after     * adjusting the underlying data to reflect this move.     *     * @param fromPosition The start position of the moved item.     * @param toPosition   Then resolved position of the moved item.     * @return True if the item was moved to the new adapter position.     *     * @see RecyclerView#getAdapterPositionFor(RecyclerView.ViewHolder)     * @see RecyclerView.ViewHolder#getAdapterPosition()     */    boolean onItemMove(int fromPosition, int toPosition);    /**     * Called when an item has been dismissed by a swipe.<br/>     * <br/>     * Implementations should call {@link RecyclerView.Adapter#notifyItemRemoved(int)} after     * adjusting the underlying data to reflect this removal.     *     * @param position The position of the item dismissed.     *     * @see RecyclerView#getAdapterPositionFor(RecyclerView.ViewHolder)     * @see RecyclerView.ViewHolder#getAdapterPosition()     */    void onItemDismiss(int position);}
public class RecyclerListAdapter extends RecyclerView.Adapter<RecyclerListAdapter.ItemViewHolder>        implements ItemTouchHelperAdapter {    private ArrayList<String> datas;    public RecyclerListAdapter(ArrayList<String> data) {        datas = data;    }    @Override    public ItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.view_friend_item, parent, false);        ItemViewHolder itemViewHolder = new ItemViewHolder(view);        return itemViewHolder;    }    @Override    public void onBindViewHolder(final ItemViewHolder holder, int position) {        holder.tvName.setText(datas.get(position));    }    @Override    public void onItemDismiss(int position) {        datas.remove(position);        notifyItemRemoved(position);    }    @Override    public boolean onItemMove(int fromPosition, int toPosition) {        Collections.swap(datas, fromPosition, toPosition);        notifyItemMoved(fromPosition, toPosition);        return true;    }    @Override    public int getItemCount() {        return datas.size();    }    /**     * Simple example of a view holder that implements {@link ItemTouchHelperViewHolder} and has a     * "handle" view that initiates a drag event when touched.     */    public static class ItemViewHolder extends RecyclerView.ViewHolder implements ItemTouchHelperViewHolder {        public CardView mCardView;        public TextView tvName;        public ItemViewHolder(View itemView) {            super(itemView);            mCardView = (CardView) itemView.findViewById(R.id.card_view);            tvName = (TextView) itemView.findViewById(R.id.tv_name);        }        @Override        public void onItemSelected() {            itemView.setBackgroundColor(Color.LTGRAY);        }        @Override        public void onItemClear() {            itemView.setBackgroundColor(0);        }    }}
public class SimpleItemTouchHelperCallback extends ItemTouchHelper.Callback {    public static final float ALPHA_FULL = 1.0f;    private final ItemTouchHelperAdapter mAdapter;    public SimpleItemTouchHelperCallback(ItemTouchHelperAdapter adapter) {        mAdapter = adapter;    }    @Override    public boolean isLongPressDragEnabled() {        return true;    }    @Override    public boolean isItemViewSwipeEnabled() {        return true;    }    @Override    public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {        // Set movement flags based on the layout manager        if (recyclerView.getLayoutManager() instanceof GridLayoutManager) {            final int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;            final int swipeFlags = 0;            return makeMovementFlags(dragFlags, swipeFlags);        } else {            final int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;            final int swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END;            return makeMovementFlags(dragFlags, swipeFlags);        }    }    @Override    public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder source, RecyclerView.ViewHolder target) {        if (source.getItemViewType() != target.getItemViewType()) {            return false;        }        // Notify the adapter of the move        mAdapter.onItemMove(source.getAdapterPosition(), target.getAdapterPosition());        return true;    }    @Override    public void onSwiped(RecyclerView.ViewHolder viewHolder, int i) {        // Notify the adapter of the dismissal        mAdapter.onItemDismiss(viewHolder.getAdapterPosition());    }    @Override    public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {        if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {            // Fade out the view as it is swiped out of the parent's bounds            final float alpha = ALPHA_FULL - Math.abs(dX) / (float) viewHolder.itemView.getWidth();            viewHolder.itemView.setAlpha(alpha);            viewHolder.itemView.setTranslationX(dX);        } else {            super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);        }    }    @Override    public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {        // We only want the active item to change        if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) {            if (viewHolder instanceof ItemTouchHelperViewHolder) {                // Let the view holder know that this item is being moved or dragged                ItemTouchHelperViewHolder itemViewHolder = (ItemTouchHelperViewHolder) viewHolder;                itemViewHolder.onItemSelected();            }        }        super.onSelectedChanged(viewHolder, actionState);    }    @Override    public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {        super.clearView(recyclerView, viewHolder);        viewHolder.itemView.setAlpha(ALPHA_FULL);        if (viewHolder instanceof ItemTouchHelperViewHolder) {            // Tell the view holder it's time to restore the idle state            ItemTouchHelperViewHolder itemViewHolder = (ItemTouchHelperViewHolder) viewHolder;            itemViewHolder.onItemClear();        }    }}
首先先声明了两个接口,分别是ItemTouchHelperAdapter,该接口需要Adapter实现,并且Adapter集成自RecyclerView.Adapter,另一个接口是ItemTouchHelperViewHolder,由ViewHolder实现,并且ViewHolder需要集成自RecyclerView.ViewHolder。

ItemTouchHelperAdapter中两个函数

  • onItemMove(int fromPosition, int toPosition):调用该函数告诉adapter在fromPosition的item将会移植到toPosition的位置,所以在adapter中在该方法中,首先需要更新数据集,交换该两个数据的位置,在调用notifyItemMoved(fromPosition,toPosition)函数告诉adapter更新视图。这里需要注意的是,并不是调用notifyDataSetChanged,还有其他类似的函数。
notifyItemInserted(int position);notifyItemChanged(int position);notifyItemRemoved(int position);notifyItemRangeChanged(int positionStart,int itemCount);notifyItemRangeInserted(int positionStart,int itemCount);notifyItemRangeRemoved(int positionStart,int itemCount);
  • onItemDismiss(int position):调用该函数告诉Adapter在position位置的item被删除了,需要在数据集中删除该item,并调用notifyItemRemoved(position)。
ItemTouchHelperViewHolder中两个函数
  • onItemSelected():当一个item在拖拉或者滑动被选中时,会调用该函数,告诉ViewHolder,如果想要在item被选中时需要什么效果,可以写在该函数中。在该例子中,当选中时,为ViewHolder中的itemView添加一个灰色的背景,表示被选中。
  • onItemClear():当一个item被滑动结束或者拖拉结束的时候,会调用该函数,告诉ViewHolder,如果刚刚在onItemSelected函数中对item有任何绘制上的改变,则需要在该函数中恢复至原样。
接下来重点是SimpleItemTouchHelperCallback,在该类的构造函数中传入刚刚声明的接口ItemTouchHelperAdapter。
  • public boolean isLongPressDragEnabled():当该函数返回true的时候,则item可以在长按下被拖拉,如果返回false,则不可以。该函数默认的实现是返回true。
  • public boolean isItemViewSwipeEnabled():当该函数返回true的时候,则item可以被滑动,如果返回false,则不可以。该函数默认的实现是返回true。
  • public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder):该函数中你需要为手势指明可以移动的方向,主要是通过调用makeMovementsFlags(dragFlags,swipeFlags),在该例子中,如果LayoutManager是GridLayoutManager的话,允许上下左右拖拉,只能水平滑动。如果是其他LayoutManager的话,则只能上下拖拉,水平滑动,相信大家看代码就能看懂了。
  • public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder source, RecyclerView.ViewHolder target):当ItemTouchHelper想要拖拉一个item从source到target的时候会调用该函数,在该函数中需要通知adapter更新数据集,并且调用adapter的notifyItemViewMove(fromPosition,toPosition)即可,该函数还需要返回boolean类型的值,如果返回true,则标志该item已经被移置正确的位置,如果返回false,则表明未移置正确的位置。
  • public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction):当一个item被swipe的时候会调用该函数,第二个参数代表swipe最开始是从哪个方向滑动的,读者可以根据不同的方向来处理不同的操作,现在默认都是删除,需要告知adapter去更新数据集,并调用notifyItemViewRemoved(position)。
  • onChildDraw:该函数在ItemTouchHelper的onDraw中会被调用到,如果你想在拖拉或者滑动时有任何的效果的话,可以在该函数中写,在该例子中,当在滑动时,会根据dx来不断改变item的透明度。
  • onSelectedChanged:当一个item被选中时(即当前正在被拖拉和滑动时)会调用该函数,在该例子中,当item被选中时,会为其itemView增加一个灰色的背景。
  • clearView:该一个item被选中结束后,手势已结束,动画也结束了,调用该函数,在该函数中需要恢复item原来的样子

为RecyclerView增加ItemTouchHelper
ItemTouchHelper.Callback mCallback = new SimpleItemTouchHelperCallback(adapter);ItemTouchHelper mItemTouchHelper = new ItemTouchHelper(mCallback);mItemTouchHelper.attachToRecyclerView(rv);

效果如下:录得不太好,最后几帧有点仓促

0 0
原创粉丝点击