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" }
}
}
二:基本应用
- 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方法
}
- 编写适配器
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);
}
};