深入解析RecyclerView的使用

feqi8790 7年前發布 | 8K 次閱讀 Android開發 移動開發 RecyclerView

從Android 5.0開始,谷歌公司推出了RecylerView控件,當看到RecylerView這個新控件的時候,大部分人會首先發出一個疑問,recylerview是什么?為什么會有recylerview也就是說recylerview的優點是什么?recylerview怎么用?等等,下面我們將深入解析recylerview。

1.RecyclerView是什么?

RecyclerView是support-v7包中的新組件,是一個強大的滑動組件,與經典的ListView相比,同樣擁有item回收復用的功能,這一點從它的名字Recyclerview即回收view也可以看出。看到這也許有人會問,不是已經有ListView了嗎,為什么還要RecyclerView呢?這就牽扯到第二個問題了。

2.RecyclerView的優點是什么?

根據官方的介紹RecyclerView是ListView的升級版,既然如此那RecyclerView必然有它的優點,現就RecylerView相對于ListView的優點羅列如下:

① RecyclerView封裝了viewholder的回收復用,也就是說RecyclerView標準化了ViewHolder,編寫Adapter面向的是ViewHolder而不再是View了,復用的邏輯被封裝了,寫起來更加簡單。

② 提供了一種插拔式的體驗,高度的解耦,異常的靈活,針對一個Item的顯示RecyclerView專門抽取出了相應的類,來控制Item的顯示,使其的擴展性非常強。例如:你想控制橫向或者縱向滑動列表效果可以通過LinearLayoutManager這個類來進行控制(與GridView效果對應的是GridLayoutManager,與瀑布流對應的還StaggeredGridLayoutManager等),也就是說RecyclerView不再拘泥于ListView的線性展示方式,它也可以實現GridView的效果等多種效果。你想控制Item的分隔線,可以通過繼承RecyclerView的ItemDecoration這個類,然后針對自己的業務需求去抒寫代碼。

③ 可以控制Item增刪的動畫,可以通過ItemAnimator這個類進行控制,當然針對增刪的動畫,RecyclerView有其自己默認的實現。

recyclerView = (RecyclerView) findViewById(R.id.recyclerView);  
LinearLayoutManager layoutManager = new LinearLayoutManager(this );  
//設置布局管理器  
recyclerView.setLayoutManager(layoutManager);  
//設置為垂直布局,這也是默認的  
layoutManager.setOrientation(OrientationHelper. VERTICAL);  
//設置Adapter  
recyclerView.setAdapter(recycleAdapter);  
 //設置分隔線  
recyclerView.addItemDecoration( new DividerGridItemDecoration(this ));  
//設置增加或刪除條目的動畫  
recyclerView.setItemAnimator( new DefaultItemAnimator());

可以看到對RecylerView的設置過程,比ListView要復雜一些,雖然代碼抒寫上有點復雜,但它的擴展性是極高的。在了解了RecyclerView的一些控制之后,緊接著來看看它的Adapter的寫法,RecyclerView的Adapter與ListView的Adapter還是有點區別的,RecyclerView.Adapter,需要實現3個方法:

a) onCreateViewHolder()這個方法主要生成為每個Item inflater出一個View,但是該方法返回的是一個ViewHolder。該方法把View直接封裝在ViewHolder中,然后我們面向的是ViewHolder這個實例,當然這個ViewHolder需要我們自己去編寫。直接省去了當初的convertView.setTag(holder)和convertView.getTag()這些繁瑣的步驟。

b) onBindViewHolder()這個方法主要用于適配渲染數據到View中。方法提供給你了一viewHolder而不是原來的convertView。

c) getItemCount()這個方法就類似于BaseAdapter的getCount方法了,即總共有多少個條目。接下來通過幾個小的實例幫助大家更深入的了解RecyclerView的用法。

例子1:用RecyclerView實現一個圖片滾動的列表代碼如下:

public class MainActivity extends ActionBarActivity {

    private RecyclerView mRecyclerView;
    private List<Integer> mDatas;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initData();
        // 得到控件
        mRecyclerView = (RecyclerView) findViewById(R.id.recyclerview);
        // 設置布局管理器
        LinearLayoutManager layoutManager = new LinearLayoutManager(this);
        layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
        mRecyclerView.setLayoutManager(layoutManager);
        // 設置適配器
        mRecyclerView.setAdapter(new MyRecyclerAdapter(this, mDatas));

    }

    private void initData() {
        mDatas = new ArrayList<Integer>(Arrays.asList(R.drawable.kenan1,
                R.drawable.kenan2, R.drawable.kenan3, R.drawable.kenan4,
                R.drawable.kenan5, R.drawable.kenan6, R.drawable.kenan7,
                R.drawable.kenan8));
    }
}
public class DividerItemDecoration extends ItemDecoration {

