关闭

关于RecyclerView的一切

44人阅读 评论(0) 收藏 举报
分类:

RecyclerView

RecyclerView只管回收和复用View,其他的自己去设置。

整体上看RecyclerView架构,提供了一种插拔式的体验,高度的解耦,异常的灵活

常用操作

RecyclerView.Adapter

处理数据集+绑定视图

ViewHolder

装载view

RecyclerView.LayoutManager

布局管理器,用于控制item的摆放

必须为RecyclerView指定LayoutManager,否则会出现异常

RecyclerView.ItemDecoration

设置分割线以及item之间的间隔

RecyclerView.ItemAnimator

设置item增删动画效果

RcyclerView.OnItemTouchListener

ItemTouchHelper是一个处理RecyclerView的滑动删除和拖拽的辅助类

1.RecyclerView.Adapter

public class SimplerItemAdapter extends RecyclerView.Adapter<SimplerItemAdapter.SimpleItemViewHolder> {

    private List<String> items;

    public SimplerItemAdapter(@NonNull List<String> dateItems) {
        this.items = (dateItems != null ? dateItems : new ArrayList<String>());
    }

    //创建ViewHolder并关联item视图      
    @Override
    public SimpleItemViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
        return new SimpleItemViewHolder(LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item, viewGroup, false));
    }
    //设置数据
    @Override
    public void onBindViewHolder(SimpleItemViewHolder viewHolder, int position) {
        viewHolder.textView.setText(items.get(position));
    }

    @Override
    public int getItemCount() {
        return (this.items != null) ? this.items.size() : 0;
    }

    protected final static class SimpleItemViewHolder extends RecyclerView.ViewHolder {
        protected TextView textView;

        public SimpleItemViewHolder(View itemView) {
            super(itemView);
            this.textView = (TextView) itemView.findViewById(R.id.text);
        }
    }
}

2.RecyclerView.LayoutManager

有如下三种布局:

  • LinearLayoutManager 水平或者垂直的Item视图
  • GridLayoutManager 网格Item视图
  • StaggeredGridLayoutManager 瀑布流

LayoutManager 提供了以下方法:

findFirstVisibleItemPosition() 返回当前第一个可见Item的position
findFirstCompletelyVisibleItemPosition() 返回当前第一个完全可见Item的position
findLastVisibleItemPosition() 返回当前最后一个可见Item的position
findLastCompletelyVisibleItemPosition() 返回当前最后一个完全可见Item的position

3.RecyclerView.ItemDecoration

这个写起来有点麻烦,若是没有强制需求的时候,还是设置item的margin方便

通过重写以下三个方法,来实现Item之间的偏移量或者装饰效果:

* public void onDraw(Canvas c, RecyclerView parent) 
    * 在Item条目绘制之前调用,所以这有可能被Item的内容所遮挡

* public void onDrawOver(Canvas c, RecyclerView parent) 
    * 在Item条目绘制之后调用,因此装饰将浮于Item之上

* public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) 
    * 可以通过outRect.set()为每个Item设置一定的偏移量,主要用于绘制Decorator。

ItemDecoration是可以叠加的,因此你可以为每个子view装饰多次

添加分割线

recyclerView.addItemDecoration(new DividerDecoration(this,DividerItemDecoration.HORIZONTAL_LIST))
  • DividerDecoration是ItemDecoration的一个实现类,通过读取系统主题中Android.R.attr.listDivider作为Item间的分割线,并且支持横向和纵向

  • 获取到listDivider以后,该属性的值是个Drawable,在getItemOffsets中,outRect设置了绘制的范围,在onDraw中实现了真正的绘制

设置分割线的样式

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    .......
    <item name="android:listDivider">@drawable/divider_bg</item>
</style>    

<shape xmlns:android="http://schemas.android.com/apk/res/android"
       android:shape="rectangle">
    <gradient
        android:centerColor="#ff00ff00"
        android:endColor="#ff0000ff"
        android:startColor="#ffff0000"
        android:type="linear"/>
    <size android:height="4dp"/>
</shape>

注意:当每个Item一行,这样是没问题的,但是当布局为GridLayoutManager、流式布局时,就会出现问题,然后就需要自己去写一个分割线,好难过

4.RecyclerView.ItemAnimator

设置item增删动画效果

这个就好多了,Android给了一个DefaultItemAnimator,我们可以通过以下代码设置item增删动画效果:

recyclerView.setItemAnimator(new DefaultItemAnimator());

