Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> RecyclerView學習筆記

RecyclerView學習筆記

編輯:關於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

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved