Android應(yīng)用開發(fā)中RecyclerView組件使用入門教程
RecyclerView是一種列表容器, 發(fā)布很久了, 才想起來寫點什么.
RecyclerView相比于ListView, 在回收重用時更具有靈活性, 也就是低耦合, 并且提供了擴(kuò)展. 加載多個視圖時, 應(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: 負(fù)責(zé)添加\移除\重排序時的動畫效果.
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);
// 隨機(jī)高度, 模擬瀑布效果.
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強(qiáng)制使用ViewHolder.
public class MyViewHolder extends RecyclerView.ViewHolder {
private TextView mTvLabel; // 標(biāo)簽
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ù)雜, 使用默認(rèn)動畫. 如要定制的話, 繼承DefaultItemAnimator; 如設(shè)置null, 則不顯示任何動畫.
private void initItemAnimator(RecyclerView recyclerView) {
recyclerView.setItemAnimator(new DefaultItemAnimator()); // 默認(rèn)動畫
}
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()); // 默認(rèn)動畫
}
/**
* 模擬的數(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版本太高, 默認(rèn)1.5.0, 實際1.2.0~1.3.0都可以運行.
我的是1.2.3.
真機(jī)調(diào)試, 本人紅米note(Android 4.2)
搖動手機(jī), 選擇Dev Settings->Debug sever host & port for device. 設(shè)置IP地址, 觀察本機(jī)的IP, 填入即可. 我當(dāng)前的是
192.168.2.202:8081
注意設(shè)置端口8081, 否則無法加載. 有些情況可以直接輸入IP即可.
Android5.0以上, 直接設(shè)置端口即可.
adb reverse tcp:8081 tcp:8081
參考Android的真機(jī)調(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)文章
基于Flutter實現(xiàn)短信驗證碼監(jiān)控與轉(zhuǎn)發(fā)
這篇文章主要為大家詳細(xì)介紹了如何基于Flutter實現(xiàn)短信驗證碼監(jiān)控與轉(zhuǎn)發(fā)功能,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2025-03-03
Android實現(xiàn)ListView分頁加載數(shù)據(jù)
這篇文章主要為大家詳細(xì)介紹了Android實現(xiàn)ListView分頁加載數(shù)據(jù),文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-11-11
Android使用ViewDragHelper實現(xiàn)QQ6.X最新版本側(cè)滑界面效果實例代碼
這篇文章主要介紹了Android程序開發(fā)實現(xiàn)QQ6.X最新版本側(cè)滑界面效果實例代碼的相關(guān)資料,需要的朋友可以參考下2016-02-02
Android動態(tài)替換Application實現(xiàn)
這篇文章主要介紹了Android動態(tài)替換Application實現(xiàn),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-05-05
提升Android應(yīng)用視覺吸引效果的10個UI設(shè)計技巧
在Android應(yīng)用開發(fā)中,風(fēng)格和設(shè)計或許不是最關(guān)鍵的要素,但它們在決定Android應(yīng)用成功與否上確實扮演重要的角色,以下是10個Android應(yīng)用的UI設(shè)計技巧,還有個附加技巧,感興趣的朋友可以了解下哦2013-01-01
android wifi信號強(qiáng)度等級區(qū)分的修改介紹
calculateSignalLevel為計算信號等級函數(shù),MAX_RSSI和MIN_RSSI分別為最強(qiáng)和最弱信號強(qiáng)度等級的信號強(qiáng)度閥值2013-06-06
Android中的Notification機(jī)制深入理解
這篇文章主要給大家介紹了關(guān)于Android中Notification機(jī)制的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2019-02-02
android仿知乎標(biāo)題欄隨ScrollView滾動變色
這篇文章主要為大家詳細(xì)介紹了android仿知乎標(biāo)題欄隨ScrollView滾動變色,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-06-06

