作者:李旺成
時(shí)間:2016年4月18日
接上篇 AndroidStudyDemo之Android5.x新控件介紹(二)
這是 Android 5.x 中的新控件介紹的最后一篇,在這一篇中主要介紹 RecyclerView屎债,該系列文章以講解新特性和簡(jiǎn)單使用為主突诬。
簡(jiǎn)介
RecyclerView 是 android-support-v7-21版本中新增的一個(gè) Widget念链,看看一句話介紹:
- 能夠在有限的窗口中展示大數(shù)據(jù)集合的靈活視圖
- ListView 的升級(jí)版本,更加先進(jìn)和靈活
- 通過(guò)方便的視圖復(fù)用輕松實(shí)現(xiàn)自定義高效的視圖集
- 它就是一個(gè)容器,可以有效的重用和滾動(dòng)
上面從不同角度指出了 RecyclerView 的一些特點(diǎn),我覺(jué)得可以簡(jiǎn)單的從其命名上來(lái)理解:
Recycler:反復(fù)循環(huán)器放刨,再循環(huán)器。
對(duì)尸饺,就是循環(huán)进统,這個(gè)概念有沒(méi)有很熟悉的感覺(jué)±颂回想下 ListView螟碎、GridView等,這些 AdapterView馋辈,它們就是利用視圖回收技術(shù)來(lái)展示大量數(shù)據(jù)的控件抚芦;RecyclerView 與它們有什么區(qū)別,或者說(shuō)其優(yōu)勢(shì)在哪里迈螟?
先看看官方介紹:
繼承自 ViewGroup叉抡,這個(gè)情理之中意料之外,為什么這么說(shuō)答毫?
情理之中褥民,是指 RecyclerView 它是個(gè)容器,那么肯定會(huì)直接或間接繼承自 ViewGroup洗搂。
意料之外消返,上面提到的 ListView 都是繼承自 AdapterView,而 RecyclerView 又何其有類似的功能 —— 視圖復(fù)用耘拇。但是他竟然沒(méi)有繼承自 AdapterView撵颊,從這一點(diǎn)來(lái)看,RecyclerView 的改動(dòng)應(yīng)該不小惫叛,完全不按以前那一套來(lái)了倡勇。
好了,閑話不多說(shuō)嘉涌,看看 RecyclerView 的相關(guān)類:
這里直接列了出來(lái)妻熊,不是說(shuō)它們都很常用,不要緊張仑最,常用到的沒(méi)這么多扔役。
大致說(shuō)一下 RecyclerView 的實(shí)現(xiàn)思路:
上圖只是為了說(shuō)明一個(gè)問(wèn)題:RecyclerView 只負(fù)責(zé) Receiver,其他的功能都交給其他專門(mén)的類去處理警医,比如:Adapter 專門(mén)負(fù)責(zé)數(shù)據(jù)綁定(PS:話說(shuō)亿胸,ListView 也有Adapter,哈哈预皇,舉個(gè)例子而已)损敷。有沒(méi)有一點(diǎn)單一職責(zé)原則的感覺(jué)。
可以看出 RecyclerView 的設(shè)計(jì)更利于解耦深啤,更靈活拗馒,可操作性更高。
下面來(lái)看看怎么用溯街。
簡(jiǎn)單使用
先看看效果:
一個(gè)簡(jiǎn)單的 Demo诱桂,演示了 RecyclerView 的簡(jiǎn)單使用,下面看看所涉及到的幾個(gè)類呈昔。
幾個(gè)關(guān)鍵類
Adapter
這個(gè)與 ListView 的 Adapter 功能類似挥等,可以托管數(shù)據(jù)集合,為每一項(xiàng)Item創(chuàng)建視圖并且綁定數(shù)據(jù)堤尾。
先看官方文檔:
繼承自 Object肝劲,與 AdapterView 所使用的 BaseAdapter 沒(méi)有多少關(guān)系。但是不用急,看看它提供的方法辞槐,和 BaseAdapter 提供了不少類似的掷漱。
關(guān)于具體用法,在下面介紹吧榄檬!
ViewHolder
Google 建議 在 ListView 的 Adapter 中使用 ViewHolder卜范,但是沒(méi)有嚴(yán)格的限制,使用與否(還有用對(duì)與否)都沒(méi)有什么標(biāo)準(zhǔn)鹿榜,完全看開(kāi)發(fā)者的意愿海雪。
ViewHolder ,主要用來(lái)將數(shù)據(jù)和布局 item 進(jìn)行綁定舱殿。所有對(duì)視圖的操作都需要通過(guò) ViewHolder 來(lái)進(jìn)行奥裸,這是強(qiáng)制性的。
看下官方的介紹:
LayoutManager
LayoutManager沪袭,這個(gè)是新概念湾宙,在 ListView 中從沒(méi)聽(tīng)說(shuō)過(guò);其作用就是負(fù)責(zé)Item 視圖的布局的顯示管理枝恋。
先看下官方介紹:
它有三個(gè)實(shí)現(xiàn)類创倔,看下它們的繼承結(jié)構(gòu):
- LinearLayoutManager:水平或者垂直的 Item 視圖
- GridLayoutManager:網(wǎng)格 Item 視圖
- StaggeredGridLayoutManager:交錯(cuò)的網(wǎng)格 Item 視圖(瀑布流,明白了吧)
這幾個(gè)類負(fù)責(zé) RecyclerView 中 Item 的排布焚碌,具體使用在下面介紹畦攘。
ItemDecoration
ItemDecoration 用于設(shè)置條目的分割線,在 ListView 中通過(guò) android:divider 屬性來(lái)為條目設(shè)置分割線十电。RecyclerView 可不管這些了知押,都交給“專人”(專門(mén)的類)負(fù)責(zé)。這樣靈活了鹃骂,效果也更豐富台盯,也稍微麻煩了(呵呵,哪有兩全其美的設(shè)計(jì))畏线。
看下官方介紹:
上面使用紅色框圈出來(lái)的 onDrawXxx() 方法静盅,可想而知,你想要什么樣式/效果寝殴,自己畫(huà)去蒿叠。
ItemAnimator
還記得給 ListView 的 Item 添加動(dòng)畫(huà)嗎?全部都得自己弄蚣常,現(xiàn)在好了市咽,RecyclerView Item 的添加/刪除動(dòng)畫(huà)都交給 ItemAnimator。
ItemAnimator 就是負(fù)責(zé)處理數(shù)據(jù)添加或者刪除時(shí)候的動(dòng)畫(huà)效果的抵蚊。
看下官方介紹:
提供了很多方法施绎,這里就不全部貼出來(lái)了溯革,幸好 ItemAnimator 提供了默認(rèn)實(shí)現(xiàn),如果你不打算自定義的話谷醉,那用默認(rèn)的即可致稀。
下面介紹具體使用。
使用示例
1孤紧、導(dǎo)入兼容包
RecyclerView 是兼容包中提供的豺裆,要使用拒秘,首先需要導(dǎo)入兼容包:
compile 'com.android.support:recyclerview-v7:23.2.0'
注意号显,不同版本可能稍有差別,例如:條目根布局設(shè)置為 much_parent躺酒,早期的版本和目前的版本是有區(qū)別的押蚤。
2、在 Layout 中使用
RecyclerView 在布局中的使用與 ListView 類似羹应,就把它當(dāng)作 ListView 即可揽碘。看代碼:
<android.support.v7.widget.RecyclerView
android:id="@+id/rv_content"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
這里只是簡(jiǎn)單使用园匹,所以沒(méi)有去嘗試使用更多的屬性了雳刺。
3、設(shè)置 LayoutManager
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
layoutManager.setOrientation(mOrientation); // 可以設(shè)置LinearLayoutManager.VERTICAL/LinearLayoutManager.HORIZONTAL
// 設(shè)置布局管理器
mContentRV.setLayoutManager(layoutManager);
橫向布局與縱向布局在示例 Demo 中可以通過(guò)開(kāi)關(guān)來(lái)切換裸违,自己 Down 下 Demo 去試試吧掖桦!
4、設(shè)置 ItemAnimator
這里直接用默認(rèn)的來(lái)演示了:
mContentRV.setItemAnimator(new DefaultItemAnimator());
5供汛、自定義 Adapter
這個(gè)與 ListView 的 Adapter 稍有不同枪汪,具體看注釋吧:
public class RecyclerAdapter1 extends RecyclerView.Adapter<RecyclerAdapter1.ViewHolder> {
private List<String> mDataList;
private OnRVItemClickListener mListener;
public RecyclerAdapter1(List<String> dataList) {
mDataList = dataList;
}
// 設(shè)置 Item 點(diǎn)擊監(jiān)聽(tīng)
public void setOnItemClickListener(OnRVItemClickListener listener) {
mListener = listener;
}
// 自定義的 ViewHolder,持有每個(gè) Item 的所有 View 控件
// 必須繼承自 RecyclerView.ViewHolder
public static class ViewHolder extends RecyclerView.ViewHolder {
public TextView mTextView;
public ViewHolder(View itemView) {
super(itemView);
mTextView = (TextView) itemView;
}
}
// 獲取Item的數(shù)量
@Override
public int getItemCount() {
return mDataList.size();
}
// 將數(shù)據(jù)與 View 控件進(jìn)行綁定
@Override
public void onBindViewHolder(ViewHolder holder, final int position) {
holder.mTextView.setText(mDataList.get(position));
if (mListener != null) {
holder.mTextView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mListener.onItemClick(v, position);
}
});
}
}
// 創(chuàng)建新 View怔昨,被 LayoutManager 所調(diào)用
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = View.inflate(parent.getContext(),
android.R.layout.simple_list_item_1, null);
ViewHolder holder = new ViewHolder(view);
return holder;
}
}
6雀久、設(shè)置分割線
首先,定義出分割線趁舀,直接看代碼赖捌,都有注釋:
public class DIYDecoration extends RecyclerView.ItemDecoration {
// 采用系統(tǒng)內(nèi)置的風(fēng)格的分割線
private static final int[] attrs = new int[]{android.R.attr.listDivider};
private Drawable mDivider;
private int orientation;
public DIYDecoration(Context context, int orientation) {
TypedArray typedArray = context.obtainStyledAttributes(attrs);
mDivider = typedArray.getDrawable(0);
typedArray.recycle();
this.orientation = orientation;
}
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
drawHDeraction(c, parent);
drawVDeraction(c, parent);
}
/**
* 繪制水平方向的分割線
*/
private void drawHDeraction(Canvas c, RecyclerView parent) {
int left = parent.getPaddingLeft();
int right = parent.getWidth() - parent.getPaddingRight();
int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
View child = parent.getChildAt(i);
RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) child.getLayoutParams();
int top = child.getBottom() + layoutParams.bottomMargin;
int bottom = top + mDivider.getIntrinsicHeight();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
/**
* 繪制垂直方向的分割線
*/
private void drawVDeraction(Canvas c, RecyclerView parent) {
int top = parent.getPaddingTop();
int bottom = parent.getHeight() - parent.getPaddingBottom();
int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
View child = parent.getChildAt(i);
RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) child.getLayoutParams();
int left = child.getRight() + layoutParams.rightMargin;
int right = left + mDivider.getIntrinsicWidth();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
if (OrientationHelper.HORIZONTAL == orientation) {
outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
} else {
outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
}
}
}
(參考自:RecyclerView完全解析,讓你從此愛(ài)上它(二十八))
然后,為 RecyclerView 設(shè)置分割線:
mDIYDecoration = new DIYDecoration(RecyclerViewActivity.this, OrientationHelper.HORIZONTAL);
mContentRV.addItemDecoration(mDIYDecoration);
7矮烹、更新數(shù)據(jù)
ListView 中使用 Adapter 更新數(shù)據(jù)提供了 notifyDataSetChanged()越庇,而 RecyclerView 的 Adapter 提供了更多,更細(xì)粒度的更新數(shù)據(jù)的方法:
基本上都可以見(jiàn)名知義擂送,直接看看用法吧:
mDataList.add(0, "DIY-ITEM:NEW");
if (mAdapter1 != null) {
mAdapter1.notifyItemInserted(0);
}
mDataList.remove(0);
if (mAdapter1 != null) {
mAdapter1.notifyItemRemoved(0);
}
在 RecyclerView 中要做局部刷新那就很簡(jiǎn)單了悦荒,RecyclerView 還提供了一些很實(shí)用的方法(LayoutManager 中提供的):
- findFirstVisibleItemPosition() :返回當(dāng)前第一個(gè)可見(jiàn)Item的position
- findFirstCompletelyVisibleItemPosition() :返回當(dāng)前第一個(gè)完全可見(jiàn)Item的position
- findLastVisibleItemPosition() :返回當(dāng)前最后一個(gè)可見(jiàn)Item的position
- findLastCompletelyVisibleItemPosition() :返回當(dāng)前最后一個(gè)完全可見(jiàn)Item的position
這些東西就不再這里詳述了,方法名已經(jīng)給出了它的作用嘹吨,有興趣可以自己去試試搬味。
進(jìn)階
所謂進(jìn)階,就是在 RecyclerView 的基本使用上進(jìn)行擴(kuò)展,主要有如下幾個(gè)方面:
- 下拉刷新上拉加載更多
- 萬(wàn)能Adapter
- 多View Type
- AddHeader和AddFooter
- Drag
- 動(dòng)畫(huà)
- 嵌套 ViewPager/Gridview
這里只是說(shuō)一下有這么幾個(gè)擴(kuò)展的方向碰纬,不在這里展開(kāi)介紹了萍聊,計(jì)劃以后專門(mén)出幾篇來(lái)介紹。
小結(jié)
關(guān)于 Android 5.x 的新控件的介紹就到這里了悦析,都只是簡(jiǎn)單的介紹了下使用方法寿桨。
對(duì)于其他的控件這樣介紹過(guò)之后使用應(yīng)該基本沒(méi)問(wèn)題了,但是對(duì)于 RecyclerView 的使用强戴,這里只是個(gè)引子亭螟。關(guān)于 RecyclerView 的進(jìn)階內(nèi)容打算用幾篇(暫定三篇)來(lái)專門(mén)介紹,這里就不做展開(kāi)了骑歹。
下一篇就是 Android 6.x 的相關(guān)介紹了预烙,未完待續(xù)...
附錄
項(xiàng)目地址
Android學(xué)習(xí)之RecyclerView
使用StaggeredGridLayoutManager實(shí)現(xiàn)瀑布流效果
關(guān)于RecyclerView的卡頓問(wèn)題,對(duì)谷歌非常失望
RecyclerView使用介紹
RecyclerView技術(shù)棧
RecyclerView完全解析,讓你從此愛(ài)上它(二十八)