RecycleView万能适配器

一导入

  • implementation 'com.github.CymChad:BaseRecyclerViewAdapterHelper:2.9.24'
  • implementation 'com.android.support:recyclerview-v7:25.3.1'
allprojects {
        repositories {
            ...
            maven { url "https://jitpack.io" }
        }
    }

二:基本应用

  1. activity_main xml文件
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
</RelativeLayout>

2.条目布局文件

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="5dp">



    <TextView
        android:id="@+id/tv_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        android:layout_marginLeft="10dp"
        android:layout_marginTop="10dp"
        android:text="我是标题"
        android:textColor="#f00"
        android:textSize="20dp" />

    <TextView
        android:id="@+id/tv_age"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_toRightOf="@+id/tv_title"
        android:layout_marginLeft="10dp"
        android:textSize="20dp"
        android:layout_marginTop="10dp"
        android:text="我年龄" />

    <TextView
        android:id="@+id/tv_content"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/tv_title"
        android:textSize="18dp"
        android:layout_marginLeft="10dp"
        android:layout_marginTop="10dp"
        android:text="我是描述" />
</RelativeLayout>

3.编写数据实体类型

public class Person {
    private String name;
    private int age;
    private String des;
    
   生成 get set方法
}
  1. 编写适配器
public class QuickAdapter extends BaseQuickAdapter<Person, BaseViewHolder> {

    private Context context;

    public QuickAdapter(int layoutResId, @Nullable List<Person> data, Context context) {
        super(layoutResId, data);
        this.context = context;
    }

    @SuppressLint("ResourceAsColor")
    @Override
    protected void convert(@NonNull BaseViewHolder helper, Person item) {
        helper.setText(R.id.tv_title, item.getName()+"")
                .setTextColor(R.id.tv_title, context.getResources().getColor(R.color.colorPrimary))
                .setText(R.id.tv_age, item.getAge()+"")
                .setText(R.id.tv_content, item.getDes()+"")
                .setTextColor(R.id.tv_content, context.getResources().getColor(R.color.colorAccent));

    }
}

5: 最后一步:在Activity中使用该适配器

public class MainActivity extends AppCompatActivity {
    private RecyclerView recyclerView;
    private QuickAdapter adapter;
    private ArrayList<Person> list;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        recyclerView =findViewById(R.id.recycler_view);
        list =new ArrayList<>();
        for (int i = 0; i < 20; i++) {
            Person person =new Person();
            person.setAge(i);
            person.setName("我是第"+i+"条数据");
            person.setDes("我是描述"+i);
            list.add(person);
        }
        LinearLayoutManager manager =new LinearLayoutManager(this);
        manager.setOrientation(LinearLayoutManager.VERTICAL);
        recyclerView.setLayoutManager(manager);

        adapter =new QuickAdapter(R.layout.item_rv,list,this);
        recyclerView.setAdapter(adapter);
    }
}

三:点击事件
1:Item的点击事件

adapter.setOnItemClickListener(new BaseQuickAdapter.OnItemClickListener() {
            @Override
            public void onItemClick(BaseQuickAdapter adapter, View view, int position) {
                Toast.makeText(MainActivity.this, "点击了第"+position+"条目", Toast.LENGTH_SHORT).show();
            }
        });

2:长按点击事件

adapter.setOnItemLongClickListener(new BaseQuickAdapter.OnItemLongClickListener() {
            @Override
            public boolean onItemLongClick(BaseQuickAdapter adapter, View view, int position) {
                Toast.makeText(MainActivity.this, "长按"+position+"条目", Toast.LENGTH_SHORT).show();
                return false;
            }
        });

3:条目子控件事件
首先,在adapter的convert方法里面通过 helper.addOnClickListener 绑定一下子控件的控件id