    public DividerItemDecoration() {
        super();
        // TODO Auto-generated constructor stub
    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
            State state) {
        // TODO Auto-generated method stub
        super.getItemOffsets(outRect, view, parent, state);
    }

    @Override
    @Deprecated
    public void onDraw(Canvas c, RecyclerView parent) {
        // TODO Auto-generated method stub
        super.onDraw(c, parent);
    }

}
public class MyRecyclerAdapter extends Adapter<MyRecyclerAdapter.MyHolder> {

    private Context mContext;
    private List<Integer> mDatas;

    public MyRecyclerAdapter(Context context, List<Integer> datas) {
        super();
        this.mContext = context;
        this.mDatas = datas;
    }

    @Override
    public int getItemCount() {
        // TODO Auto-generated method stub
        return mDatas.size();
    }

    @Override
    // 填充onCreateViewHolder方法返回的holder中的控件
    public void onBindViewHolder(MyHolder holder, int position) {
        // TODO Auto-generated method stub
        holder.imageView.setImageResource(mDatas.get(position));
    }

    @Override
    // 重寫onCreateViewHolder方法,返回一個自定義的ViewHolder
    public MyHolder onCreateViewHolder(ViewGroup arg0, int arg1) {
        // 填充布局
        View view = LayoutInflater.from(mContext).inflate(R.layout.item, null);
        MyHolder holder = new MyHolder(view);
        return holder;
    }

    // 定義內部類繼承ViewHolder
    class MyHolder extends ViewHolder {

        private ImageView imageView;

        public MyHolder(View view) {
            super(view);
            imageView = (ImageView) view.findViewById(R.id.iv_item);
        }

    }


}

效果如下:

3.為RecyclerView添加OnItemClickListener回調

效果很不錯,這就是RecyclerView的基本用法了,但細心的你會發現,竟然沒有提供setOnItemClickListener這個回調,也就是無法響應點擊事件,然而在日常開發中,響應點擊事件無疑都是必須的,雖然它沒有提供,但是我們可以手動添加OnItemClickListener,我們可以在Adapter中添加這個回調接口:

例子2:可以點擊的RecyclerView在原工程基礎上對Adapter進行修改,添加OnItemClickListener接口,由于具體點擊后的邏輯是交給MainActivity去確定的,所以我們定義抽象的OnItemClickListener接口,里面有一個抽象方法,用于設置被點擊后的邏輯:

//item的回調接口
    public interface OnItemClickListener{
        void onItemClick(View view,int Position);
    }

對外暴露一個設置點擊監聽器的方法,其中傳入需要OnItemClickListener接口

//定義一個設置點擊監聽器的方法
    public void setOnItemClickListener(OnItemClickListener itemClickListener) {
        this.mItemClickListener = itemClickListener;
    }

在綁定ViewHolder的邏輯之中,對RecyclerView的每一個itemView設置點擊事件:

@Override
    // 填充onCreateViewHolder方法返回的holder中的控件
    public void onBindViewHolder(final MyHolder holder, final int position) {
        // TODO Auto-generated method stub
        holder.imageView.setImageResource(mDatas.get(position));
        //如果設置了回調,則設置點擊事件  
        if(mItemClickListener != null){
            holder.itemView.setOnClickListener(new OnClickListener() {

                @Override
                public void onClick(View v) {
                    mItemClickListener.onItemClick(holder.itemView, position);

                }
            });
        }
    }

最后粘上Adapter的全部代碼,其他代碼均和例子1一樣

public class MyRecyclerAdapter extends Adapter<MyRecyclerAdapter.MyHolder> {

    private Context mContext;
    private List<Integer> mDatas;
    private OnItemClickListener mItemClickListener;

    public MyRecyclerAdapter(Context context, List<Integer> datas) {
        super();
        this.mContext = context;
        this.mDatas = datas;
    }
    //item的回調接口
    public interface OnItemClickListener{
        void onItemClick(View view,int Position);
    }
    //定義一個設置點擊監聽器的方法
    public void setOnItemClickListener(OnItemClickListener itemClickListener) {
        this.mItemClickListener = itemClickListener;
    }
    @Override
    public int getItemCount() {
        // TODO Auto-generated method stub
        return mDatas.size();
    }

