Android應用開發中RecyclerView組件使用入門教程_Android

來源:互聯網
上載者:User

RecyclerView是一種列表容器, 發布很久了, 才想起來寫點什麼.

RecyclerView相比於ListView, 在回收重用時更具有靈活性, 也就是低耦合, 並且提供了擴充. 載入多個視圖時, 應該多用RecyclerView代替ListView.

那麼我們來看看這東西應該怎麼用? 比如產生一個瀑布流的視圖.

首先我們從一個HelloWorld寫起, 看看如何構建一個RecyclerView.

1. 依賴庫
Gradle配置, 添加Recycler庫

compile 'com.android.support:recyclerview-v7:+'

2. 資源檔
資源檔

 <android.support.v7.widget.RecyclerView  android:id="@+id/test_recycler_view"  android:layout_width="match_parent"  android:layout_height="match_parent"/>
3. 代碼
LayoutManager: 管理RecyclerView的結構.
Adapter: 處理每個Item的顯示.
ItemDecoration: 添加每個Item的裝飾.
ItemAnimator: 負責添加\移除\重排序時的動畫效果.
LayoutManager\Adapter是必須, ItemDecoration\ItemAnimator是可選. /**  * 初始化RecyclerView  *  * @param recyclerView 主控制項  */ private void initRecyclerView(RecyclerView recyclerView) {  recyclerView.setHasFixedSize(true); // 設定固定大小  initRecyclerLayoutManager(recyclerView); // 初始化布局  initRecyclerAdapter(recyclerView); // 初始化適配器  initItemDecoration(recyclerView); // 初始化裝飾  initItemAnimator(recyclerView); // 初始化動畫效果 }

4. LayoutManager
管理RecyclerView的布局結構.

 private void initRecyclerLayoutManager(RecyclerView recyclerView) {  // 錯列網格布局  recyclerView.setLayoutManager(new StaggeredGridLayoutManager(4,    StaggeredGridLayoutManager.VERTICAL)); }

提供了多種LayoutManager, 瀑布流使用錯列網格布局.
5. Adapter
適配器, 處理RecyclerView的Item事務.

 private void initRecyclerAdapter(RecyclerView recyclerView) {  mAdapter = new MyAdapter(getData());  recyclerView.setAdapter(mAdapter); }

對於Adapter, 我們需要展開來說, 先看看類.

public class MyAdapter extends RecyclerView.Adapter<MyViewHolder> { private List<DataModel> mDataModels; private List<Integer> mHeights; MyAdapter(List<DataModel> dataModels) {  if (dataModels == null) {   throw new IllegalArgumentException("DataModel must not be null");  }  mDataModels = dataModels;  mHeights = new ArrayList<>(); } @Override public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {  View itemView = LayoutInflater.from(parent.getContext())    .inflate(R.layout.item_recycler_view, parent, false);  return new MyViewHolder(itemView); } @Override public void onBindViewHolder(MyViewHolder holder, int position) {  DataModel dataModel = mDataModels.get(position);  // 隨機高度, 類比瀑布效果.  if (mHeights.size() <= position) {   mHeights.add((int) (100 + Math.random() * 300));  }  ViewGroup.LayoutParams lp = holder.getTvLabel().getLayoutParams();  lp.height = mHeights.get(position);  holder.getTvLabel().setLayoutParams(lp);  holder.getTvLabel().setText(dataModel.getLabel());  holder.getTvDateTime().setText(new SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH)    .format(dataModel.getDateTime())); } @Override public int getItemCount() {  return mDataModels.size(); } public void addData(int position) {  DataModel model = new DataModel();  model.setDateTime(getBeforeDay(new Date(), position));  model.setLabel("No. " + (int) (new Random().nextDouble() * 20.0f));  mDataModels.add(position, model);  notifyItemInserted(position); } public void removeData(int position) {  mDataModels.remove(position);  notifyItemRemoved(position); } /**  * 擷取日期的前一天  *  * @param date 日期  * @param i 偏離  * @return 新的日期  */ private static Date getBeforeDay(Date date, int i) {  Calendar calendar = Calendar.getInstance();  calendar.setTime(date);  calendar.add(Calendar.DAY_OF_YEAR, i * (-1));  return calendar.getTime(); }}

(1)onCreateViewHolder建立ViewHolder.

(2)onBindViewHolder綁定每一項資料.

(3)getItemCount返回列表長度.

(4)RecyclerView強制使用ViewHolder.

public class MyViewHolder extends RecyclerView.ViewHolder { private TextView mTvLabel; // 標籤 private TextView mTvDateTime; // 日期 public MyViewHolder(View itemView) {  super(itemView);  mTvLabel = (TextView) itemView.findViewById(R.id.item_text);  mTvDateTime = (TextView) itemView.findViewById(R.id.item_date); } public TextView getTvLabel() {  return mTvLabel; } public TextView getTvDateTime() {  return mTvDateTime; }}

在onCreateViewHolder方法, 建立類; 在onBindViewHolder方法, 綁定資料.
DataModel

public class DataModel { private String mLabel; private Date mDateTime; public String getLabel() {  return mLabel; } public void setLabel(String label) {  mLabel = label; } public Date getDateTime() {  return mDateTime; } public void setDateTime(Date dateTime) {  mDateTime = dateTime; }}

6. ItemDecoration
項的裝飾, 比如ListView中的分割線, 在本例中, 左右兩條粉線.

 private void initItemDecoration(RecyclerView recyclerView) {  recyclerView.addItemDecoration(new MyItemDecoration(this)); }

ItemDecoration, 注意parent和child的使用方式.

public class MyItemDecoration extends RecyclerView.ItemDecoration { private static final int[] ATTRS = new int[]{android.R.attr.listDivider}; private Drawable mDivider; public MyItemDecoration(Context context) {  final TypedArray array = context.obtainStyledAttributes(ATTRS);  mDivider = array.getDrawable(0);  array.recycle(); } @Override public void onDraw(Canvas c, RecyclerView parent, State state) {  drawHorizontal(c, parent);  drawVertical(c, parent); } // 水平線 public void drawHorizontal(Canvas c, RecyclerView parent) {  final int childCount = parent.getChildCount();  // 在每一個子控制項的底部畫線  for (int i = 0; i < childCount; i++) {   final View child = parent.getChildAt(i);   final int left = child.getLeft() + child.getPaddingLeft();   final int right = child.getWidth() + child.getLeft() - child.getPaddingRight();   final int top = child.getBottom() - mDivider.getIntrinsicHeight() - child.getPaddingBottom();   final int bottom = top + mDivider.getIntrinsicHeight();   mDivider.setBounds(left, top, right, bottom);   mDivider.draw(c);  } } // 豎直線 public void drawVertical(Canvas c, RecyclerView parent) {  final int childCount = parent.getChildCount();  // 在每一個子控制項的右側畫線  for (int i = 0; i < childCount; i++) {   final View child = parent.getChildAt(i);   int right = child.getRight() - child.getPaddingRight();   int left = right - mDivider.getIntrinsicWidth();   final int top = child.getTop() + child.getPaddingTop();   final int bottom = child.getTop() + child.getHeight() - child.getPaddingBottom();   mDivider.setBounds(left, top, right, bottom);   mDivider.draw(c);  } } // Item之間的留白 @Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state) {  outRect.set(0, 0, mDivider.getIntrinsicWidth(), mDivider.getIntrinsicHeight()); }}

本例重寫了listDivider

 <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">  ...  <item name="android:listDivider">@drawable/divider_bg</item> </style><?xml version="1.0" encoding="utf-8"?><shape xmlns:android="http://schemas.android.com/apk/res/android"  android:shape="rectangle"> <solid android:color="#ff00ff"/> <size android:height="4dp"/> <size android:width="4dp"/></shape>

7. ItemAnimator
動畫效果比較複雜, 使用預設動畫. 如要定製的話, 繼承DefaultItemAnimator; 如設定null, 則不顯示任何動畫.

 private void initItemAnimator(RecyclerView recyclerView) {  recyclerView.setItemAnimator(new DefaultItemAnimator()); // 預設動畫 }

8. 最終Activity

public class MainActivity extends AppCompatActivity { private MyAdapter mAdapter; @Override protected void onCreate(Bundle savedInstanceState) {  super.onCreate(savedInstanceState);  setContentView(R.layout.activity_main);  Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);  setSupportActionBar(toolbar);  FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);  fab.setOnClickListener(new View.OnClickListener() {   @Override   public void onClick(View view) {    Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)      .setAction("Action", null).show();   }  });  // 初始化RecyclerView  initRecyclerView((RecyclerView) findViewById(R.id.test_recycler_view)); } /**  * 初始化RecyclerView  *  * @param recyclerView 主控制項  */ private void initRecyclerView(RecyclerView recyclerView) {  recyclerView.setHasFixedSize(true); // 設定固定大小  initRecyclerLayoutManager(recyclerView); // 初始化LayoutManager  initRecyclerAdapter(recyclerView); // 初始化Adapter  initItemDecoration(recyclerView); // 初始化邊界裝飾  initItemAnimator(recyclerView); // 初始化動畫效果 } /**  * 初始化RecyclerView的LayoutManager  *  * @param recyclerView 主控制項  */ private void initRecyclerLayoutManager(RecyclerView recyclerView) {  // 錯列網格布局  recyclerView.setLayoutManager(new StaggeredGridLayoutManager(4,    StaggeredGridLayoutManager.VERTICAL)); } /**  * 初始化RecyclerView的Adapter  *  * @param recyclerView 主控制項  */ private void initRecyclerAdapter(RecyclerView recyclerView) {  mAdapter = new MyAdapter(getData());  recyclerView.setAdapter(mAdapter); } /**  * 初始化RecyclerView的(ItemDecoration)項目裝飾  *  * @param recyclerView 主控制項  */ private void initItemDecoration(RecyclerView recyclerView) {  recyclerView.addItemDecoration(new MyItemDecoration(this)); } /**  * 初始化RecyclerView的(ItemAnimator)項目動畫  *  * @param recyclerView 主控制項  */ private void initItemAnimator(RecyclerView recyclerView) {  recyclerView.setItemAnimator(new DefaultItemAnimator()); // 預設動畫 } /**  * 類比的資料  *  * @return 資料  */ private ArrayList<DataModel> getData() {  int count = 57;  ArrayList<DataModel> data = new ArrayList<>();  for (int i = 0; i < count; i++) {   DataModel model = new DataModel();   model.setDateTime(getBeforeDay(new Date(), i));   model.setLabel("No. " + i);   data.add(model);  }  return data; } /**  * 擷取日期的前一天  *  * @param date 日期  * @param i 偏離  * @return 新的日期  */ private static Date getBeforeDay(Date date, int i) {  Calendar calendar = Calendar.getInstance();  calendar.setTime(date);  calendar.add(Calendar.DAY_OF_YEAR, i * (-1));  return calendar.getTime(); } @Override public boolean onCreateOptionsMenu(Menu menu) {  // Inflate the menu; this adds items to the action bar if it is present.  getMenuInflater().inflate(R.menu.menu_main, menu);  return true; } @Override public boolean onOptionsItemSelected(MenuItem item) {  // Handle action bar item clicks here. The action bar will  // automatically handle clicks on the Home/Up button, so long  // as you specify a parent activity in AndroidManifest.xml.  int id = item.getItemId();  switch (item.getItemId()) {   case R.id.id_action_add:    mAdapter.addData(1);    break;   case R.id.id_action_delete:    mAdapter.removeData(1);    break;  }  //noinspection SimplifiableIfStatement  if (id == R.id.action_settings) {   return true;  }  return super.onOptionsItemSelected(item); }}

為了測試動畫, Menu額外添加兩個按鈕。

9.運行ReactNative樣本
既然感覺ReactNative開發靠譜, 那麼我們就來看看ReactNative都能做哪些好玩的東西, 和原生的有哪些區別?
樣本圖

按照文檔安裝一些命令列工具, 再下載Git代碼.
Github: https://github.com/facebook/react-native

內容很多, 包含一些依賴庫和樣本(Example), 下載的有點慢, 耐心等待.

下載完成後, 在react-native內, 執行npm install.

Android項目執行, 參考ReactAndroid的README.md.

在react-native目錄, 建立local.properties

sdk.dir=/Users/wangchenlong/Installations/android-sdkndk.dir=/Users/wangchenlong/Installations/android-ndk-r10e

執行

cd react-native./gradlew :ReactAndroid:assembleDebug

再執行

./gradlew :ReactAndroid:installArchives

啟動服務

./packager/packager.sh

安裝項目

cd react-native./gradlew :Examples:UIExplorer:android:app:installDebug

一定要先啟動服務, 再安裝項目.
出現transforming 100%, 即匯入成功.

在最新版本中, 我的紅米note4無法運行項目.
報錯: Upload package to device fails.
原因是編譯的gradle版本太高, 預設1.5.0, 實際1.2.0~1.3.0都可以運行.
我的是1.2.3.
真機調試, 本人紅米note(Android 4.2)
搖動手機, 選擇Dev Settings->Debug sever host & port for device. 設定IP地址, 觀察原生IP, 填入即可. 我當前的是

192.168.2.202:8081
注意設定連接埠8081, 否則無法載入. 有些情況可以直接輸入IP即可.
Android5.0以上, 直接設定連接埠即可.

adb reverse tcp:8081 tcp:8081

參考Android的真機調試文檔.

IOS模擬器, 太窮沒有iPhone. 直接開啟open UIExplorer.xcodeproj項目, 執行就可以顯示.

開發有兩種選擇, 一種是直接基於ReactNative開發, 一種是把ReactNative整合到現有的App中, 對於第二種, 我們就需要關注, ReactNative會增大多少代碼呢?

使用最基本的HelloWorld做測試, ReactNative也是產生一個簡單HelloWorld的JS. 最新產生的HelloWorld的大小是1.4M, 加上ReactNative的是7.6M, 架構大約6.2M左右, 各位可以權衡一下使用.

ReactNative的UIExplorer已經包含了大量樣本, 很接近原生, 非常絢麗, 速度也很快. 如Android的ViewPager

OK, 好的開始是成功的一半, 繼續探索吧! Enjoy it!

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.