@SuppressLint("ResourceAsColor")
    @Override
    protected void convert(@NonNull BaseViewHolder helper, Person item) {
        helper.setText(R.id.tv_title, item.getName()+"")
                .setTextColor(R.id.tv_title, context.getResources().getColor(R.color.colorPrimary))
                .setText(R.id.tv_age, item.getAge()+"")
                .setText(R.id.tv_content, item.getDes()+"")
                .setTextColor(R.id.tv_content, context.getResources().getColor(R.color.colorAccent))
                .addOnClickListener(R.id.tv_title); //给标题增加点击事件
    }

然后设置

//条目子控件事件
        adapter.setOnItemChildClickListener(new BaseQuickAdapter.OnItemChildClickListener() {
            @Override
            public void onItemChildClick(BaseQuickAdapter adapter, View view, int position) {
                //多个事件
                Toast.makeText(MainActivity.this, "标题子控件点击"+position+"条目", Toast.LENGTH_SHORT).show();
                }
            }
        });

4:Item子控件的长按事件 跟点击条目控件事件做法相同
5:多个Item子控件事件
首先绑定

@SuppressLint("ResourceAsColor")
    @Override
    protected void convert(@NonNull BaseViewHolder helper, Person item) {
        helper.setText(R.id.tv_title, item.getName()+"")
                .setTextColor(R.id.tv_title, context.getResources().getColor(R.color.colorPrimary))
                .setText(R.id.tv_age, item.getAge()+"")
                .setText(R.id.tv_content, item.getDes()+"")
                .setTextColor(R.id.tv_content, context.getResources().getColor(R.color.colorAccent))
                .addOnClickListener(R.id.tv_title) //给标题增加点击事件
                .addOnClickListener(R.id.tv_content)给描述增加点击事件;
              
    }

然后区分id

adapter.setOnItemChildClickListener(new BaseQuickAdapter.OnItemChildClickListener() {
            @Override
            public void onItemChildClick(BaseQuickAdapter adapter, View view, int position) {
                //多个事件
                switch (view.getId()){
                    case R.id.tv_title:
                        Toast.makeText(MainActivity.this, "标题子控件点击"+position+"条目", Toast.LENGTH_SHORT).show();
                      
                        break;
                    case R.id.tv_content:
                        Toast.makeText(MainActivity.this, "描述子控件点击"+position+"条目", Toast.LENGTH_SHORT).show();
                        break;
                }
            }
        });

那么如果是长按事件呢?当然也是相同的处理方法。

6如果需要在子控件事件中获取其他子控件可以使用:

getViewByPosition(RecyclerView recyclerView, int position, @IdRes int viewId)
adapter.setOnItemChildClickListener(new BaseQuickAdapter.OnItemChildClickListener() {
            @Override
            public void onItemChildClick(BaseQuickAdapter adapter, View view, int position) {
                //多个事件
                switch (view.getId()){
                    case R.id.tv_title:
                        Toast.makeText(MainActivity.this, "标题子控件点击"+position+"条目", Toast.LENGTH_SHORT).show();
                        // TODO 子控件事件中获取其他子控件可以使用
                        //TODO getViewByPosition(RecyclerView recyclerView, int position, @IdRes int viewId)
                        TextView tv_title = (TextView) adapter.getViewByPosition(recyclerView, position, R.id.tv_title);
                        Log.e("wudi", "onItemChildClick: tv_title.getText()="+tv_title.getText() );
                        TextView tv_content = (TextView) adapter.getViewByPosition(recyclerView, position, R.id.tv_content);
                        Log.e("wudi", "onItemChildClick: tv_content.getText()="+tv_content.getText() );
                        break;
                    case R.id.tv_content:
                        Toast.makeText(MainActivity.this, "描述子控件点击"+position+"条目", Toast.LENGTH_SHORT).show();
                        break;
                }
            }
        });

四、添加列表加载动画
1://开启动画(默认为渐显效果)
adapter.openLoadAnimation();
2:五种状态 (渐显、缩放、从下到上,从左到右、从右到左)

