Android入門教程 | RecyclerView實際使用
RecyclerView實際使用
用 RecyclerView 做出一個列表。顯示測試資料。
這個示例會分為以下幾個步驟:
- 確定資料。這裡用的是模擬資料。
- 設計UI和表現形式。
- 編寫 layout 與介面卡。
模擬資料
巧婦難為無米之炊,模擬資料就是模擬需求。
新建一個類 DataTest,設計 4 個屬性。
public class DataTest { private String timezone; private int number; private int personCount; private int count; public DataTest(String timezone, int number, int personCount, int count) { this.timezone = timezone; this.number = number; this.personCount = personCount; this.count = count; } // getter setter... }
設計 item 的佈局
設計UI,在一行裡顯示 4 個屬性值。item 用的是 item_recy2.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android=" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <TextView android:id="@+id/tv1" style="@style/RePage2Header" android:layout_marginEnd="@dimen/re_2_half_gap" /> <TextView android:id="@+id/tv2" style="@style/RePage2Header" android:layout_marginStart="@dimen/re_2_half_gap" android:layout_marginEnd="@dimen/re_2_half_gap" /> <TextView android:id="@+id/tv3" style="@style/RePage2Header" android:layout_marginStart="@dimen/re_2_half_gap" android:layout_marginEnd="@dimen/re_2_half_gap" /> <TextView android:id="@+id/tv4" style="@style/RePage2Header" android:layout_marginStart="@dimen/re_2_half_gap" /> </LinearLayout>
準備顏色,尺寸等資源
相關的style和顏色,尺寸配置檔案,在
res/values
目錄下。
style檔案
style.xml
。
<style name="RePage2Header"> <item name="android:layout_width">0dp</item> <item name="android:layout_height">40dp</item> <item name="android:layout_weight">2</item> <item name="android:textColor">#ffffff</item> <item name="android:gravity">center</item> <item name="android:background">@color/rePage2Item</item> </style>
我們給 layout 裡的每個
TextView都設定了
layout_width
為 0dp。是為了使用
layout_weight
屬性。 讓它們 4 個
TextView 按比例分割父 View 的寬度。
這裡的分割用到了LinearLayout的分割佔位元性。
顏色配置檔案
color.xml
,新增如下顏色設定。
<color name="rePage2Item">#082941</color>
尺寸配置dimens.xml。
<dimen name="re_2_gap">4dp</dimen> <dimen name="re_2_half_gap">2dp</dimen>
設計 ViewHolder
資原始檔和 layout 準備妥當,開始寫對應的 viewHolder。這裡也是把 VH 類和 Adapter 類放在 activity 類裡面。
private class VH extends RecyclerView.ViewHolder { TextView tv1; TextView tv2; TextView tv3; TextView tv4; public VH(@NonNull View itemView) { super(itemView); tv1 = itemView.findViewById(R.id.tv1); tv2 = itemView.findViewById(R.id.tv2); tv3 = itemView.findViewById(R.id.tv3); tv4 = itemView.findViewById(R.id.tv4); } }
設計 Adapter 類
介面卡 Adapter 類。
private class Adapter extends RecyclerView.Adapter<VH> { private List<DataTest> dataList = new ArrayList<>(); public Adapter() { } public void setDataList(List<DataTest> dataList) { this.dataList = dataList; // 最好進行判空操作 notifyDataSetChanged(); } @NonNull @Override public VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { return new VH(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_recy2, parent, false)); } @Override public void onBindViewHolder(@NonNull VH holder, int position) { DataTest dataTest = dataList.get(position); holder.tv1.setText(dataTest.getTimezone()); holder.tv2.setText(String.valueOf(dataTest.getNumber())); holder.tv3.setText(String.valueOf(dataTest.getPersonCount())); holder.tv4.setText(String.valueOf(dataTest.getCount())); } @Override public int getItemCount() { return dataList.size(); } }
配置 RecyclerView
設定 recyclerview。
// 在activity或者fragment裡 private Adapter mAdapter = new Adapter(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.act_recy_2); RecyclerView recyclerView = findViewById(R.id.re_view); recyclerView.setLayoutManager(new LinearLayoutManager(this, RecyclerView.VERTICAL, false)); recyclerView.setAdapter(mAdapter); mAdapter.setDataList(genDataTestList()); recyclerView.addItemDecoration(new RecyclerView.ItemDecoration() { @Override public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) { super.getItemOffsets(outRect, view, parent, state); outRect.top = getResources().getDimensionPixelOffset(R.dimen.re_2_gap); } }); } // 生成模擬資料 private List<DataTest> genDataTestList() { List<DataTest> list = new ArrayList<>(); for (int i = 1; i <= 60; i++) { DataTest d = new DataTest("時區" + i, i, i, i); list.add(d); } return list; }
在 onCreate 方法中配置 recyclerView。 recyclerView.addItemDecoration 方法是給 item 設定間隔樣式。 getItemOffsets 可以設定子項的間距。這裡給子項底部一個間距值。具體數值設定在 dimen 中。 genDataTestList() 是生成模擬的資料。
使用 include 修改 layout
有的朋友問:表頭和item的結構是一樣的,可以複用嗎?
其實是可以的。我們可以在layout中使用
include
標籤,把另一個layout檔案“包含”進來。 複製
act_recy_2.xml
貼上得到
act_recy_2_include.xml
,把原來的表頭的 LinearLayout 改成 include。
<include layout="@layout/item_recy2" />
給 include 設定 layout,即我們定義的 item 的佈局
item_recy2
。 當我們想新增個
margin-top
的時候,比如這樣
<include android:layout_marginTop="4dp" layout="@layout/item_recy2" />
as會彈出警告:
Layout parameter layout_marginTop ignored unless both layout_width and layout_height are also specified on tag
也就是在 include 標籤中,如果要設定其他屬性,需要先設定 layout_width 和 layout_height。
修改一下,再加個 id,變成這樣。
<include android:id="@+id/header" layout="@layout/item_recy2" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="4dp" />
讓Activity使用這個layout。修改一下RecyclerViewDemo2Act。
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.act_recy_2_include); initHeader(); // 配置RecyclerView的部分 } private void initHeader() { View header = findViewById(R.id.header); TextView tv1 = header.findViewById(R.id.tv1); tv1.setText("時區"); TextView tv2 = header.findViewById(R.id.tv2); tv2.setText("序號"); TextView tv3 = header.findViewById(R.id.tv3); tv3.setText("人員"); TextView tv4 = header.findViewById(R.id.tv4); tv4.setText("數量"); }
我們新增了一個方法 initHeader()。裡面先把 header 找到,透過 header 找到它的子 view,也就是那4個TextView。 分別設定文字即可。
RecyclerView 相關面試題
1. RecyclerView第一次layout時,會發生預佈局pre-layout嗎?
第一次佈局時,並不會觸發pre-layout。pre-layout只會在每次notify change時才會被觸發,目的是透過saveOldPosition方法將螢幕中各位置上的ViewHolder的座標記錄下來,並在重新佈局之後,透過對比實現Item的動畫效果。
2. ViewHolder何時被快取到RecycledViewPool中?
主要有以下2種情況:
- 當ItemView被滑動出螢幕時,並且CachedView已滿,則ViewHolder會被快取到RecycledViewPool中
- 當資料發生變動時,執行完disappearrance的ViewHolder會被快取到RecycledViewPool中
3. CachedView 和 RecycledViewPool 的關係
當一個ItemView被滑動滾出螢幕之後,預設會先被儲存在CachedView中。CachedView的預設大小為2,可以透過 setItemViewCacheSize 方法修改它的值。
當CachedView已滿後,後續有新的ItemView從螢幕內滑出時,會迫使CachedView根據FIFO規則,將之前的快取的ViewHolder轉移到RecycledViewPool中.
RecycledViewPool預設大小為5,可以透過以下方式修改RecycledViewPool的快取大小:
RecyclerView.getRecycledViewPool().setMaxRecycledViews(int viewType, int max);
4. CachedView 和 RecycledViewPool 兩者區別
快取到CachedView中的ViewHolder並不會清理相關資訊(比如position、state等),因此剛移出螢幕的ViewHolder,再次被移回螢幕時,只要從CachedView中查詢並顯示即可,不需要重新繫結(bindViewHolder)。
而快取到RecycledViewPool中的ViewHolder會被清理狀態和位置資訊,因此從RecycledViewPool查詢到ViewHolder,需要重新呼叫bindViewHolder繫結資料。
5. 你是從哪些方面最佳化RecyclerView的?
主要可以從以下幾個方面對 RecyclerView 進行最佳化:
- 儘量將複雜的資料處理操作放到非同步中完成。RecyclerView 需要展示的資料經常是從遠端伺服器上請求獲取,但是在網路請求拿到資料之後,需要將資料做扁平化操作,儘量將最優質的資料格式返回給UI執行緒。
- 最佳化 RecyclerView 的佈局,避免將其與 ConstraintLayout 使用
- 針對快速滑動事件,可以使用 addOnScrollListener 新增對快速滑動的監聽,當使用者快速滑動時,停止載入資料操作。
- 如果 ItemView 的高度固定,可以使用 setHasFixSize(true)。這樣 RecyclerView 在 onMeasure 階段可以直接計算出高度,不需要多次計運算元 ItemView 的高度,這種情況對於垂直 RecyclerView 中巢狀橫向 RecyclerView 效果非常顯著。
- 當 UI 是 Tab feed 流時,可以考慮使用 RecycledViewPool 來實現多個 RecyclerView 的快取共享。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70008155/viewspace-2841198/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Android入門教程 | RecyclerView使用入門AndroidView
- Android入門教程 | RecyclerView響應子項點選AndroidView
- Android入門教程 | AsyncTask 使用介紹Android
- Android入門教程 | DialogFragment 的使用AndroidFragment
- Android入門教程 | EditText 使用者輸入Android
- Android使用RecyclerView實現二級列表AndroidView
- Android入門教程 | Kotlin協程入門AndroidKotlin
- Realm for Android快速入門教程Android
- Android SQLite快速入門教程AndroidSQLite
- Android RecyclerView的使用AndroidView
- 【Android開發入門教程】三.Activity入門指南!Android
- Android入門教程 | Handler,Looper與MessageQueue使用與分析AndroidOOP
- SpringBoot系列之Elasticsearch極速入門與實際教程Spring BootElasticsearch
- OpenStack入門之實際操作
- Android WorkManager使用入門Android
- Android入門教程 | SharedPreferences 簡介Android
- Android入門教程 | 多執行緒Android執行緒
- Android入門教程 | DrawerLayout 側滑欄Android
- Android入門教程 | Fragment 基礎概念AndroidFragment
- 【Android開發入門教程】四.使用者介面之LayoutAndroid
- Android中使用RecyclerView + SnapHelper實現類似ViewPager效果AndroidViewpager
- Systemd 入門教程:實戰篇
- Charles(Windows/Android)入門使用WindowsAndroid
- Android入門教程 | Fragment (載入方法與通訊)AndroidFragment
- Android入門教程 | 使用 ConstraintLayout 構建自適應介面AndroidAI
- 手把手教你實現Android RecyclerView上拉載入功能AndroidView
- Android OkHttp原始碼解析入門教程(一)AndroidHTTP原始碼
- Android OkHttp原始碼解析入門教程(二)AndroidHTTP原始碼
- Android入門教程 | Button,TextView背景設定AndroidTextView
- Android手機遊戲開發入門教程Android遊戲開發
- glide從入門到使用教程IDE
- Puppeteer的入門教程和實踐
- ROS基礎入門——實操教程ROS
- Android入門(五):實踐技巧Android
- Android入門教程 | 廣播機制 BroadcastAndroidAST
- Android Studio教程從入門到精通2.0Android
- 傻瓜式Android APP開發入門教程AndroidAPP
- 初學者的機器學習入門實戰教程!機器學習