在ListView中,当集合数据发生改变的时候,通过调用notifyDataSetChanged刷新界面,这样做只是对界面进行重绘,并没有任何动画效果,因此在RecyclerVIew中提供了以下方法:

public final void notifyItemInserted(int position) 向指定位置插入Item

public final void notifyItemRemoved(int position) 移除指定位置Item

public final void notifyItemChanged(int position) 更新指定位置Item

常用方法

//如果可以确定每个item的高度是固定的,设置这个选项可以提高性能
mRecyclerView.setHasFixedSize(true);

拓展

A.设置item点击,长按事件

@Override
public void onBindViewHolder(final MyViewHolder holder, final int position) {
    holder.tv.setText(mDatas.get(position));

    if (mOnItemClickLitener != null) {
        holder.itemView.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                int pos = holder.getLayoutPosition();
                mOnItemClickLitener.onItemClick(holder.itemView, pos);
            }
        });

        holder.itemView.setOnLongClickListener(new OnLongClickListener() {
            @Override
            public boolean onLongClick(View v) {
                int pos = holder.getLayoutPosition();
                mOnItemClickLitener.onItemLongClick(holder.itemView, pos);
                return false;
            }
        });
    }
}

注意:这里使用了ViewHolder的getLayoutPosition方法,此方法返回的pos值与onBindViewHolder方法传入的position值有可能不同。

根据SDK中的解释,在Recyclerview 进行添加、移除item等操作时,position位置可能会变化,而所有的adapter的刷新并不总是及时的,只有这个方法返回的才是当前item经过一些变换后所处的真正位置

B.多布局

getItemViewType(int position)+onCreateViewHolder(ViewGroup parent, int viewType)

//标题
private int TYPE_TITLE = 0;
//内容
private int TYPE_SUB = 1;

@Override
public int getItemViewType(int position) {
    return typeList.get(position) instanceof String ? TYPE_TITLE : TYPE_SUB;
}

@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    if (viewType == TYPE_TITLE) {
        //标题类型
        return new TitleViewHolder(LayoutInflater.from(mActivity).inflate(R.layout.adapter_category_title, parent, false));
    } else {
        return new SubViewHolder(LayoutInflater.from(mActivity).inflate(R.layout.adapter_category_sub, parent, false));
    }
}

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
    int viewType = getItemViewType(position);
    if(viewType==TYPE_TITLE){
        String title = (String) typeList.get(position);
        ((TitleViewHolder) holder).mTitle.setText(title);
    }else {
       CategoryInfo.ListBean.InfosBean infosBean = (CategoryInfo.ListBean.InfosBean) typeList.get(position);
        SubViewHolder subViewHolder = (SubViewHolder) holder;
        subViewHolder.tv_name1.setText(infosBean.name1);
        Glide.with(mActivity).load(NetUrl.IMG_PREFIX+infosBean.url1).into(subViewHolder.iv_image1);
    }
}

@Override
public int getItemCount() {
    return typeList.size();
}

//titleViewHolder
public class TitleViewHolder extends RecyclerView.ViewHolder {
    public TextView mTitle;

    public TitleViewHolder(View itemView) {
        super(itemView);
        mTitle = (TextView) itemView.findViewById(R.id.tv_title);
    }
}

//subVieHOlder 
public class SubViewHolder extends RecyclerView.ViewHolder {
    public ImageView iv_image1;
    public TextView tv_name1;

    public SubViewHolder(View itemView) {
        super(itemView);
        this.iv_image1 = (ImageView) itemView.findViewById(R.id.iv_image1);
        this.tv_name1 = (TextView) itemView.findViewById(R.id.tv_name1);
        //在这里可以设置item点击事件
    }
}

C. RecyclerView的滑动删除和拖拽

这个实现的核心主要由ItemTouchHelper来完成

ItemTouchHelper简介:

  • 实现RecyclerView滑动删除和拖拽的工具类

  • 是RecyclerView.ItemDecoration的子类

常用方法