public static final int ALPHAIN = 0x00000001;
    /**
     * Use with {@link #openLoadAnimation}
     */
    public static final int SCALEIN = 0x00000002;
    /**
     * Use with {@link #openLoadAnimation}
     */
    public static final int SLIDEIN_BOTTOM = 0x00000003;
    /**
     * Use with {@link #openLoadAnimation}
     */
    public static final int SLIDEIN_LEFT = 0x00000004;
    /**
     * Use with {@link #openLoadAnimation}
     */
    public static final int SLIDEIN_RIGHT = 0x00000005;

//使用缩放
adapter.openLoadAnimation(BaseQuickAdapter.SCALEIN);

如果想自定义动画,该适配器也提供了接口

//自定义动画效果
        adapter.openLoadAnimation(new BaseAnimation() {
            @Override
            public Animator[] getAnimators(View view) {
                return new Animator[]{
                        ObjectAnimator.ofFloat(view, "scaleY", 1, 0.5f, 1),
                        ObjectAnimator.ofFloat(view, "scaleX", 1, 0.5f, 1)
                };
            }
        });

动画默认只执行一次,如果想重复执行可设置

//设置重复执行动画
adapter.isFirstOnly(false);

设置不显示动画数量(老实讲,我没明白这个方法的效果是啥,因为我并没有看到效果 0.0

adapter.setNotDoAnimationCount(count);

首次到界面的item每次都依次执行加载动画
由于进入界面的item都是很多的速度进来的所以不会出现滑动显示的依次执行动画效果,这个时候会一起执行动画,如果觉得这样的效果不好可以使用setNotDoAnimationCount设置第一屏item不执行动画,但是如果需要依次执行动画可以重写startAnim让第一个屏幕的item动画延迟执行即可。

@Override
    protected void startAnim(Animator anim, int index) {
        super.startAnim(anim, index);
        if (index < count)
        anim.setStartDelay(index * 150);
    }

五:添加头部、尾部

View headView = View.inflate(MainActivity.this, R.layout.head_view, null);
        View footView = View.inflate(MainActivity.this, R.layout.foot_view, null);
        adapter.addHeaderView(headView);
        adapter.addFooterView(footView);

删除指定view

adapter.removeHeaderView(headView);
        adapter.removeFooterView(footView);

删除所有布局

adapter.removeAllHeaderView();
        adapter.removeAllFooterView();

出现了头布局就不会显示Empty

adapter.setHeaderAndEmpty(true);
adapter.setHeaderFooterEmpty(true,true);

六、上拉加载
1:创建一个数据管理

public class DataServer {

    //分页加载数据
    public static List<Person> getData(int lenth) {
        List<Person> list = new ArrayList<>();
        for (int i = 0; i < lenth; i++) {
            Person person = new Person();
            person.setName("name"+i);
            person.setDes("描述"+i);
            person.setAge(i);
            list.add(person);
        }
        return list;
    }
}

2:设置上啦加载

//当前个数
    private  int mCurrentCounter =0;
    //总数
    private  int TOTAL_COUNTER =100;
    //每次取多少个
    private  int PAGE_SIZE =10;
    //是否加载失败
    private boolean isErr =true;

 adapter.setOnLoadMoreListener(new BaseQuickAdapter.RequestLoadMoreListener() {
            @Override
            public void onLoadMoreRequested() {
                recyclerView.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        if (mCurrentCounter >= TOTAL_COUNTER) {
                            //数据全部加载完毕
                            adapter.loadMoreEnd();
                        } else {
                            if (isErr) {
                                //成功获取更多数据(可以直接往适配器添加数据)
                                adapter.addData(DataServer.getData(PAGE_SIZE));
                                mCurrentCounter = adapter.getData().size();
                                //主动调用加载完成,停止加载
                                adapter.loadMoreComplete();
                            } else {
                                //获取更多数据失败
                                isErr = true;
                                Toast.makeText(MainActivity.this, R.string.network_err, Toast.LENGTH_LONG).show();
                                //同理,加载失败也要主动调用加载失败来停止加载(而且该方法会提示加载失败)
                                adapter.loadMoreFail();

                            }
                        }
                    }

                }, 300);
            }
        },recyclerView);

