一、入门级-Adapter:仿QQ消息列表

1.创建一个item布局:
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="wrap_content"> <ImageView android:id="@+id/id_iv_qq_head" android:layout_width="@dimen/item_qq_msg_iv_size" android:layout_height="@dimen/item_qq_msg_iv_size" android:layout_marginStart="@dimen/dp_16" android:layout_marginTop="@dimen/dp_8" android:layout_marginBottom="@dimen/dp_8" android:src="@mipmap/head" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent"/> <android.support.constraint.ConstraintLayout android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginStart="@dimen/dp_16" android:layout_marginEnd="@dimen/dp_16" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toEndOf="@id/id_iv_qq_head" app:layout_constraintTop_toTopOf="parent"> <TextView android:id="@+id/id_tv_qq_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="张风捷特烈" android:textSize="@dimen/sp_16"/> <TextView android:id="@+id/id_tv_time" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="2018-11-30" android:textSize="@dimen/sp_12" app:layout_constraintBottom_toBottomOf="@id/id_tv_qq_name" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="@id/id_tv_qq_name"/> <TextView android:id="@+id/id_tv_info" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="@dimen/dp_4" android:text="天下无双" android:textColor="#aaa" android:textSize="@dimen/sp_12" app:layout_constraintTop_toBottomOf="@id/id_tv_qq_name"/> <com.toly1994.test.view.CountTextView android:id="@+id/id_ctv_num" android:layout_width="wrap_content" android:layout_height="wrap_content" app:layout_constraintBottom_toBottomOf="@id/id_tv_info" app:layout_constraintEnd_toEndOf="@+id/id_tv_time" app:layout_constraintTop_toTopOf="@+id/id_tv_info" app:z_bg_color="@color/red" app:z_ctv_font_size="@dimen/sp_14" app:z_ctv_num="10"/> </android.support.constraint.ConstraintLayout> </android.support.constraint.ConstraintLayout>
2.创建ViewHolder
至于为什么要ViewHolder,这里简单的讲一下:
item的布局里面有控件,将这些控件封装起来放在一个类中,使用的时候相当于对成员变量的使用
避免用一次找一次。就像你通过身份证(id)去找人,不用ViewHolder的话,你每次用他都要去找一次
使用ViewHolder相当于把他放旁边放着,使用的时候直接拿,就不需要用id再去找他了
/**
* ViewHolder
*/
class MyViewHolder extends RecyclerView.ViewHolder {
private TextView mItemTV;
public MyViewHolder(View itemView) {
super(itemView);
mItemTV = itemView.findViewById(R.id.id_tv_qq_name);
}
}3.适配器:Adapter
/**
* 作者:张风捷特烈<br/>
* 时间:2018/12/2 0002:9:22<br/>
* 邮箱:1981462002@qq.com<br/>
* 说明:qq信息列表Adapter
*/
public class QQRvAdapter extends RecyclerView.Adapter<QQRvAdapter.MyViewHolder> {
private static final String TAG = "TolyRvAdapter";
private List<String> mData;
private Context mContext;
public QQRvAdapter(List<String> data) {
mData = data;
}
@NonNull
@Override//将item布局文件与ViewHolder结合
public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
mContext = parent.getContext();
View view = LayoutInflater.from(mContext)
.inflate(R.layout.item_qq_msg, parent, false);
return new MyViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
holder.mItemTV.setText(mData.get(position));
}
@Override
public int getItemCount() {
return mData.size();
}
}4.使用
mIdRvContent.addItemDecoration(new RecycleViewDivider(this, LinearLayoutManager.VERTICAL));//分割线 mACAdapter = new QQRvAdapter(DataUtils.getRandomName(60, true));//初始化适配器 mIdRvContent.setAdapter(mACAdapter);//设置适配器 mIdRvContent.setLayoutManager(new LinearLayoutManager(this));
二、入门级-LayoutManager(仿淘宝商品列表)
| 瀑布流 | 网格流 |
|---|---|
![]() | ![]() |
1.创建一个item布局:
<?xml version="1.0" encoding="utf-8"?> <android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="@dimen/dp_8" app:cardCornerRadius="@dimen/dp_8" app:cardElevation="@dimen/dp_4" app:cardPreventCornerOverlap="false"> <android.support.constraint.ConstraintLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <ImageView android:id="@+id/id_iv_goods" android:layout_width="match_parent" android:layout_height="wrap_content" android:adjustViewBounds="true" android:scaleType="fitStart" android:src="@mipmap/pic1" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent"/> <TextView android:id="@+id/id_iv_info" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_margin="@dimen/dp_8" android:text="蓝夜皮肤,2030年爆款,限额三件,先到先得,售完为止" android:textColor="@color/black" android:textSize="@dimen/sp_12" app:layout_constraintEnd_toEndOf="@id/id_iv_goods" app:layout_constraintStart_toStartOf="@id/id_iv_goods" app:layout_constraintTop_toBottomOf="@id/id_iv_goods"/> <android.support.constraint.ConstraintLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="@dimen/dp_8" android:orientation="horizontal" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/id_iv_info"> <TextView android:id="@+id/id_yang" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="@dimen/dp_2" android:text="¥" android:textColor="@color/red" android:textSize="@dimen/sp_12" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent"/> <TextView android:id="@+id/id_goods_price" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="99999" android:textColor="@color/red" android:textSize="@dimen/sp_18" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toEndOf="@id/id_yang"/> <TextView android:id="@+id/id_goods_buy_num" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="@dimen/dp_8" android:text="1人已付款" android:textColor="@color/gray_8f" android:textSize="@dimen/sp_12" app:layout_constraintBottom_toBottomOf="@id/id_yang" app:layout_constraintStart_toEndOf="@id/id_goods_price"/> <ImageView android:id="@+id/id_iv_btn_more" android:layout_width="10dp" android:layout_height="10dp" android:src="@drawable/icon_more_h" android:tint="@color/gray_8f" app:layout_constraintBottom_toBottomOf="@id/id_goods_buy_num" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="@id/id_goods_buy_num"/> </android.support.constraint.ConstraintLayout> </android.support.constraint.ConstraintLayout> </android.support.v7.widget.CardView>
2.数据准备
/**
* 作者:张风捷特烈<br/>
* 时间:2018/12/4 0004:8:43<br/>
* 邮箱:1981462002@qq.com<br/>
* 说明:商品的实体类
*/
public class GoodsBean {
private int imgId; //资源id
private String info; //详情
private float price;//价格
private int buyNum;//购买人数
private String ticket;//优惠券
//其他,略....
}public class BeanFactory {
public static List<GoodsBean> getGoodsBean() {
List<GoodsBean> beans = new ArrayList<>();
for (int i = 0; i < 4; i++) {
beans.add(new GoodsBean(R.mipmap.pic4, "混沌战士,等比例人形,附加刀及盔甲,2030年爆款,限额三百件,先到先得,售完为止", 6666, 277, "店铺优惠,满100送10"));
beans.add(new GoodsBean(R.mipmap.pic1, "蓝夜皮肤,2030年爆款,限额三件,先到先得,售完为止", 99999, 2));
beans.add(new GoodsBean(R.mipmap.pic3, "古典美女,等比例人形,2030年爆款,限额三百件,先到先得,售完为止", 999, 177, "店铺优惠,满100送1000"));
beans.add(new GoodsBean(R.mipmap.pic6, "珍藏,非卖品", 9999999, 1));
beans.add(new GoodsBean(R.mipmap.pic2, "黑夜皮肤,附加魔法加成,2030年爆款,限额三百件,先到先得,售完为止", 8888, 277, "店铺优惠,满100送100000"));
beans.add(new GoodsBean(R.mipmap.pic5, "买洞爷湖送银时,只要998,绝对良心价,2030年爆款,限额三百件,先到先得,售完为止", 998, 277, "店铺优惠,满100送100000"));
}
return beans;
}
}3.适配器:GoodsAdapter(自动生成Adapter代码,感觉非常棒:详见)
public class GoodsAdapter extends RecyclerView.Adapter<GoodsAdapter.MyViewHolder> {
private Context mContext;
private List<GoodsBean> mData;
public GoodsAdapter(List<GoodsBean> data) {
mData = data;
}
@NonNull
@Override
public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
mContext = parent.getContext();
View view = LayoutInflater.from(mContext).inflate(R.layout.item_goods_list, parent, false);
return new MyViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
GoodsBean str = mData.get(position);
holder.mIdIvGoods.setImageResource(str.getImgId());
holder.mIdGoodsPrice.setText(str.getPrice()+"");
holder.mIdIvInfo.setText(str.getInfo());
holder.mIdGoodsBuyNum.setText(str.getBuyNum()+"人已付款");
}
@Override
public int getItemCount() {
return mData.size();
}
class MyViewHolder extends RecyclerView.ViewHolder {
public ImageView mIdIvGoods;
public TextView mIdYang;
public TextView mIdGoodsPrice;
public TextView mIdIvInfo;
public TextView mIdGoodsBuyNum;
public ImageView mIdIvBtnMore;
public MyViewHolder(View itemView) {
super(itemView);
mIdIvGoods = itemView.findViewById(R.id.id_iv_goods);
mIdYang = itemView.findViewById(R.id.id_yang);
mIdGoodsPrice = itemView.findViewById(R.id.id_goods_price);
mIdIvInfo = itemView.findViewById(R.id.id_iv_info);
mIdGoodsBuyNum = itemView.findViewById(R.id.id_goods_buy_num);
mIdIvBtnMore = itemView.findViewById(R.id.id_iv_btn_more);
}
}
}4.使用:
//初始化RecyclerView @BindView(R.id.id_rv_goods) RecyclerView mIdRvGoods; //使用 mIdRvGoods.setAdapter(new GoodsAdapter(BeanFactory.getGoodsBean())); mIdRvGoods.setLayoutManager(new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL));
//网格流 mIdRvGoods.setLayoutManager( new GridLayoutManager(this, 3, GridLayoutManager.VERTICAL, false));
二、初级(Item操作效果):
1.点击+水波纹