    @Override
    // 填充onCreateViewHolder方法返回的holder中的控件
    public void onBindViewHolder(final MyHolder holder, final int position) {
        // TODO Auto-generated method stub
        holder.imageView.setImageResource(mDatas.get(position));
        //如果設置了回調,則設置點擊事件  
        if(mItemClickListener != null){
            holder.itemView.setOnClickListener(new OnClickListener() {

                @Override
                public void onClick(View v) {
                    mItemClickListener.onItemClick(holder.itemView, position);

                }
            });
        }
    }

    @Override
    // 重寫onCreateViewHolder方法,返回一個自定義的ViewHolder
    public MyHolder onCreateViewHolder(ViewGroup viewgroup, int i) {
        // 填充布局
        View view = LayoutInflater.from(mContext).inflate(R.layout.item, null);
        MyHolder holder = new MyHolder(view);
        return holder;
    }

    // 定義內部類繼承ViewHolder
    class MyHolder extends ViewHolder {

        private ImageView imageView;

        public MyHolder(View view) {
            super(view);
            imageView = (ImageView) view.findViewById(R.id.iv_item);
        }

    }


}

效果如下:

4.自定義RecyclerView實現滾動時內容聯動

例子3:RecyclerView制作相冊效果效果:在原工程的基礎上進行修改,改成相冊效果,即上面顯示一張大圖,下面的RecyclerView做為圖片切換的指示器。

首先修改下布局:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.example.recyclerviewdemo.MainActivity" >

    <ImageView
        android:id="@+id/iv_group"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_margin="10dp"
        android:layout_weight="1"
        android:scaleType="centerCrop"
        android:src="@drawable/ic_launcher" />

    <!--
    <FrameLayout
        android:layout_width="fill_parent"
        android:layout_height="0dp"
        android:layout_weight="1" >

        <ImageView
            android:id="@+id/iv_group"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:layout_gravity="center"
            android:layout_margin="10dp"
            android:scaleType="centerCrop"
            android:src="@drawable/ic_launcher" />
    </FrameLayout>
    -->

    <com.example.recyclerviewdemo.MyRecyclerView
        android:id="@+id/recyclerview"
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:layout_centerVertical="true"
        android:layout_gravity="bottom" />

</LinearLayout>

添加一個顯示大圖的區域,把RecyclerView改為自己定義的。然后看我們自定義RecyclerView的代碼:

public class MyRecyclerView extends RecyclerView {

    private onItemScrollChangeListener mItemScrollChangeListener;
    private View mCurrentView;

    public MyRecyclerView(Context context, AttributeSet attrs) {
        super(context, attrs);
        // TODO Auto-generated constructor stub
    }

    // 回調的接口
    public interface onItemScrollChangeListener {
        void onChange(View view, int position);
    }

    // 對外暴露設置滾動接口的方法
    public void setOnItemScrollChangeListener(
            onItemScrollChangeListener itemScrollChangeListener) {
        this.mItemScrollChangeListener = itemScrollChangeListener;
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        // TODO Auto-generated method stub
        super.onLayout(changed, l, t, r, b);
        mCurrentView = getChildAt(0);
        if (mItemScrollChangeListener != null) {
            mItemScrollChangeListener.onChange(mCurrentView,
                    getChildPosition(mCurrentView));
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent e) {
        // TODO Auto-generated method stub

        if (e.getAction() == MotionEvent.ACTION_MOVE) {
            mCurrentView = getChildAt(0);
            if (mItemScrollChangeListener != null) {
                mItemScrollChangeListener.onChange(mCurrentView,
                        getChildPosition(mCurrentView));
            }
        }
        return super.onTouchEvent(e);
    }
}

最主要是重寫onLayout,onTouchEvent方法,并設置滾動監聽的回調,還有向外界暴露監聽器的邏輯。

然后是Adapter的代碼

public class MyRecyclerAdapter extends Adapter<MyRecyclerAdapter.MyHolder> {

    private Context mContext;
    private List<Integer> mDatas;
    //private OnItemClickListener mItemClickListener;

