Android應(yīng)用開發(fā)中RecyclerView組件使用入門教程
RecyclerView是一種列表容器, 發(fā)布很久了, 才想起來寫點什么.
RecyclerView相比于ListView, 在回收重用時更具有靈活性, 也就是低耦合, 并且提供了擴展. 加載多個視圖時, 應(yīng)該多用RecyclerView代替ListView.
那么我們來看看這東西應(yīng)該怎么用? 比如生成一個瀑布流的視圖.
首先我們從一個HelloWorld寫起, 看看如何構(gòu)建一個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"/>
LayoutManager: 管理RecyclerView的結(jié)構(gòu).
Adapter: 處理每個Item的顯示.
ItemDecoration: 添加每個Item的裝飾.
ItemAnimator: 負責添加\移除\重排序時的動畫效果.
LayoutManager\Adapter是必須, ItemDecoration\ItemAnimator是可選. /** * 初始化RecyclerView * * @param recyclerView 主控件 */ private void initRecyclerView(RecyclerView recyclerView) { recyclerView.setHasFixedSize(true); // 設(shè)置固定大小 initRecyclerLayoutManager(recyclerView); // 初始化布局 initRecyclerAdapter(recyclerView); // 初始化適配器 initItemDecoration(recyclerView); // 初始化裝飾 initItemAnimator(recyclerView); // 初始化動畫效果 }
4. LayoutManager
管理RecyclerView的布局結(jié)構(gòu).
private void initRecyclerLayoutManager(RecyclerView recyclerView) { // 錯列網(wǎng)格布局 recyclerView.setLayoutManager(new StaggeredGridLayoutManager(4, StaggeredGridLayoutManager.VERTICAL)); }
提供了多種LayoutManager, 瀑布流使用錯列網(wǎng)格布局.
5. Adapter
適配器, 處理RecyclerView的Item事務(wù).
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創(chuàng)建ViewHolder.
(2)onBindViewHolder綁定每一項數(shù)據(jù).
(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方法, 創(chuàng)建類; 在onBindViewHolder方法, 綁定數(shù)據(jù).
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(); // 在每一個子控件的右側(cè)畫線 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
動畫效果比較復(fù)雜, 使用默認動畫. 如要定制的話, 繼承DefaultItemAnimator; 如設(shè)置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); // 設(shè)置固定大小 initRecyclerLayoutManager(recyclerView); // 初始化LayoutManager initRecyclerAdapter(recyclerView); // 初始化Adapter initItemDecoration(recyclerView); // 初始化邊界裝飾 initItemAnimator(recyclerView); // 初始化動畫效果 } /** * 初始化RecyclerView的LayoutManager * * @param recyclerView 主控件 */ private void initRecyclerLayoutManager(RecyclerView recyclerView) { // 錯列網(wǎng)格布局 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()); // 默認動畫 } /** * 模擬的數(shù)據(jù) * * @return 數(shù)據(jù) */ 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開發(fā)靠譜, 那么我們就來看看ReactNative都能做哪些好玩的東西, 和原生的有哪些區(qū)別?
示例圖
按照文檔安裝一些命令行工具, 再下載Git代碼.
Github: https://github.com/facebook/react-native
內(nèi)容很多, 包含一些依賴庫和示例(Example), 下載的有點慢, 耐心等待.
下載完成后, 在react-native內(nèi), 執(zhí)行npm install.
Android項目執(zhí)行, 參考ReactAndroid的README.md.
在react-native目錄, 新建local.properties
sdk.dir=/Users/wangchenlong/Installations/android-sdk ndk.dir=/Users/wangchenlong/Installations/android-ndk-r10e
執(zhí)行
cd react-native ./gradlew :ReactAndroid:assembleDebug
再執(zhí)行
./gradlew :ReactAndroid:installArchives
啟動服務(wù)
./packager/packager.sh
安裝項目
cd react-native ./gradlew :Examples:UIExplorer:android:app:installDebug
一定要先啟動服務(wù), 再安裝項目.
出現(xiàn)transforming 100%, 即導(dǎo)入成功.
在最新版本中, 我的紅米note4無法運行項目.
報錯: Upload package to device fails.
原因是編譯的gradle版本太高, 默認1.5.0, 實際1.2.0~1.3.0都可以運行.
我的是1.2.3.
真機調(diào)試, 本人紅米note(Android 4.2)
搖動手機, 選擇Dev Settings->Debug sever host & port for device. 設(shè)置IP地址, 觀察本機的IP, 填入即可. 我當前的是
192.168.2.202:8081
注意設(shè)置端口8081, 否則無法加載. 有些情況可以直接輸入IP即可.
Android5.0以上, 直接設(shè)置端口即可.
adb reverse tcp:8081 tcp:8081
參考Android的真機調(diào)試文檔.
IOS模擬器, 太窮沒有iPhone. 直接打開open UIExplorer.xcodeproj項目, 執(zhí)行就可以顯示.
開發(fā)有兩種選擇, 一種是直接基于ReactNative開發(fā), 一種是把ReactNative集成到現(xiàn)有的App中, 對于第二種, 我們就需要關(guān)注, ReactNative會增大多少代碼呢?
使用最基本的HelloWorld做測試, ReactNative也是生成一個簡單HelloWorld的JS. 最新生成的HelloWorld的大小是1.4M, 加上ReactNative的是7.6M, 框架大約6.2M左右, 各位可以權(quán)衡一下使用.
ReactNative的UIExplorer已經(jīng)包含了大量示例, 很接近原生, 非常絢麗, 速度也很快. 如Android的ViewPager
OK, 好的開始是成功的一半, 繼續(xù)探索吧! Enjoy it!
相關(guān)文章
Android 網(wǎng)絡(luò)圖片查看器與網(wǎng)頁源碼查看器
本篇文章主要介紹了Android 網(wǎng)絡(luò)圖片查看器與網(wǎng)頁源碼查看器的相關(guān)知識。具有很好的參考價值。下面跟著小編一起來看下吧2017-04-04Android自定義View實現(xiàn)可以拖拽的GridView
這篇文章主要為大家詳細介紹了Android自定義View實現(xiàn)可以拖拽的GridView,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-06-06Android中編寫屬性動畫PropertyAnimation的進階實例
這篇文章主要介紹了Android中編寫屬性動畫PropertyAnimation的進階實例,包括一些縮放和淡入淡出效果的設(shè)計,強大且不算復(fù)雜,需要的朋友可以參考下2016-04-04Android軟件啟動動畫及動畫結(jié)束后跳轉(zhuǎn)的實現(xiàn)方法
這篇文章主要介紹了Android軟件啟動動畫及動畫結(jié)束后跳轉(zhuǎn)的實現(xiàn)方法,實例分析了Android圖片播放及定時器的相關(guān)使用技巧,非常具有使用價值,需要的朋友可以參考下2015-10-10Android ViewPager相冊橫向移動的實現(xiàn)方法
本篇文章小編為大家介紹,Android ViewPager相冊橫向移動的實現(xiàn)方法。需要的朋友參考下2013-04-04