加载完成(注意不是加载结束,而是本次数据加载结束并且还有下页数据)

adapter.loadMoreComplete()

加载失败

adapter.loadMoreFail();

加载结束

adapter.loadMoreEnd();

注意:如果上拉结束后,下拉刷新需要再次开启上拉监听,需要使用setNewData方法填充数据。
打开或关闭加载(一般用于下拉的时候做处理,因为上拉下拉不能同时操作)

adapter.setEnableLoadMore(boolean);

预加载(这个功能屌炸天)

// 当列表滑动到倒数第N个Item的时候(默认是1)回调onLoadMoreRequested方法
adapter.setPreLoadNumber(int);
adapter.setPreLoadNumber(3);

设置自定义加载布局

adapter.setLoadMoreView(new CustomLoadMoreView());

public final class CustomLoadMoreView extends LoadMoreView {

    @Override
    public int getLayoutId() {
        return R.layout.view_load_more;
    }

    /**
     * 如果返回true,数据全部加载完毕后会隐藏加载更多
     * 如果返回false,数据全部加载完毕后会显示getLoadEndViewId()布局
     */
    @Override
    public boolean isLoadEndGone() {
        return true;
    }

    @Override
    protected int getLoadingViewId() {
        return R.id.load_more_loading_view;
    }

    @Override
    protected int getLoadFailViewId() {
        return R.id.load_more_load_fail_view;
    }

    /**
     * isLoadEndGone()为true,可以返回0
     * isLoadEndGone()为false,不能返回0
     */
    @Override
    protected int getLoadEndViewId() {
        return 0;
    }
}

七、下拉加载(符合聊天软件下拉历史数据需求)

mAdapter.setUpFetchEnable(true);
mAdapter.setUpFetchListener(new BaseQuickAdapter.UpFetchListener() {
            @Override
            public void onUpFetch() {
                startUpFetch();
            }
        });
private void startUpFetch() {
        count++;
        /**
         * set fetching on when start network request.
         */
        mAdapter.setUpFetching(true);
        /**
         * get data from internet.
         */
        mRecyclerView.postDelayed(new Runnable() {
            @Override
            public void run() {
                mAdapter.addData(0, genData());
                /**
                 * set fetching off when network request ends.
                 */
                mAdapter.setUpFetching(false);
                /**
                 * set fetch enable false when you don't need anymore.
                 */
                if (count > 5) {
                    mAdapter.setUpFetchEnable(false);
                }
            }
        }, 300);
    }

八:多布局
1:实体类

public class Person {
    private String name;
    private int age;
    private String des;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getDes() {
        return des;
    }

    public void setDes(String des) {
        this.des = des;
    }
}

2:多布局设置类型 MultipleItem implements MultiItemEntity

/**
 * 多布局展示
 * */
//实体类必须实现MultiItemEntity,在设置数据的时候,需要给每一个数据设置itemType
public class MultipleItem implements MultiItemEntity {

    public static final int FIRST_TYPE = 1;
    public static final int SECOND_TYPE = 2;
    public static final int NORMAL_TYPE = 3;

    private int itemType;
    private Person person;

    public MultipleItem(int itemType, Person person) {
        this.itemType = itemType;
        this.person = person;
    }

    @Override
    public int getItemType() {
        return itemType;
    }
    public Person getPerson(){
        return person;
    }
}

3:适配器 继承 BaseMultiItemQuickAdapter
addItemType 区分类型
helper.getItemViewType() 实现多布局展示

/**
 * 多布局adapter
 * */
public class MoreQuickAdapter extends BaseMultiItemQuickAdapter<MultipleItem, BaseViewHolder> {

    private Context context;

    public MoreQuickAdapter(List<MultipleItem> data, Context context) {
        super(data);
        this.context = context;
        addItemType(MultipleItem.FIRST_TYPE, R.layout.first_type_layout);
        addItemType(MultipleItem.SECOND_TYPE, R.layout.second_type_layout);
        addItemType(MultipleItem.NORMAL_TYPE, R.layout.item_rv);
    }