    public MyRecyclerAdapter(Context context, List<Integer> datas) {
        super();
        this.mContext = context;
        this.mDatas = datas;
    }
    /*//item的回調接口
    public interface OnItemClickListener{
        void onItemClick(View view,int Position);
    }
    //定義一個設置點擊監聽器的方法
    public void setOnItemClickListener(OnItemClickListener itemClickListener) {
        this.mItemClickListener = itemClickListener;
    }*/
    @Override
    public int getItemCount() {
        // TODO Auto-generated method stub
        return mDatas.size();
    }

    @Override
    // 填充onCreateViewHolder方法返回的holder中的控件
    public void onBindViewHolder(final MyHolder holder, final int position) {
        // TODO Auto-generated method stub
        holder.imageView.setImageResource(mDatas.get(position));
        /*//如果設置了回調,則設置點擊事件  
        if(mItemClickListener != null){
            holder.itemView.setOnClickListener(new OnClickListener() {

                @Override
                public void onClick(View v) {
                    mItemClickListener.onItemClick(holder.itemView, position);

                }
            });
        }*/
    }

    @Override
    // 重寫onCreateViewHolder方法,返回一個自定義的ViewHolder
    public MyHolder onCreateViewHolder(ViewGroup viewgroup, int i) {
        // 填充布局
        View view = LayoutInflater.from(mContext).inflate(R.layout.item, null);
        MyHolder holder = new MyHolder(view);
        return holder;
    }

    // 定義內部類繼承ViewHolder
    class MyHolder extends ViewHolder {

        private ImageView imageView;

        public MyHolder(View view) {
            super(view);
            imageView = (ImageView) view.findViewById(R.id.iv_item);
        }

    }


}

定義了一個滾動時回調的接口,然后在onTouchEvent中,監聽ACTION_MOVE,用戶手指滑動時,不斷把當前第一個View回調回去

關于為什么getChildAt(0)和getChildPosition()可用,起初我以為有getFirstVisibleItem這個方法,后來發現么有;但是發現了getRecycledViewPool()看名字我覺得是Viewholder那個緩存隊列,我想那么直接取這個隊列的第一個不就是我要的View么,后來沒有成功。我就觀察它內部的View,最后發現,第一個顯示的始終是它第一個child,至于getChildPosition這個看方法就看出來了。

效果如下:

5.RecyclerView實現瀑布流

例子4:用RecyclerView打造瀑布流效果其中大部分內容實現和基本的RecyclerView使用是一樣的,就不多敘述了,就一個地方不同,就是我們在適配器中綁定ViewHolder的方法中需要重新給我們的itemView布局設置height,這里是生成隨機數來設置高度的。

//得到隨機item的高度
    private void getRandomHeight(List<Integer> datas) {
        heights = new ArrayList<Integer>();
        for (int i = 0; i < datas.size(); i++) {
            heights.add((int) (200+Math.random()*100));
        }
    }

在onBindViewHolder方法中:

@Override
    // 填充onCreateViewHolder方法返回的holder中的控件
    public void onBindViewHolder(final MyHolder holder, int position) {
        // TODO Auto-generated method stub  
        //得到item的LayoutParams布局參數
        ViewGroup.LayoutParams params= holder.itemView.getLayoutParams();
        //把隨機的高度賦予item布局
        params.height = heights.get(position);
        //把params設置給item布局
        holder.itemView.setLayoutParams(params);
        //為控件綁定數據
        holder.imageView.setImageResource(mDatas.get(position));
        //如果設置了監聽那么它就不為空,然后回調相應的方法
        if(onItemClickListener!=null){
            holder.itemView.setOnClickListener(new OnClickListener() {

                @Override
                public void onClick(View v) {
                    //得到當前點擊item的位置pos
                    int position = holder.getLayoutPosition();
                    //把事件交給我們實現的接口那里處理
                    onItemClickListener.onOnItemClick(holder.itemView, position);
                }
            });
            holder.itemView.setOnLongClickListener(new OnLongClickListener() {

                @Override
                public boolean onLongClick(View v) {
                    //得到當前點擊item的位置pos
                    int position = holder.getLayoutPosition();
                    //把事件交給我們實現的接口那里處理
                    onItemClickListener.onOnItemClick(holder.itemView, position);
                    return true;
                }
            });
        }
    }

最后附上項目完整代碼:

public class MainActivity extends ActionBarActivity {

    private RecyclerView mRecyclerView;
    private List<Integer> mDatas;
    private MyRecyclerAdapter adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initData();
        // 得到控件
        mRecyclerView = (RecyclerView) findViewById(R.id.recyclerview);
        mRecyclerView.setItemAnimator(new DefaultItemAnimator());
        //設置RecyclerView布局管理器為2列垂直排布
        StaggeredGridLayoutManager layoutManager = new StaggeredGridLayoutManager(3, StaggeredGridLayoutManager.VERTICAL);
        mRecyclerView.setLayoutManager(layoutManager);

