目錄:
-
一 RecyclerView簡介
-
二 RecyclerView為什么會出現(xiàn)
-
三 基本使用方法
-
四 高級用法
Scrollbars
Item間分割線
Item顯示方式
自定義LayoutManager
RecyclerView的點擊事件
高級效果 波紋點擊
添加 刪除時動畫
與某些View結(jié)合使用
滑動監(jiān)聽addOnScrollListener 可實現(xiàn)下拉刷新等
ItemTouchHelper實現(xiàn)item刪除蠢涝,互相拉動
-
五 RecyclerView多種布局顯示
-
六 注意(未全)
-
七 開源庫XRecyclerView
一 RecyclerView簡介
官方對RecycleView的描述是在有限的窗口中大量顯示數(shù)據(jù)集
的靈活view,類似ListView,GridViewRecyclerView在support.V7包里钓试,個人覺得Google是為了用它來替代ListView
可以實現(xiàn)
1.ListView的功能(包括橫向)
2.GridView的功能
3.橫向ScrollView
4.瀑布流
5.方便添加animation(item的add remove時)
二 RecyclerView為什么會出現(xiàn)
優(yōu)點
高度解耦汹族,隨意定制瑞凑。它提供的LayoutManager嗦随,ItemDecoration,ItemAnimator可以讓開發(fā)者自定義奇特的效果
RecyclerView封裝了ViewHolder
通過設(shè)置LayoutManager可以設(shè)計Item的顯示的方式伸但,橫肾请,豎甚至瀑布流
通過ItemDecoration可以控制Item間的間隔條,還有自定義間隔條
通過ItemAnimator可以控制Item增刪的動畫
缺點
- 短按長按點擊事件需要自己來實現(xiàn)(WTF)
雖然有缺點更胖,但是幾乎忽略不計铛铁,RecyclerView的隨意定制,其自帶的ViewHolder不再像ListView中ViewHolder作為View出現(xiàn)函喉,其“Recycle”的強大避归,一行代碼切換布局樣式的優(yōu)點使其勢必取代ListView
三 基本使用方法
當(dāng)前IDE:Android Studio 2.2正式版
jdk1.8.102
compileSdkVersion 25
buildToolsVersion "25.0.2"
gradle導(dǎo)包
compile 'com.android.support:recyclerview-v7:25.2.0'
然后layout布局文件中加入
<android.support.v7.widget.RecyclerView
android:id="@+id/activity_main_recycle_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
code中需要
//設(shè)置布局管理器,必須
mRecyclerView.setLayoutManager(layout);
//設(shè)置RecycleView的Adapter管呵,必須
mRecyclerView.setAdapter(adapter)
//設(shè)置分割線梳毙,非必須
mRecyclerView.addItemDecoration(ItemDecoration);
//設(shè)置item的增刪動畫,非必須
mRecyclerView.setItemAnimator(animator);
現(xiàn)在先最簡單創(chuàng)建一個列表
新建Item的布局recycleview_item捐下,以TextView為根節(jié)點
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/recycle_textview"
android:layout_width="match_parent"
android:layout_height="50dp"
android:gravity="center"
android:textSize="20sp"/>
新建一個RecyclerView.Adapter適配器TestRecycleViewAdapter
public class TestRecycleViewAdapter extends RecyclerView.Adapter<TestRecycleViewAdapter.ViewHolderA> {
private Context mContext;
private List<String> mList;
public TestRecycleViewAdapter(Context context, List<String> list) {
mContext = context;
mList = list;
}
@Override
public ViewHolderA onCreateViewHolder(ViewGroup parent, int viewType) {
//此處動態(tài)加載ViewHolder的布局文件并返回holder
View view = LayoutInflater.from(mContext).inflate(R.layout.recycleview_item, parent, false);
ViewHolderA holderA = new ViewHolderA(view);
return holderA;
}
@Override
public void onBindViewHolder(ViewHolderA holder, int position) {
//此處設(shè)置Item中view的數(shù)據(jù)
holder.mTextView.setText(mList.get(position));
}
@Override
public int getItemCount() {
//生成的item的數(shù)量
return mList.size();
}
//Item的ViewHolder以及item內(nèi)部布局控件的id綁定
class ViewHolderA extends RecyclerView.ViewHolder{
TextView mTextView;
public ViewHolderA(View itemView) {
super(itemView);
mTextView = (TextView) itemView.findViewById(R.id.recycle_textview);
}
}
}
用Android Studio生成Adapter結(jié)構(gòu)技巧
- 先新建一個類TestRecycleViewAdapter
- 繼承自RecyclerView.Adapter<這里填ViewHolder名字的>账锹,現(xiàn)在出現(xiàn)紅線,先不管它
- 然后在TestRecycleViewAdapter 內(nèi)部寫內(nèi)部類ViewHolderA 繼承自 RecyclerView.ViewHolder坷襟,這個時候會自動生成構(gòu)造函數(shù)
- 接著將ViewHolder的名字ViewHolderA填入第2步粗體處奸柬,注意這里是內(nèi)部類所以正確填入是TestRecycleViewAdapter.ViewHolderA
- 接著點紅線,然后IDE會自己生成所需的3個方法
Activity中代碼是這樣
public class MainActivity extends AppCompatActivity {
private List<String> list;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
RecyclerView mRecyclerView = (RecyclerView) findViewById(R.id.activity_main_recycle_view);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
initData();
//實例化并傳輸數(shù)據(jù)給adapter
TestRecycleViewAdapter adapter = new TestRecycleViewAdapter(getApplicationContext(), list);
mRecyclerView.setAdapter(adapter);
}
/**
* 添加數(shù)據(jù)
* */
private void initData() {
list = new ArrayList<>();
for (int i = 0; i < 50; i++) {
list.add("item" + i);
}
}
}
這樣子一個最基本的RecyclerView就寫好了婴程,效果圖
可以看到默認(rèn)沒有Scrollbars廓奕,沒有item之間的間隔線,沒有item點擊效果档叔,實際上這些我們需要自定義(雖然有點麻煩)
四 高級用法
1 Scrollbars
- 只需要在RecycleView根節(jié)點加上
android:scrollbars="vertical"
默認(rèn)為none桌粉,還有橫向horizontal樣式的
2 Item間分割線
- 可以給item設(shè)置margin,不過感覺好丑
只要在item的layout里加上
android:layout_marginTop="3dp"http://只需要這一行
android:background="#cf1616"http://顯示成紅色比較明顯
- 另外就是高大上的ItemDecoration了衙四,直接在code中加上
mRecyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));
這里的context要注意用Activity的Context铃肯,假如用了Application的Context會顯示默認(rèn)的樣式,謝謝@且聽風(fēng)吟9527的提醒context使用場景圖
數(shù)字1:啟動Activity在這些類中是可以的传蹈,但是需要創(chuàng)建一個新的task押逼,一般情況不推薦;
數(shù)字2:在這些類中去layout inflate是合法的惦界,但是會使用系統(tǒng)默認(rèn)的主題樣式挑格,如果你自定義了某些樣式可能不會被使用;
數(shù)字3:在Receiver為null時允許沾歪,在4.2或以上的版本中恕齐,用于獲取黏性廣播的當(dāng)前值。(可以無視);
ContentProvider显歧、BroadcastReceiver之所以在上述表格中,是因為在其內(nèi)部方法中都有一個context用于使用确镊。
但是我們不能僅限于此士骤,
默認(rèn)實現(xiàn)類DividerItemDecoration類中源代碼部分如下
public class DividerItemDecoration extends RecyclerView.ItemDecoration {
public static final int HORIZONTAL = LinearLayout.HORIZONTAL;
public static final int VERTICAL = LinearLayout.VERTICAL;
//關(guān)鍵在這里,看到有個屬性定義成了listDivider
private static final int[] ATTRS = new int[]{ android.R.attr.listDivider };
.......
public DividerItemDecoration(Context context, int orientation) {
//這里調(diào)用了屬性值listDivider
final TypedArray a = context.obtainStyledAttributes(ATTRS);
mDivider = a.getDrawable(0);
a.recycle();
setOrientation(orientation);
}
........
所以蕾域,自定義間隔線步驟如下:
在Style文件里加上name="android:listDivider"的item
<item name="android:listDivider">@drawable/divider_backgroud</item>
書寫divider_bg.xml文件拷肌,shape文件為漸變xml
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:centerColor="#b3b35f"
android:endColor="#515187"
android:startColor="#b34747"
android:type="linear" />
<size android:height="5dp"/>
</shape>
這樣一個自定義間隔線樣式就好了
網(wǎng)絡(luò)與瀑布流的間隔線個人水平暫時弄不好.......(擱置中、旨巷、巨缘、、QAQ)
3 Item顯示方式
GridLayout
只需要將
mRecyclerView.setLayoutManager(layoutManager);
layoutManager設(shè)為
mRecyclerView.setLayoutManager(new GridLayoutManager(this, 5));
就可以實現(xiàn)5列的網(wǎng)格布局
我們看到網(wǎng)格樣式看起來像流式的List采呐,那是因為沒有左右間隔線的問題(其實是水平不行)若锁,
左右間隔線弄不出來QAQ,現(xiàn)在只能在recycleview_item.xml里面增加屬性做一個假的間隔線QAQ
android:layout_marginStart="1dp"
瀑布流
同上
mRecyclerView.setLayoutManager(new StaggeredGridLayoutManager(3,StaggeredGridLayoutManager.VERTICAL));
感覺上和網(wǎng)格布局沒啥差別斧吐,因為這上面的item我們使用了固定的高度又固,我們可以在適配器的onBindViewHolder方法中隨機設(shè)置item高度
- 先在adapter的里聲明
private List<Integer> mHeight;
- 在構(gòu)造方法里隨機生成高度值
mHeight = new ArrayList<Integer>();
for (int i = 0; i < mList.size(); i++) {
mHeight.add((int) (80 + Math.random() * 300));
}
- 接著在onBindViewHolder方法里
ViewGroup.LayoutParams lp = holder.mTextView.getLayoutParams();
lp.height = mHeight.get(position);
類似的,不止有豎屏的效果煤率,橫屏的只要修改
mRecyclerView.setLayoutManager(new StaggeredGridLayoutManager(3, StaggeredGridLayoutManager.HORIZONTAL));
就可以快速實現(xiàn)類似的效果
4 自定義LayoutManager
請參考
RecyclerView自定義LayoutManager,打造不規(guī)則布局
另外
對LayoutManager的分析,創(chuàng)建一個 RecyclerView LayoutManager
5 RecycleView的點擊事件
RecycleView并沒有提供類似setOnItemClickListener()這樣的注冊監(jiān)聽器方法,而是需要我們自己給子項view注冊響應(yīng)事件
我們只需要在Adapter的onBindViewHolder方法里添加
public void onBindViewHolder(ViewHolderA holder, final int position) {
.....
.....
holder.mTextView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(mContext, "item"+position,Toast.LENGTH_SHORT).show();
}
});
.....
}
這樣就輕松實現(xiàn)了TextView的點擊事件劫哼,同理划乖,長按的類似如此
RecycleView的強大之處也在這里,就算是ViewHolder里的View多層嵌套了幾個view都可以輕松的實現(xiàn)點擊事件
6 高級效果 波紋點擊
實現(xiàn)方法昼捍,在item布局文件根節(jié)點加上
android:foreground="?android:attr/selectableItemBackground"
7 添加 刪除時動畫
在adapter里新添加方法
public void addData(int position){
mList.add(position, "新增" + position);
//通知適配器item內(nèi)容插入
notifyItemInserted(position);
}
public void RemoveData(int position){
mList.remove(position);
//通知適配器item內(nèi)容刪除
notifyItemRemoved(position);
}
然后在Activity里添加action欄菜單
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()){
case R.id.menu_add:
adapter.addData(1);
break;
case R.id.menu_remove:
adapter.RemoveData(1);
break;
}
return true;
}
menu文件
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/menu_remove"
android:icon="@android:drawable/alert_light_frame"
android:title="remove"
app:showAsAction="ifRoom"/>
<item
android:id="@+id/menu_add"
android:icon="@android:drawable/ic_input_add"
android:orderInCategory="100"
android:title="add"
app:showAsAction="ifRoom"/>
</menu>
然后识虚,這個是默認(rèn)的動畫,現(xiàn)在有很多開源庫端三,具體可以Google
mRecyclerView.setItemAnimator(new DefaultItemAnimator());
8 與某些View結(jié)合使用
- 可以被SwipeRefreshLayout實現(xiàn)Android原生下拉刷新舷礼,也可以實現(xiàn)第三方下拉刷新
- item可以使用CardView,恩郊闯,超漂亮
9 滑動監(jiān)聽addOnScrollListener 可實現(xiàn)下拉刷新等
實現(xiàn)這個借口的倆個方法
- onScrollStateChanged
- onScrolled
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
//標(biāo)記當(dāng)前是否向最后一項滑動
boolean isSlidingToLast = false;
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
//獲取當(dāng)前的LayoutManager
LinearLayoutManager manager = (LinearLayoutManager) recyclerView.getLayoutManager();
//當(dāng)不滾動的時候
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
//得到最后一個完全顯示的item的position
int lastViusalItem = manager.findLastCompletelyVisibleItemPosition();
//總的ITemPosition
int totalItemCount = manager.getItemCount();
//當(dāng)前是否向下滑妻献,是否滾動到LastItem
if (lastViusalItem == (totalItemCount - 1) && isSlidingToLast) {
// 這里就是下拉加載更多功能的邏輯
//往item的List里添加數(shù)據(jù),并通知適配器更新
adapter.addListData(lastViusalItem);
}
}
}
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
//如果dy>0則當(dāng)前向下滑state
if (dy > 0) {
isSlidingToLast = true;
} else if (dy < 0) {
isSlidingToLast = false;
}
}
});
adapter里添加
public void addListData(int position){
for (int i = 0; i <6; i++) {
mList.add("底部添加" + i);
}
//通知適配器item內(nèi)容刪除
notifyItemChanged(position);
}
這里的LayoutManager對象manager還可以獲取到很多參數(shù)团赁,比如
可以用tm來實現(xiàn)更多邏輯功能
10 ItemTouchHelper實現(xiàn)item刪除育拨,互相拉動
一個大神寫的
Android實現(xiàn)RecyclerView側(cè)滑刪除和長按拖拽-ItemTouchHelper
,講的很清楚欢摄,這里不再詳解熬丧,貼上測試的代碼,僅做記錄
在setAdapter后面添加
ItemTouchHelper.Callback mCallback = new ItemTouchHelper.SimpleCallback(ItemTouchHelper.DOWN|ItemTouchHelper.UP, ItemTouchHelper.RIGHT) {
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
int fromPosition = viewHolder.getAdapterPosition();//得到拖動ViewHolder的position
int toPosition = target.getAdapterPosition();//得到目標(biāo)ViewHolder的position
if (fromPosition < toPosition){
// 向下
for (int i = fromPosition; i < toPosition; i++) {
Collections.swap(list, i, i+1);
}
}else {
for (int i = fromPosition; i < toPosition; i++) {
// 向上
Collections.swap(list, i, i-1);
}
}
mTestRecycleViewAdapterMulType.notifyItemMoved(fromPosition, toPosition);
//返回true表示執(zhí)行拖動
return true;
}
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
int position = viewHolder.getAdapterPosition();
list.remove(position);
mTestRecycleViewAdapterMulType.notifyItemRemoved(position);
}
};
ItemTouchHelper mItemTouchHelper = new ItemTouchHelper(mCallback);
mItemTouchHelper.attachToRecyclerView(mRecyclerView);
效果
五 RecycleView多種布局顯示
先看效果
RecycleView.Adapter也提供了getItemViewType方法怀挠,此方法和ListView加載多布局一樣析蝴。
這里需要注意的是這里的范型不再是自己寫的ViewHolder
public class TestRecycleViewAdapterMulType extends RecyclerView.Adapter<RecyclerView.ViewHolder>
重寫adapter的getItemViewType
public int getItemViewType(int position) {
if (position == 0) {
//111代表頂部item
return 111;
} else if (position == getItemCount() - 1) {
//111代表di部item
return 112;
}
//111代表中間item
return 110;
}
自定義3個ViewHolder害捕,分別表示top ,bottom 和中間的item布局
/**
* 代表頂部item
*/
class ViewHolderA extends RecyclerView.ViewHolder {
ImageView mImageViewA;
public ViewHolderA(View itemView) {
super(itemView);
mImageViewA = (ImageView) itemView.findViewById(R.id.top_imageview);
}
}
/**
* 代表di部item
*/
class ViewHolderB extends RecyclerView.ViewHolder {
ImageView mImageViewB;
public ViewHolderB(View itemView) {
super(itemView);
mImageViewB = (ImageView) itemView.findViewById(R.id.bottom_imageview);
}
}
/**
* 代表中間item
*/
class ViewHolderC extends RecyclerView.ViewHolder {
TextView mTextView;
public ViewHolderC(View itemView) {
super(itemView);
mTextView = (TextView) itemView.findViewById(R.id.recycle_textview);
}
}
在onCreateViewHolder方法里
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = null;
RecyclerView.ViewHolder holder = null;
//此處動態(tài)加載ViewHolder的布局文件并返回holder
switch (viewType) {
case 111:
//111代表頂部item
view = LayoutInflater.from(mContext).inflate(R.layout.top, parent, false);
holder = new ViewHolderA(view);
break;
case 112:
//112代表頂部item
view = LayoutInflater.from(mContext).inflate(R.layout.bottom, parent, false);
holder = new ViewHolderB(view);
break;
case 110:
//112代表中間item
view = LayoutInflater.from(mContext).inflate(R.layout.recycleview_item, parent, false);
holder = new ViewHolderC(view);
break;
}
return holder;
}
最后是onBindViewHolder闷畸,在這里分別設(shè)置不同布局item數(shù)據(jù)
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {
// //這樣寫也行
// if(holder instanceof ViewHolderA){
// ViewHolderA holderA = (ViewHolderA) holder;
// holderA.mImageViewA.setImageResource(R.mipmap.ic_launcher);
// }else if(holder instanceof ViewHolderB).....
// .....
switch (getItemViewType(position)) {
case 111:
ViewHolderA holderA = (ViewHolderA) holder;
holderA.mImageViewA.setImageResource(R.mipmap.ic_launcher);
break;
case 112:
ViewHolderB holderB = (ViewHolderB) holder;
holderB.mImageViewB.setImageResource(R.mipmap.ic_launcher);
break;
case 110:
ViewHolderC holderC = (ViewHolderC) holder;
holderC.mTextView.setText(mList.get(position));
break;
}
}
貼一下完整Adapter代碼
**
* Created by NIWA on 2017/3/9.
*/
public class TestRecycleViewAdapterMulType extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private Context mContext;
private List<String> mList;
public TestRecycleViewAdapterMulType(Context context, List<String> list) {
mContext = context;
mList = list;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = null;
RecyclerView.ViewHolder holder = null;
//此處動態(tài)加載ViewHolder的布局文件并返回holder
switch (viewType) {
case 111:
//111代表頂部item
view = LayoutInflater.from(mContext).inflate(R.layout.top, parent, false);
holder = new ViewHolderA(view);
break;
case 112:
//112代表頂部item
view = LayoutInflater.from(mContext).inflate(R.layout.bottom, parent, false);
holder = new ViewHolderB(view);
break;
case 110:
//112代表中間item
view = LayoutInflater.from(mContext).inflate(R.layout.recycleview_item, parent, false);
holder = new ViewHolderC(view);
break;
}
return holder;
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {
// //這樣寫也行
// if(holder instanceof ViewHolderA){
// ViewHolderA holderA = (ViewHolderA) holder;
// holderA.mImageViewA.setImageResource(R.mipmap.ic_launcher);
// }else if(holder instanceof ViewHolderB).....
// .....
switch (getItemViewType(position)) {
case 111:
ViewHolderA holderA = (ViewHolderA) holder;
holderA.mImageViewA.setImageResource(R.mipmap.ic_launcher);
break;
case 112:
ViewHolderB holderB = (ViewHolderB) holder;
holderB.mImageViewB.setImageResource(R.mipmap.ic_launcher);
break;
case 110:
ViewHolderC holderC = (ViewHolderC) holder;
holderC.mTextView.setText(mList.get(position));
break;
}
}
@Override
public int getItemCount() {
//生成的item的數(shù)量
return mList.size();
}
/**
* 代表頂部item
*/
class ViewHolderA extends RecyclerView.ViewHolder {
ImageView mImageViewA;
public ViewHolderA(View itemView) {
super(itemView);
mImageViewA = (ImageView) itemView.findViewById(R.id.top_imageview);
}
}
/**
* 代表di部item
*/
class ViewHolderB extends RecyclerView.ViewHolder {
ImageView mImageViewB;
public ViewHolderB(View itemView) {
super(itemView);
mImageViewB = (ImageView) itemView.findViewById(R.id.bottom_imageview);
}
}
/**
* 代表中間item
*/
class ViewHolderC extends RecyclerView.ViewHolder {
TextView mTextView;
public ViewHolderC(View itemView) {
super(itemView);
mTextView = (TextView) itemView.findViewById(R.id.recycle_textview);
}
}
@Override
public int getItemViewType(int position) {
if (position == 0) {
//111代表頂部item
return 111;
} else if (position == getItemCount() - 1) {
//因為item是從0開始尝盼,所以最后一項應(yīng)該是getItemCount() - 1
//111代表di部item
return 112;
}
//111代表中間item
return 110;
}
}
最后在Activity里
mTestRecycleViewAdapterMulType = new TestRecycleViewAdapterMulType(getApplicationContext(), list);
mRecyclerView.setAdapter(mTestRecycleViewAdapterMulType);
heiheihei
六 注意
- 刷新數(shù)據(jù)的幾個方法
notifyItemInserted();
notifyItemRangeInserted();
notifyItemChanged();
notifyItemRangeChanged();
notifyItemRemoved();
notifyItemRangeRemoved();
七 開源庫
1. XRecyclerView
據(jù)我所知,支付寶用了這個庫佑菩,比較輕量級別
- 用法和原生RecycleView一樣
- 封裝好了下拉刷新盾沫,上拉加載,刷新動畫
- 增加頭部殿漠,甚至增加倆個headerView
2.BaseRecyclerViewAdapterHelper
用了之后你才發(fā)現(xiàn)RecyclerView的Adapter也可以寫的這么優(yōu)雅赴精,功能很全,ListView時代的感覺绞幌。
當(dāng)然一切開源庫蕾哟,不要過度依賴開源庫,一切都要建立在踏實的基礎(chǔ)上啊奄,以上多加練習(xí)渐苏。
The End