RecyclerView零點突破(基本使用篇)
一直以為自己是會用RecyclerView的,但現在感覺只是剛入門而已
本文以仿幾個生活常見的介面來演示RecyclerView
本系列分為3篇:
- RecyclerView零點突破(基本使用篇)
- RecyclerView零點突破(動畫+邊線篇)
- RecyclerView零點突破(詳細分析篇)
一、入門級-Adapter:仿QQ訊息列表

rv_qq_item.gif

Adapter.png
1.建立一個item佈局:

qq_itme.png
<?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/> * 郵箱:[email protected]<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(仿淘寶商品列表)

recyclerView.png
瀑布流 | 網格流 |
---|---|
![]() |
![]() |
1.建立一個item佈局:

item.png
<?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/> * 郵箱:[email protected]<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( ofollow,noindex">自動生成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操作效果):

條目的操作.png
1.點選+水波紋

item點選+波紋.gif
//為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/> * 郵箱:[email protected]<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/> * 郵箱:[email protected]<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
和n otifyItemMoved
呼叫時,item所在的position是不更新的
public void addItem(int position, GoodsBean bean) { mData.add(position, bean); notifyItemInserted(position);//重新整理插入資料---將不重新整理position if (position == 0) { mRecyclerView.scrollToPosition(0); } }
三、初級-多條目(仿微信聊天)

多條目.png
多個條目 | 型別分析 |
---|---|
![]() |
![]() |
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/> * 郵箱:[email protected]<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/> * 郵箱:[email protected]<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));
後記:捷文規範
1.本文成長記錄及勘誤表
專案原始碼 | 日期 | 備註 |
---|---|---|
V0.1--github | 2018-12-4 | RecyclerView零點突破(基本使用篇) |
2.更多關於我
筆名 | 微信 | 愛好 | |
---|---|---|---|
張風捷特烈 | 1981462002 | zdl1994328 | 語言 |
我的github | 我的簡書 | 我的掘金 | 個人網站 |
3.宣告
1----本文由張風捷特烈原創,轉載請註明
2----歡迎廣大程式設計愛好者共同交流
3----個人能力有限,如有不正之處歡迎大家批評指證,必定虛心改正
4----看到這裡,我在此感謝你的喜歡與支援

icon_wx_200.png