        adapter = new MyRecyclerAdapter(this, mDatas);
        mRecyclerView.setAdapter(adapter);
        adapter.setOnItemClickListener(new onItemClickListener() {

            @Override
            public void onOnItemClick(View view, int position) {
                // TODO Auto-generated method stub
                Toast.makeText(MainActivity.this, "點擊了:"+position, Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onLongClick(View view, int position) {
                 //長按刪除
                mDatas.remove(position);
                adapter.notifyItemRemoved(position);
            }
        });

    }

    private void initData() {
        mDatas = new ArrayList<Integer>(Arrays.asList(R.drawable.kenan1,
                R.drawable.kenan2, R.drawable.kenan3, R.drawable.kenan4,
                R.drawable.kenan5, R.drawable.kenan6, R.drawable.kenan7,
                R.drawable.kenan8,R.drawable.kenan1,
                R.drawable.kenan2, R.drawable.kenan3, R.drawable.kenan4));
    }
}
public class MyRecyclerAdapter extends Adapter<MyRecyclerAdapter.MyHolder> {

    private Context mContext;
    private List<Integer> mDatas;
    private List<Integer> heights;
    private onItemClickListener onItemClickListener;
    public MyRecyclerAdapter(Context context, List<Integer> datas) {
        super();
        this.mContext = context;
        this.mDatas = datas;
        getRandomHeight(this.mDatas);
    }

    public interface onItemClickListener{
        //條目被點擊時觸發的回調
        void onOnItemClick(View view,int position);
        //長按時觸發的回調
        void onLongClick(View view,int position);
    }


    public void setOnItemClickListener(onItemClickListener onItemClickListener) {
        this.onItemClickListener = onItemClickListener;
    }
    @Override
    public int getItemCount() {
        // TODO Auto-generated method stub
        return mDatas.size();
    }

    @Override
    // 填充onCreateViewHolder方法返回的holder中的控件
    public void onBindViewHolder(final MyHolder holder, int position) {
        // TODO Auto-generated method stub  
        //得到item的LayoutParams布局參數
        ViewGroup.LayoutParams params= holder.itemView.getLayoutParams();
        //把隨機的高度賦予item布局
        params.height = heights.get(position);
        //把params設置給item布局
        holder.itemView.setLayoutParams(params);
        //為控件綁定數據
        holder.imageView.setImageResource(mDatas.get(position));
        //如果設置了監聽那么它就不為空,然后回調相應的方法
        if(onItemClickListener!=null){
            holder.itemView.setOnClickListener(new OnClickListener() {

                @Override
                public void onClick(View v) {
                    //得到當前點擊item的位置pos
                    int position = holder.getLayoutPosition();
                    //把事件交給我們實現的接口那里處理
                    onItemClickListener.onOnItemClick(holder.itemView, position);
                }
            });
            holder.itemView.setOnLongClickListener(new OnLongClickListener() {

                @Override
                public boolean onLongClick(View v) {
                    //得到當前點擊item的位置pos
                    int position = holder.getLayoutPosition();
                    //把事件交給我們實現的接口那里處理
                    onItemClickListener.onOnItemClick(holder.itemView, position);
                    return true;
                }
            });
        }
    }

    @Override
    // 重寫onCreateViewHolder方法,返回一個自定義的ViewHolder
    public MyHolder onCreateViewHolder(ViewGroup viewGroup, int arg1) {
        // 填充布局
        View view = LayoutInflater.from(mContext).inflate(R.layout.item,viewGroup, false);
        MyHolder holder = new MyHolder(view);
        return holder;
    }

    // 定義內部類繼承ViewHolder
    class MyHolder extends ViewHolder {

        private ImageView imageView;
        public MyHolder(View view) {
            super(view);
            imageView = (ImageView) view.findViewById(R.id.iv_item);
        }

    }
    //得到隨機item的高度
    private void getRandomHeight(List<Integer> datas) {
        heights = new ArrayList<Integer>();
        for (int i = 0; i < datas.size(); i++) {
            heights.add((int) (200+Math.random()*100));
        }
    }
}

 

來自:http://blog.csdn.net/u012124438/article/details/53495951

 

 本文由用戶 feqi8790 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!