private class MyCallBack1 extends ItemTouchHelper.Callback {
    /**
     * 设置滑动或者拖拽的方向
     *      drag - 表示拖拽的方向,有六个类型的值:LEFT、RIGHT、START、END、UP、DOWN
     *      swipe - 表示滑动的方向,有六个类型的值:LEFT、RIGHT、START、END、UP、DOWN
     *          如果为0,则不执行拖动或者滑动
     * @param recyclerView
     * @param viewHolder
     * @return
     */
    @Override
    public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        int dragFlags = 0, swipeFlags = 0;
        if (recyclerView.getLayoutManager() instanceof StaggeredGridLayoutManager || recyclerView.getLayoutManager() instanceof GridLayoutManager) {
            //网格式布局有4个方向
            dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
        } else if (recyclerView.getLayoutManager() instanceof LinearLayoutManager) {
            //线性式布局有2个方向
            dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
            swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END; //设置侧滑方向为从两个方向都可以
        }
        //makeMovementFlags./.
        return makeMovementFlags(dragFlags, swipeFlags);
    }

    /**
     * item拖拽时调用的方法
     *
     * @param recyclerView
     * @param viewHolder   拖动的ViewHolder
     * @param target       目标位置的ViewHolder
     * @return
     */
    @Override
    public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
        //拿到起始/目标位置Position
        int from = viewHolder.getAdapterPosition();
        int to = target.getAdapterPosition();
        Collections.swap(list, from, to);
        myAdapter.notifyItemMoved(from, to);
        //返回true表示执行拖动
        return true;
    }

    /**
     * item滑动时调用的方法
     *
     * @param viewHolder 滑动的ViewHolder
     * @param direction  滑动的方向
     */
    @Override
    public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
        int position = viewHolder.getAdapterPosition();
        list.remove(position);
        myAdapter.notifyItemRemoved(position);
    }

    /**
     * 当item绘制的时候调用的方法
     *
     * @param c
     * @param recyclerView
     * @param viewHolder
     * @param dX
     * @param dY
     * @param actionState       三种状态SWIPE(滑动)、IDLE(静止)、DRAG(拖动)
     * @param isCurrentlyActive
     */
    @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);
        }
    }

    //当选中Item时候会调用该方法,重写此方法可以实现选中时候的一些动画逻辑
    @Override
    public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
        super.onSelectedChanged(viewHolder, actionState);
    }

    //当动画已经结束的时候调用该方法,重写此方法可以实现恢复Item的初始状态
    @Override
    public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        super.clearView(recyclerView, viewHolder);

    }

    /**
     * 是否可以长按拖拽排序
     * @return
     */
    @Override
    public boolean isLongPressDragEnabled() {
        return super.isLongPressDragEnabled();
    }
    /**
     * Item是否可以被滑动(H:左右滑动,V:上下滑动)
     */
    @Override
    public boolean isItemViewSwipeEnabled() {
        return true;
    }
}

使用

//1.创建ItemTouchHelper实例
MyCallback callback = new MyCallback(
        ItemTouchHelper.UP|ItemTouchHelper.DOWN,
        ItemTouchHelper.START|ItemTouchHelper.END
);
ItemTouchHelper helper = new ItemTouchHelper(callback);
//2.与recyclerView进行绑定
helper.attachToRecyclerView(recyclerView);
//3.在callback中实现具体逻辑

Android提供了一个默认的实现类SimpleCallback,简化了操作。

CardView

CardLayout是一个拥有高度和阴影,以及轮廓裁剪,圆角的布局
添加依赖:
    compile 'com.android.support:cardview-v7:23.2.1'
各属性说明:
    1.设置圆角:card_view:cardCornerRadius="10dp"
    2.设置高度:card_view:cardElevation="10dp"
    3.设置内边距:card_view:contentPadding="10dp"
    4.设置背景色:card_view:cardBackgroundColor="?android:attr/colorPrimary"

布局文件使用
     <!--cardCornerRadius : 圆角
        contentPadding : 距离内边框的距离
        cardBackgroundColor : 背景色
        cardElevation: 海拔  决定 阴影效果
        cardUseCompatPadding打开设备兼容性
    -->
    <android.support.v7.widget.CardView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="10dp"
        app:cardCornerRadius="10dp"
        app:contentPadding="10dp"
        app:cardBackgroundColor="#0000FF"
        app:cardElevation="50dp"
        >

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            >
            <TextView
            android:layout_width="30dp"
            android:layout_height="30dp"
            android:background="#FF0000"
            />
            <TextView
                android:layout_width="30dp"
                android:layout_height="30dp"
                android:background="#00FF00"
                />
        </LinearLayout>
    </android.support.v7.widget.CardView>
可在布局文件设置多个显示多种效果
0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:2355次
    • 积分:135
    • 等级:
    • 排名:千里之外
    • 原创:27篇
    • 转载:2篇
    • 译文:0篇
    • 评论:0条
    文章存档