編輯:關於Android編程
自學習android以來,其實一直都有接觸到 RecyclerView,今天便總結一下關於RecyclerView的相關知識,並不是非常全面。主要從以下幾個方面:
RecyclerView概述 RecyclerView與ListView區別 RecyclerView基本使用 RecyclerView item單擊與長按事件 RecyclerView item長按拖拽和側滑刪除源碼地址:https://github.com/Ti2Yuan/RecyclerViewDemo
1. RecyclerView概述
2014年Google IO的召開,Android L Preview版發布,對於開發者而言,它帶來了性能上的改善。其中,一個全新的控件也進入開發者的視野中,並得到越來越多的使用,大有取代ListView的趨勢,它就是RecyclerView。
A flexible view for providing a limited window into a large data set.
上面那句話是官網中對RecyclerView的描述:能在有限的窗口中顯示大數據集的靈活視圖。
RecyclerView是Google support-v7包下新增的控件,用來替代ListView的使用,RecyclerView標准化了ViewHolder類似於ListView中convertView用來做視圖緩存。但是它卻是ListView的增強版。
2.RecyclerView與ListView區別
正如官方文檔所言,RecyclerView是ListView的豪華增強版。它主要包含以下幾處新的特性,如ViewHolder,ItemDecorator,LayoutManager,SmoothScroller以及增加或刪除item時item動畫等。官方推薦我們采用RecyclerView來取代ListView。
ViewHolder
ViewHolder是用來保存視圖引用的類,在ListView中,ViewHolder需要自己來定義,但只是一種推薦的使用方式,不是必須要使用的。取而代之的是ListView性能的遲緩,因為ListView每次getView的時候都會調用findViewById(int)方法。而在RecyclerView中則強調必須使用RecyclerView.ViewHolder,否則代碼將不能運行。雖然這個過程實現起來稍顯復雜,但是卻避免了ListView不使用ViewHolder而帶來的性能問題。
LayoutManager
ListView只能在垂直方向上滾動,google並沒有給出ListView在水平方向上面滾動的Android API支持。但是RecyclerView相較於ListView,在滾動上可以支持多種類型列表,例如:
LinearLayoutManager,可以支持水平和豎直方向上滾動的列表。
StaggeredGridLayoutManager,可以支持交叉網格風格的列表,類似於瀑布流或者Pinterest。
GridLayoutManager,支持網格展示,可以水平或者豎直滾動,如展示圖片的畫廊。
ItemAnimator
列表動畫是一個全新的、擁有無限可能的維度。起初的Android API中,刪除或添加item時,item是無法產生動畫效果的。後面隨著Android的進化,Google的Chat Hasse推薦使用ViewPropertyAnimator屬性動畫來實現上述需求。
RecyclerView.ItemAnimator用於在RecyclerView中添加、刪除或移動item時處理動畫效果。同時也存在一個默認的動畫效果DefaultItemAnimator。而ListView則不具備這種API。
Adapter
在ListView的Adapter中,getView方法將視圖跟position綁定在一起。同時我們能通過registerDataObserver在Adapter中注冊一個觀察者。在RecyclerView中,我們也可以通過RecyclerView.AdapterDataObserver觀察。ListView有三個Adapter的默認實現,分別是ArrayAdapter、CursorAdapter和SimpleCursorAdapter。然而,RecyclerView的Adapter則擁有除了內置的內DB游標和ArrayList的支持之外的所有功能。RecyclerView.Adapter的實現中,我們必須采取措施將數據提供給Adapter。
ItemDecoration
在ListView中我們可以通過divider和dividerHeight這些相關屬性為item添加間隔符。但是在RecyclerView中可通過RecyclerView.ItemDecoration類來實現自定義間隔符。默認情況下item之間不會展示間隔符。這的確增加了開發人員的負擔,如果你想要添加間隔符的話。你還可以參考官方示例中的DividerItemDecoration.java文件。
OnItemTouchListener
ListView通過AdapterView.OnItemClickListener接口來探測點擊事件。而RecyclerView則可以通過RecyclerView.OnItemTouchListener接口來探測觸摸事件。這種探測方式雖然增加了實現的難度,但是卻給予開發人員攔截觸摸事件更多的控制權限。
還有就是ListView可以添加MultiChoiceModeListener來設置選擇模式,但是RecyclerView卻沒有。
總結:
recyclerView自定義強,可以實現復雜的布局,但過程稍顯復雜。
3.RecyclerView基本使用
首先,在moudle下的build.gradle導入包
dependencies {
compile 'com.android.support:recyclerview-v7:23.2.0'
}
然後在布局中
`
然後就是為RecyclerView設置LayoutManager,Adapter,ItemAnimator和ItemDecoration。而自定義的RecyclerViewAdapter中必須繼承RecyclerView.Adapter,並且實現需要重寫的方法,如onCreateViewHolder,onBindViewHolder,getItemCount()等等。並且可通過getItemViewType(int position)方法返回item 類型,可用於返回headItem和FootItem類型。
recyclerView =(RecyclerView)findViewById(R.id.recycleView);
mLayoutManager = new LinearLayoutManager(this);
adapter = new RecyclerViewAdapter(this, list);
recyclerView.setAdapter(adapter);
recyclerView.setLayoutManager(mLayoutManager);
recyclerView.setItemAnimator(new DefaultItemAnimator());
recyclerView.addItemDecoration(new RecyclerView.ItemDecoration() {
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDraw(c, parent, state);
}
});
@Override
public int getItemViewType(int position) {
if (position == 0) {
return TYPE_HEAD;
} else if (position + 1 == getItemCount()) {
return TYPE_FOOT;
} else {
return TYPE_ITEM;
}
}
4. RecyclerView item單擊與長按事件
一般情況下,我們可以在自定義RecyclerView.Adapter的Adapter類中為ViewHolder的rootView設置OnClickListener()來監聽item的點擊事件,但這種方式或多或少浪費性能,而存在一種更加高逼格的方式來實現諸如單擊和長按事件,就是通過探測手勢觸摸的方式。一下是使用方法:
recyclerView.addOnItemTouchListener(new OnRecyclerViewItemTouchListener(recyclerView) {
@Override
protected void onItemClick(RecyclerView.ViewHolder vh) {
}
@Override
protected void onItemLongClick(RecyclerView.ViewHolder vh) {
}
});
public abstract class OnRecyclerViewItemTouchListener implements RecyclerView.OnItemTouchListener {
private RecyclerView recyclerView;
private GestureDetectorCompat gestureDetector;
public OnRecyclerViewItemTouchListener(RecyclerView recyclerView) {
this.recyclerView = recyclerView;
gestureDetector = new GestureDetectorCompat(recyclerView.getContext(),
new ItemTouchHelperGestureListener());
}
@Override
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
gestureDetector.onTouchEvent(e);
return false;
}
@Override
public void onTouchEvent(RecyclerView rv, MotionEvent e) {
gestureDetector.onTouchEvent(e);
}
@Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
}
private class ItemTouchHelperGestureListener extends GestureDetector.SimpleOnGestureListener {
@Override
public boolean onDown(MotionEvent motionEvent) {
return false;
}
@Override
public void onShowPress(MotionEvent motionEvent) {
}
/**
* 單擊事件
* @param motionEvent
* @return
*/
@Override
public boolean onSingleTapUp(MotionEvent motionEvent) {
View child = recyclerView.findChildViewUnder(motionEvent.getX(),motionEvent.getY());
if(child != null){
RecyclerView.ViewHolder vh = recyclerView.getChildViewHolder(child);
onItemClick(vh);
}
return true;
}
@Override
public boolean onScroll(MotionEvent motionEvent, MotionEvent motionEvent1, float v, float v1) {
return false;
}
/**
* 長按事件
* @param motionEvent
*/
@Override
public void onLongPress(MotionEvent motionEvent) {
View child = recyclerView.findChildViewUnder(motionEvent.getX(),motionEvent.getY());
if(child != null){
RecyclerView.ViewHolder vh = recyclerView.getChildViewHolder(child);
onItemLongClick(vh);
}
}
@Override
public boolean onFling(MotionEvent motionEvent, MotionEvent motionEvent1, float v, float v1) {
return false;
}
}
protected abstract void onItemClick(RecyclerView.ViewHolder vh);
protected abstract void onItemLongClick(RecyclerView.ViewHolder vh);
}
RecyclerView提供了設置觸摸監聽的方法,那麼我們定義一個類OnRecyclerViewItemTouchListener實現OnItemTouchListener。重寫3個方法OnInterceptTouchEvent,onTouchEvent,onRequestDisallowInterceptTouchEvent。其中第三個方法是處理觸摸事件沖突的,前兩個方法就是View的事件分發機制裡面的事件攔截和事件處理的兩個方法,參數裡為我們提供了觸摸事件的數據MotionEvent,我們要做的就是去解析坐標點和觸摸規律來識別觸摸手勢,然後獲取觸摸的是哪一個item,sdk已經為我們實現了手勢的識別:
GestureDetectorCompat 就是處理手勢的類:手勢探測器,它比GestureDetector能更好兼容低版本的api,但使用方法是一致的,我們實例化一個手勢探測器:
gestureDetector = new GestureDetectorCompat(recyclerView.getContext(),
new ItemTouchHelperGestureListener());
實例化手勢探測器的時候需要提供一個手勢監聽器:OnGestureListener,探測器識別出手勢後就會回調手勢監聽器中對應的方法,我們就可以在回調方法中做我們想做的事情了。
sdk為我們提供了兩個手勢監聽器:OnGestureListener,OnDoubleTapListener。
OnGestureListener主要回調各種單擊事件,而OnDoubleTapListener回調各種雙擊事件。而我們需要處理的點擊事件其實就是上面的:onSingleTapUp()。而sdk 還提供了一個外部類SimpleOnGestureListener,這個類實現了上面兩個接口的所有方法,但全都是空實現,函數體裡什麼也沒寫,其中就是把上面兩個接口合並一下,給出默認的空實現,這樣繼承SimpleOnGestureListener的時候就不用實現每一個方法了。RecyclerView已經為我們提供了findChildViewUnder(),我們可以通過這個方法獲得點擊的item,同時我們調用RecyclerView的另一個方法getChildViewHolder(),可以獲得該item的ViewHolder,最後再回調我們定義的虛方法onItemClick()就ok了,這樣我們就可以在外部實現該方法來獲得item的點擊事件了。
5. RecyclerView item長按拖拽和側滑刪除
可以通過ItemTouchHelper這個類來實現RecyclerView item的拖拽和滑動。
This is a utility class to add swipe to dismiss and drag & drop support to RecyclerView.
這是Google官方文檔描述這個類的話:這是一個支持RecyclerView滑動刪除和拖拽的實用工具類。
具體用法如下:
itemTouchHelper = new ItemTouchHelper(new ItemTouchHelpCallback(this,list) {
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
int position = viewHolder.getLayoutPosition();
if(position != 0 && position != recyclerView.getAdapter().getItemCount() - 1) {
adapter.notifyItemRemoved(position);
list.remove(position-1);
}
}
});
itemTouchHelper.attachToRecyclerView(recyclerView);
public abstract class ItemTouchHelpCallback extends ItemTouchHelper.Callback {
private List list;
private Context context;
public ItemTouchHelpCallback(Context context, List list) {
this.list = list;
this.context = context;
}
@Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
final int dragFlags, swipeFlags;
if (recyclerView.getLayoutManager() instanceof GridLayoutManager) {
dragFlags = ItemTouchHelper.UP |
ItemTouchHelper.DOWN |
ItemTouchHelper.LEFT |
ItemTouchHelper.RIGHT;
swipeFlags = 0;
} else {
dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END;
}
return makeMovementFlags(dragFlags, swipeFlags);
}
//上下移動item
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
//得到拖動viewHolder的position
int fromPosition = viewHolder.getAdapterPosition();
//得到目標viewHolder的position
int toPosition = target.getAdapterPosition();
if (fromPosition != 0 && fromPosition != recyclerView.getAdapter().getItemCount() - 1 &&
toPosition != 0 && toPosition != recyclerView.getAdapter().getItemCount() - 1)
{
if (fromPosition < toPosition) {
for (int i = fromPosition - 1; i < toPosition - 1; ++i) {
Collections.swap(list, i, i + 1); //改變實際的數據集
}
} else {
for (int i = fromPosition - 1; i > toPosition - 1; i--) {
Collections.swap(list, i, i - 1); //改變實際的數據集
}
}
}
recyclerView.getAdapter().notifyItemMoved(fromPosition, toPosition);
return true;
}
//當長按選中item的時候(拖拽開始的時候)調用
@Override
public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) {
viewHolder.itemView.setBackgroundColor(context.getResources().
getColor(R.color.colorPrimary));
}
Vibrator vib = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
vib.vibrate(70); //震動70毫秒
super.onSelectedChanged(viewHolder, actionState);
}
//當手指松開的時候(拖拽完成的時候)調用
@Override
public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
super.clearView(recyclerView, viewHolder);
viewHolder.itemView.setBackgroundColor(Color.WHITE);
}
@Override
public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {
//滑動時改變Item的透明度
final float alpha = 1 - Math.abs(dX) / (float) viewHolder.itemView.getWidth();
viewHolder.itemView.setAlpha(alpha);
viewHolder.itemView.setTranslationX(dX);
}
}
}
要使用ItemTouchHelper,你需要創建一個ItemTouchHelper.Callback。這個接口可以讓你監聽“move”與 “swipe”事件。自定義類繼承它時需要實現一些方法:getMovementFlags,onMove,onSwiped。另外還有一些方法如onSelectedChanged,clearView可以控制這個item被選中和被釋放的狀態,還有onChildDraw方法可以重寫item滑動的默認動畫。
ItemTouchHelper可以讓你輕易得到一個事件的方向。重寫getMovementFlags()方法來指定可以支持的拖放和滑動的方向。使用helperItemTouchHelper.makeMovementFlags(int, int)來構造返回的flag。
@Override
public boolean isLongPressDragEnabled() {
return true;
}
ItemTouchHelper可以用於沒有滑動的拖動操作(或者反過來),你必須指明你到底要支持哪一種。要支持長按RecyclerView item進入拖動操作,你必須在isLongPressDragEnabled()方法中返回true。或者,也可以調用ItemTouchHelper.startDrag(RecyclerView.ViewHolder) 方法來開始一個拖動。這會在後面講到。
@Override
public boolean isItemViewSwipeEnabled() {
return true;
}
而要在view任意位置觸摸事件發生時啟用滑動操作,則直接在sItemViewSwipeEnabled()中返回true就可以了。或者,你也主動調用ItemTouchHelper.startSwipe(RecyclerView.ViewHolder) 來開始滑動操作。
源碼地址:https://github.com/Ti2Yuan/RecyclerViewDemo
文件下載在App應用中也用到很多,一般版本更新時多要用的文件下載來進行處理,以前也有看過很多大神有過該方面的博客,今天我也自己來實踐一下,寫的一般,還請大家多提意見,共同
前言android 自定義控件之ViewGroup生命周期執行步驟。了解ViewGroup的生命周期的執行步驟對於自己自定義ViewGroup的時候十分重要,清楚了整個流
通過本文帶大家一起看過UIL這個國內外大牛都追捧的圖片緩存類庫的緩存處理機制。看了UIL中的緩存實現,才發現其實這個東西不難,沒有太多的進程調度,沒有各種內存讀取控制機制
Launching ???? has encountered a problem. Cannot connecto to VMSocket operatio