天天看點

Android -- RecyclerView

"A flexible view for providing a limited window into a large data set."

可以說是ListView的更新版,ListVie中我們需要自己寫ViewHolder,當然你也可以不寫,是在RecylerView中,是要讓寫的喲~RecyclerView适用于無法在一個螢幕範圍内展現格式一樣的資料時,需要用多行或多列來展示。例如展示聯系人,圖檔,視訊等。使用者需要滑動螢幕來檢視資料,這時RecyclerView的特性就有用武之地了。比如,當使用者滑動使目前一個可視的Item滑出螢幕,這個Item的視圖将會被回收并在一個新Item進入可視範圍後重新被使用。可回收利用View是個很實用的功能,它不僅可以減少CPU不斷inflate View的開銷,而且可以節省緩存View的記憶體開銷。

RecylerView還有一大特色,就是動畫!

RecyclerView不再負責顯示工作

和ListView不一樣的是,RecyclerView不再負責Item的擺放等顯示方面的功能。所有和布局、繪制等方面的工作都其拆分成不同的類進行管理。是以開發者可以自定義各種各樣滿足定制需求的的功能類。

RecyclerView.Adapter 托管資料集合,為每個Item建立視圖
RecyclerView.ViewHolder 承載Item視圖的子視圖
RecyclerView.LayoutManager 負責Item視圖的布局
RecyclerView.ItemDecoration 為每個Item視圖添加子視圖,在Demo中被用來繪制Divider
RecyclerView.ItemAnimator 負責添加、删除資料時的動畫效果

ViewHolder

關于ViewHolder,Google早就推薦開發者使用,但也隻是建議。但是現在,RecyclerView.Adapter最終要求開發者必須使用ViewHolder。

public class MyViewHolder extends ViewHolder{  
  
    public ImageView iv;  
    public TextView tv;  
      
    public MyViewHolder(View rootView) {  
        super(rootView);  
        iv = (ImageView)rootView.findViewById(R.id.item_iv);  
        tv = (TextView)rootView.findViewById(R.id.item_tv);  
    }  

}      

Adapter負責扮演兩個角色:不僅為底部資料提供支援而且還負責為資料建立合适的視圖。Adapter适用在Android很多控件,例如ListView、AutoCompleteTextView等。

public class MyAdapter extends Adapter<MyViewHolder> {  
  
    private List<Item> mData;  
      
    public MyAdapter(List<Item> data){  
        this.mData = data;  
    }  
      
    @Override  
    public int getItemCount() {  
        return mData.size();  
    }  
  
      
    @Override  
    public void onBindViewHolder(MyViewHolder holder, int position) {  
        Item bean = mData.get(position);  
        holder.tv.setText(bean.tv);  
    }  
  
    @Override  
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {  
        View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent,false);  
        MyViewHolder vh = new MyViewHolder(itemView);  
        return vh;  
    }      
}      

onCreateViewHolder中負責為Item建立視圖,onBindViewHolder負責将資料綁定到Item的視圖上。

LayoutManager是RecyclerView中最有意思的類。該類負責将每個Item視圖在RecylerView中的布局。目前Google提供了LayoutManager的一個子類:LinearLayoutManager。LinearLayoutManager提供了橫向和豎向兩種布局。

MyLayoutManager manager = new MyLayoutManager(this);  
manager.setOrientation(LinearLayout.HORIZONTAL);//預設是LinearLayout.VERTICAL  
mRecyclerView.setLayoutManager(manager);      