    @Override
    protected void convert(@NonNull BaseViewHolder helper, MultipleItem item) {

        switch (helper.getItemViewType()) {
            case MultipleItem.FIRST_TYPE:
                helper.setText(R.id.tv_title,item.getPerson().getName()+"布局一")
                        .setTextColor(R.id.tv_title,context.getResources().getColor(R.color.colorAccent))
                        .setText(R.id.tv_age,item.getPerson().getAge()+"")
                        .setTextColor(R.id.tv_age,context.getResources().getColor(R.color.colorAccent))
                        .setText(R.id.tv_content,item.getPerson().getDes()+"")
                        .setTextColor(R.id.tv_content,context.getResources().getColor(R.color.colorAccent));
                break;
            case MultipleItem.SECOND_TYPE:
                helper.setText(R.id.tv_title,item.getPerson().getName()+"布局二")
                        .setTextColor(R.id.tv_title,context.getResources().getColor(R.color.colorPrimary))
                        .setText(R.id.tv_age,item.getPerson().getAge()+"")
                        .setTextColor(R.id.tv_age,context.getResources().getColor(R.color.colorPrimary))
                        .setText(R.id.tv_content,item.getPerson().getDes()+"")
                        .setTextColor(R.id.tv_content,context.getResources().getColor(R.color.colorPrimary));
                break;
            case MultipleItem.NORMAL_TYPE:
                helper.setText(R.id.tv_title,item.getPerson().getName()+"正常")
                        .setTextColor(R.id.tv_title,context.getResources().getColor(R.color.color1bd))
                        .setText(R.id.tv_age,item.getPerson().getAge()+"")
                        .setTextColor(R.id.tv_age,context.getResources().getColor(R.color.color1bd))
                        .setText(R.id.tv_content,item.getPerson().getDes()+"")
                        .setTextColor(R.id.tv_content,context.getResources().getColor(R.color.color1bd));
                break;
        }
    }
}

4:activity

private RecyclerView recyclerView;
    private ArrayList<Person> data;
    private ArrayList<MultipleItem> multipleItems;
    private MoreQuickAdapter adapter;
recyclerView =findViewById(R.id.recycler_view);
        data =new ArrayList<>();
        multipleItems =new ArrayList<>();
        for (int i = 0; i < 30; i++) {
            Person person =new Person();
            person.setAge(i);
            person.setName("name"+i);
            person.setDes("描述"+i);
            data.add(person);
        }
        //这里我是随机给某一条目加载不同的布局
        for (int i = 0; i < 30; i++) {
            if (i % 3 == 0) {
                multipleItems.add(new MultipleItem(MultipleItem.FIRST_TYPE, data.get(i)));
            } else if (i % 7 == 0) {
                multipleItems.add(new MultipleItem(MultipleItem.SECOND_TYPE, data.get(i)));
            } else {
                multipleItems.add(new MultipleItem(MultipleItem.NORMAL_TYPE, data.get(i)));
            }
        }

        LinearLayoutManager manager =new LinearLayoutManager(this);
        manager.setOrientation(LinearLayoutManager.VERTICAL);
        recyclerView.setLayoutManager(manager);
        adapter =new MoreQuickAdapter(multipleItems,this);
        recyclerView.setAdapter(adapter);

十:添加拖拽、滑动删除
拖拽和滑动删除的回调方法:

public class ItemDragAdapter extends BaseItemDraggableAdapter<Person,BaseViewHolder> {

    public ItemDragAdapter(int layoutResId, List<Person> data) {
        super(layoutResId, data);
    }

    @Override
    protected void convert(@NonNull BaseViewHolder helper, Person item) {
        helper.setText(R.id.tv_title,item.getName()+"")
                .setText(R.id.tv_age,item.getAge()+"")
                .setText(R.id.tv_content,item.getDes()+"");

    }
}

activity:

LinearLayoutManager manager = new LinearLayoutManager(this);
        manager.setOrientation(LinearLayoutManager.VERTICAL);
        recyclerView.setLayoutManager(manager);

        adapter = new ItemDragAdapter(R.layout.second_type_layout,data);

        ItemDragAndSwipeCallback itemDragAndSwipeCallback = new ItemDragAndSwipeCallback(adapter);
        ItemTouchHelper itemTouchHelper = new ItemTouchHelper(itemDragAndSwipeCallback);
        itemTouchHelper.attachToRecyclerView(recyclerView);

        // 开启拖拽
        adapter.enableDragItem(itemTouchHelper, R.id.rl_layout, true);
        adapter.setOnItemDragListener(onItemDragListener);

        // 开启滑动删除
        adapter.enableSwipeItem();
        adapter.setOnItemSwipeListener(onItemSwipeListener);

        recyclerView.setAdapter(adapter);
OnItemDragListener onItemDragListener =new OnItemDragListener() {
        @Override
        public void onItemDragStart(RecyclerView.ViewHolder viewHolder, int pos) {
            Log.e("wudi", "onItemDragStart: "+pos );
        }

        @Override
        public void onItemDragMoving(RecyclerView.ViewHolder source, int from, RecyclerView.ViewHolder target, int to) {
            Log.e("wudi", "onItemDragMoving: from="+from+",to="+to );
        }

        @Override
        public void onItemDragEnd(RecyclerView.ViewHolder viewHolder, int pos) {
            Log.e("wudi", "onItemDragEnd: "+pos );
        }
    };
    OnItemSwipeListener  onItemSwipeListener =new OnItemSwipeListener () {

        @Override
        public void onItemSwipeStart(RecyclerView.ViewHolder viewHolder, int pos) {
            Log.e("wudi", "onItemSwipeStart: "+pos );
        }

        @Override
        public void clearView(RecyclerView.ViewHolder viewHolder, int pos) {

        }

        @Override
        public void onItemSwiped(RecyclerView.ViewHolder viewHolder, int pos) {

        }

        @Override
        public void onItemSwipeMoving(Canvas canvas, RecyclerView.ViewHolder viewHolder, float dX, float dY, boolean isCurrentlyActive) {

        }
    };

新增
适配器设置数据,设置点击固定数据的点击事件

Log.e(TAG, "convert:helper.getLayoutPosition()= "+helper.getLayoutPosition() );
        switch (helper.getLayoutPosition() % 3) {
            case 0:
                helper.setImageResource(R.id.image, R.mipmap.animation_img1);
                break;
            case 1:
                helper.setImageResource(R.id.image, R.mipmap.animation_img2);
                break;
            case 2:
                helper.setImageResource(R.id.image, R.mipmap.animation_img3);
                break;
            default:
                break;
        }
/**
         * 设置tv_title 样式
         * */
        helper.setText(R.id.tv_title,SpannableStringUtils.getBuilder(item.getName()).append("landscapes and nedes")
                    .setClickSpan(clickableSpan)
                    .setBoldItalic()
                .setProportion(1.5f)
                .setStrikethrough()
                .setUnderline()
                .setSuperscript()
                .setSubscript()
                .setQuoteColor(R.color.color1bd)
                .create()); //给标题增加点击事件;
        ((TextView) helper.getView(R.id.tv_title)).setMovementMethod(ClickableMovementMethod.getInstance());
        ((TextView) helper.getView(R.id.tv_title)).setFocusable(false);
        ((TextView) helper.getView(R.id.tv_title)).setClickable(false);
        ((TextView) helper.getView(R.id.tv_title)).setLongClickable(false);
//点击
 private ClickableSpan clickableSpan = new ClickableSpan() {
        @Override
        public void onClick(View widget) {
            ToastUtils.showShortToast("事件触发了 landscapes and nedes");
        }

        @Override
        public void updateDrawState(TextPaint ds) {
            ds.setColor(content.getResources().getColor(R.color.colorAccent));
            ds.setUnderlineText(true);
        }
    };