//为holder.itemView(即条目的View)设置点击事件
@Override
public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
String name = mData.get(position);
holder.mItemTV.setText(name);
holder.itemView.setOnClickListener(v -> {
ToastUtil.show(mContext, "第" + position + "个:" + name);
});
}//item的最顶层设置: android:background="?android:attr/selectableItemBackground"
番外:你也可以自定义水波纹效果(安卓5.0+)
<?xml version="1.0" encoding="utf-8"?> <ripple xmlns:android="http://schemas.android.com/apk/res/android" android:color="#8834C4F2"><!-- press和水波纹的颜色 --> <item> <shape android:innerRadius="5dp" android:shape="rectangle"> <solid android:color="@color/white"/> <corners android:radius="1dp"/> </shape> </item> </ripple>
2.条目的删除和移动操作:
好吧,炫到晃眼
| 删除条目 | 移动条目 |
|---|---|
![]() |
1).先定义操作接口:AdapterItemOp
/**
* 作者:张风捷特烈<br/>
* 时间:2018/12/3 0003:20:20<br/>
* 邮箱:1981462002@qq.com<br/>
* 说明:Item 操作的接口
*/
public interface AdapterItemOp<T> {
/**
* 交换条目
*
* @param from 起点
* @param to 终点
*/
void onItemMove(int from, int to);
/**
* 删除条目
*
* @param position 位置
*/
void onItemDelete(int position);
}2).自定义条目触摸时的回调
/**
* 作者:张风捷特烈<br/>
* 时间:2018/12/3 0003:20:20<br/>
* 邮箱:1981462002@qq.com<br/>
* 说明:条目触摸时的回调
*/
public class ItemTouchCallback extends ItemTouchHelper.Callback {
private AdapterItemOp mAdapter;//操作接口
public ItemTouchCallback(AdapterItemOp adapter) {
mAdapter = adapter;
}
@Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
//上下左右拖动
int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN |
ItemTouchHelper.RIGHT | ItemTouchHelper.LEFT;
//可向左滑动---删除
int swipeFlags = ItemTouchHelper.LEFT;
return makeMovementFlags(dragFlags, swipeFlags);
}
@Override//长按拖动
public boolean isLongPressDragEnabled() {
return true;
}
@Override//滑动删除
public boolean isItemViewSwipeEnabled() {
return true;
}
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
//移动时:---交换两个ViewHolder的位置
mAdapter.onItemMove(viewHolder.getAdapterPosition(), target.getAdapterPosition());
return true;
}
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
//滑动删除时:---
mAdapter.onItemDelete(viewHolder.getAdapterPosition());
}
}3).GoodsAdapter实现AdapterItemOp接口
用接口为了方便使用,在GoodsAdapter里直接写操作方法,ItemTouchCallback传入GoodsAdapter也可以
不过当其他的Adapter也需要操作时,要修改ItemTouchCallback,所以两者耦合度太高
@Override
public void onItemMove(int from, int to) {
//交换位置
ToastUtil.showAtOnce(mContext, "已交换:" + from + "和" + to + "的位置");
Collections.swap(mData, from, to);
notifyItemMoved(from, to);//刷新移动数据---将不刷新position
}
@Override
public void onItemDelete(int position) {
//移除数据
ToastUtil.showAtOnce(mContext, "已删除:" + position);
mData.remove(position);
notifyItemRemoved(position);//刷新移除数据---将不刷新position
}5.条目的添加:(adapter)
注:可以将addItem方法也放在操作接口
注意:notifyItemRemoved、notifyItemInserted和notifyItemMoved调用时,item所在的position是不更新的
public void addItem(int position, GoodsBean bean) {
mData.add(position, bean);
notifyItemInserted(position);//刷新插入数据---将不刷新position
if (position == 0) {
mRecyclerView.scrollToPosition(0);
}
}三、初级-多条目(仿微信聊天)
| 多个条目 | 类型分析 |
|---|---|
![]() |
1.三个布局
1).布局:type0
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="@dimen/dp_16"> <ImageView android:id="@+id/id_iv_chat_head" android:layout_width="@dimen/item_qq_msg_iv_size" android:layout_height="@dimen/item_qq_msg_iv_size" android:layout_marginEnd="@dimen/dp_8" android:layout_marginBottom="@dimen/dp_8" android:src="@mipmap/head" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent"/> <TextView android:id="@+id/id_tv_chat_msg" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginEnd="@dimen/dp_4" android:background="@mipmap/me" android:gravity="center" android:maxWidth="280dp" android:text="天下无双" android:textColor="@color/black" android:textSize="@dimen/sp_16" app:layout_constraintEnd_toStartOf="@+id/id_iv_chat_head" app:layout_constraintTop_toTopOf="@+id/id_iv_chat_head"/> </android.support.constraint.ConstraintLayout>
2).布局:type1
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="@dimen/dp_16"> <ImageView android:id="@+id/id_iv_chat_head" android:layout_width="@dimen/item_qq_msg_iv_size" android:layout_height="@dimen/item_qq_msg_iv_size" android:layout_marginStart="@dimen/dp_8" android:layout_marginBottom="@dimen/dp_8" android:src="@mipmap/icon_gql" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent"/> <TextView android:id="@+id/id_tv_chat_msg" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="@dimen/dp_4" android:background="@mipmap/he" android:gravity="center" android:maxWidth="280dp" android:text="天下无双" android:textColor="@color/black" android:textSize="@dimen/sp_16" app:layout_constraintStart_toEndOf="@+id/id_iv_chat_head" app:layout_constraintTop_toTopOf="@+id/id_iv_chat_head"/> </android.support.constraint.ConstraintLayout>
3).布局:type2
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_marginBottom="@dimen/dp_8" android:layout_height="wrap_content"> <TextView android:id="@+id/id_tv_time" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="8dp" android:layout_marginEnd="8dp" android:background="@drawable/shape_round_rect" android:padding="@dimen/dp_4" android:text="12月1日 早上11:45" android:textColor="@color/white" android:textSize="@dimen/sp_14" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent"/> </android.support.constraint.ConstraintLayout>
番外:圆角矩形的shape
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> <solid android:color="#88888888"/> <corners android:radius="4dp"/> </shape>
2.数据的初始化
1).设计Adapter中数据的实体类
/**
* 作者:张风捷特烈<br/>
* 时间:2018/12/3 0003:16:22<br/>
* 邮箱:1981462002@qq.com<br/>
* 说明:消息的实体类
*/
public class MsgBean {
private int type;//类型:0 我 1 他 2 时间
private String msg;//信息体
//get、set、构造,略...
}2).准备数据
注:聊天数据从:
《匆匆》中获取0~100个字随机拼接
/**
* 获取随机聊天消息
* @return
*/
public static List<MsgBean> getMsgBeans() {
List<MsgBean> beans = new ArrayList<>();
for (int i = 0; i < 60; i++) {
if (i % 10 == 0) {
beans.add(new MsgBean(2, ""));
continue;
}
beans.add(new MsgBean(ZRandom.rangeInt(0, 1),
ZRandom.randomChar(ZData.congcong, 100)));
}
return beans;
}3.适配器:Adapter
/**
* 作者:张风捷特烈<br/>
* 时间:2018/12/2 0002:9:22<br/>
* 邮箱:1981462002@qq.com<br/>
* 说明:聊天的适配器
*/
public class ChatRvAdapter extends RecyclerView.Adapter<ChatRvAdapter.MyViewHolder> {
private static final String TAG = "TolyRvAdapter";
private List<MsgBean> mData;
private Context mContext;
public ChatRvAdapter(List<MsgBean> data) {
mData = data;
}
@NonNull
@Override//将item布局文件与ViewHolder结合
public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
mContext = parent.getContext();
View view = null;
switch (viewType) {//对不同的viewType加载不同的布局
case 0:
view = LayoutInflater.from(mContext)
.inflate(R.layout.item_chat_me, parent, false);
break;
case 1:
view = LayoutInflater.from(mContext)
.inflate(R.layout.item_chat_he, parent, false);
break;
case 2:
view = LayoutInflater.from(mContext)
.inflate(R.layout.item_chat_time, parent, false);
break;
}
return new MyViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
MsgBean msgBean = mData.get(position);
switch (msgBean.getType()) {//对不同的viewType设置不同的数据
case 0:
case 1:
holder.mItemTV.setText(msgBean.getMsg());
break;
case 2:
String time = new SimpleDateFormat("MM月dd日 a HH:mm", Locale.CHINA)
.format(System.currentTimeMillis());
holder.mItemTvTime.setText(time);
break;
}
}
@Override
public int getItemCount() {
return mData.size();
}
@Override
public int getItemViewType(int position) {//返回条目种类viewType
return mData.get(position).getType();
}
/**
* ViewHolder
*/
class MyViewHolder extends RecyclerView.ViewHolder {
private TextView mItemTV;
private TextView mItemTvTime;
public MyViewHolder(View itemView) {
super(itemView);
mItemTV = itemView.findViewById(R.id.id_tv_chat_msg);
mItemTvTime = itemView.findViewById(R.id.id_tv_time);
}
}
}4.使用:
mIdRvContent.setAdapter(new ChatRvAdapter(getMsgBeans())); mIdRvContent.setLayoutManager(new LinearLayoutManager(this));
四、滑动监听:
newState:1:开始下滑---0:手指离开屏幕---2:手指离开屏幕,但还在滑动dx,dy每次移动的距离
mIdRvContent.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
ToastUtil.show(V012_QQMsgActivity.this, "newState:" + newState);
}
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
}
});作者:张风捷特烈