LinearLayoutManager提供了如下幾個方法來幫助開發者擷取螢幕上的頂部item和底部item:

  • findFirstVisibleItemPosition()
  • findFirstCompletelyVisibleItemPosition()
  • findLastVisibleItemPosition()
  • findLastCompletelyVisibleItemPosition()

    通過ItemDecoration可以使各個Item在視覺上互相分開,其實和ListView的Divider很像。ItemDecoration并不是RecyclerView必須設定的,開發者可以不設定或者設定多個Decoration。RecyclerView會周遊所有的ItemDecoration并調用各自的繪圖方法。
    public class MyDecoration extends ItemDecoration {  
      
        private static final int[] ATTRS = new int[]{  
            android.R.attr.listDivider  
        };  
          
        private Drawable mDivider;  
          
        public MyDecoration(Context ctx){  
            final TypedArray a = ctx.obtainStyledAttributes(ATTRS);  
            mDivider = a.getDrawable(0);  
        }  
          
        @Override  
        public void onDraw(Canvas c, RecyclerView parent, State state) {  
            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,  State state) {  
            outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);  
        }  
          
    }      

    RecyclerView.ItemAnimatior

    • 删除某一個Item
    • 添加一個新的Item
    • 移動某個Item

    Google提供了一個名為DefaultItemAnimator的預設ItemAnimator供開發者使用。如果開發者不為RecyclerView設定ItemAnimator,RecyclerView也會使用預設的DefaultItemAnimator。

    顯然,為了讓動畫效果起效,開發者必須通知Adapter資料有改變。之前我們使用Adapter時會調用notifyDataSetChanged()來通知Adapter資料改變并更新視圖,現在RecyclerView,Adapter提供了許多notifyXyz()方法。

    流程

    • 執行個體化RecyclerView
    • 為RecyclerView設定LayoutManager
    • 為RecyclerView設定Adapater
    • 如果有需求,可以設定一個或多個ItemDecorations,當然,也可以不設定
    • 如果有需求,可以設定ItemAnimator

    監聽事件

    RecyclerView不再負責Item視圖的布局及顯示,是以RecyclerView也沒有為Item開放OnItemClick等點選事件,這就需要開發者自己實作。

    因為ViewHolder我們可以拿到每個Item的根布局,是以如果我們為根布局設定單獨的OnClick監聽并将其開放給Adapter,那不就可以在組裝RecyclerView時就能夠設定ItemClickListener,隻不過這個Listener不是設定到RecyclerView上而是設定到Adapter。

    public class MyViewHolder extends ViewHolder implements OnClickListener,OnLongClickListener{  
      
        public ImageView iv;  
        public TextView tv;  
        private MyItemClickListener mListener;  
        private MyItemLongClickListener mLongClickListener;  
          
        public MyViewHolder(View rootView,MyItemClickListener listener,MyItemLongClickListener longClickListener) {  
            super(rootView);  
            iv = (ImageView)rootView.findViewById(R.id.item_iv);  
            tv = (TextView)rootView.findViewById(R.id.item_tv);  
            this.mListener = listener;  
            this.mLongClickListener = longClickListener;  
            rootView.setOnClickListener(this);  
            rootView.setOnLongClickListener(this);  
        }  
      
        /** 
         * 點選監聽 
         */  
        @Override  
        public void onClick(View v) {  
            if(mListener != null){  
                mListener.onItemClick(v,getPosition());  
            }  
        }  
      
        /** 
         * 長按監聽 
         */  
        @Override  
        public boolean onLongClick(View arg0) {  
            if(mLongClickListener != null){  
                mLongClickListener.onItemLongClick(arg0, getPosition());  
            }  
            return true;  
        }  
      
    }      

    item長寬

    public class MyLayoutManager extends LinearLayoutManager {  
      
        public MyLayoutManager(Context context) {  
            super(context);  
        }  
          
      
        @Override  
        public void onMeasure(Recycler recycler, State state, int widthSpec,int heightSpec) {  
            View view = recycler.getViewForPosition(0);  
            if(view != null){  
                measureChild(view, widthSpec, heightSpec);  
                int measuredWidth = MeasureSpec.getSize(widthSpec);  
                int measuredHeight = view.getMeasuredHeight();  
                setMeasuredDimension(measuredWidth, measuredHeight);  
            }  
        }  
    }      

    我是天王蓋地虎的分割線

    參考:http://www.grokkingandroid.com/first-glance-androids-recyclerview/

作者:我愛物聯網

出處:http://yydcdut.cnblogs.com/

本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接,否則保留追究法律責任的權利。