1. 程式人生 > >Android開發之最新Recyclerview控制元件的使用詳解(一)

Android開發之最新Recyclerview控制元件的使用詳解(一)

    本篇博文主要給大家分享關於RecyclerView控制元件的使用及通過繼承RecyclerView來實現滑動時載入圖片的優化方案,也同樣能解決防止圖片亂序的問題,之前有在網上有看到大神對Android中ListView非同步載入圖片亂序問題進行過分析,並深入剖析原理後分別給出了3種對應的解決方案:一 、使用findViewWithTag。二、使用弱引用關聯。三、使用ImageLoad框架進行處理。

                         

RecyclerView的優點是什麼?

     根據官方的介紹RecylerView是ListView的升級版,既然如此那RecylerView必然有它的優點,現就RecylerView相對於ListView的優點羅列如下:
①RecylerView封裝了viewholder的回收複用,也就是說RecylerView標準化了ViewHolder,編寫Adapter面向的是ViewHolder而不再是View了,複用的   邏輯被封裝了,寫起來更加簡單。
②提供了一種插拔式的體驗,高度的解耦,異常的靈活,針對一個Item的顯示RecylerView專門抽取出了相應的類

來控制Item的顯示,使其的擴充套件性非常強。

例如:setLayoutManager(),該方法引數就是設定不同的排列樣式,你想控制橫向或者縱向滑動列表效果可以通過LinearLayoutManager這個類來進行控制(與GridView效果對應的是GridLayoutManager,與瀑布流對應的還有StaggeredGridLayoutManager等),也就是說RecylerView不再拘泥於ListView的線性展示方式,它也可以實現GridView的效果等多種效果。你想控制Item的分隔線,可以通過繼承RecylerView的ItemDecoration這個類,然後針對自己的業務需求去抒寫程式碼。③可以控制Item增刪的動畫,可以通過ItemAnimator這個類進行控制,當然針對增刪的動畫,RecylerView有其自己預設的實現。  

現在給大家講解RecyclerView的使用和步驟:

首先是Android Studio依賴:

compile 'com.android.support:recyclerview-v7:24.2.1'  ,

新增分割線的依賴:

 compile 'com.dinuscxj:recycleritemdecoration:1.0.0',

也可以到studio中進行版本依賴,

其次,在.xml中添加布局:

 <android.support.v7.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        ></android.support.v7.widget.RecyclerView>
接下來就是對Activity的操作了,也是最主要的程式碼:
一:給RecylerView的Activity設定佈局管理器的樣式以及實現的功能:

下面是Activity的程式碼如下:

import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.StaggeredGridLayoutManager;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Toast;

import com.daimajia.swipe.util.Attributes;
import com.wjf.cool.app.recyclerview.adapter.RecyclerViewAdapter;
import com.wjf.cool.app.ui.R;

import java.util.ArrayList;
import java.util.List;

public class RecyclerViewActivity extends AppCompatActivity {

    RecyclerView recyclerView;
    private RecyclerViewAdapter mAdapter;
    private List<String> list;//新增item
    private String[] data={"北京","上海","廣州","深圳","青海","合肥","宿州","蘇州","鹽城","南海",
            "南京","畢加索","美國","天津","湖南","浙江","內蒙古","愛迪生","愛琴海",
            "泗洪","海南","哈爾濱","淮安","黑龍江","麗江",};


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_recycler_view);
        recyclerView= (RecyclerView) findViewById(R.id.recyclerView);
        initData();//方法

        mAdapter=new RecyclerViewAdapter(this,list);
        mAdapter.setMode(Attributes.Mode.Single);
        recyclerView.setAdapter(mAdapter);
        mAdapter.notifyDataSetChanged();

        //設定佈局管理器(佈局風格)
       recyclerView.setLayoutManager(new LinearLayoutManager(this,LinearLayoutManager.VERTICAL,false));

        //設定item增加  刪除動畫
        recyclerView.setItemAnimator(new DefaultItemAnimator());
        //新增分割線(需要依賴recycleritemdecoration第三方庫)
        //recyclerView.addItemDecoration(new LinearDividerItemDecoration(this,4));
        //recyclerView.setItemAnimator(new DefaultItemAnimator());

        //給介面卡設定監聽
        mAdapter.setNewOnItemClickListener(listener);
        //設定側滑事件監聽
        recyclerView.setOnScrollListener(onScrollListener);

    }
    private void initData(){
        list=new ArrayList<>();
        for (int i=0;i<data.length;i++){
            list.add(data[i]+"");
        }
    }

    //給介面卡設定監聽
    RecyclerViewAdapter.OnNewItemClickListener listener=new RecyclerViewAdapter.OnNewItemClickListener() {
        @Override
        public void onNewItemClick(View view, int postion) {
            if (onScrollListener !=null){

            }
            //實現效果
            Toast.makeText(RecyclerViewActivity.this, "點選了" + postion, Toast.LENGTH_SHORT).show();
        }

        @Override
        public void OnNewItemLongClick(View view, int postion) {
            Toast.makeText(RecyclerViewActivity.this, "長按了" + postion, Toast.LENGTH_SHORT).show();
        }
    };

    //設定側滑事件監聽(其實不需要也可以)
    RecyclerView.OnScrollListener onScrollListener=new RecyclerView.OnScrollListener() {
        @Override
        public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
            super.onScrollStateChanged(recyclerView, newState);
        }

        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            super.onScrolled(recyclerView, dx, dy);
        }
    };

    /**
     * 實現彈出對話方塊進行點選選擇
     * 1.建立res/menu----.xml佈局檔案,實現
     * 2.重寫兩個方法。如下:
     *
     * */
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu,menu);//放入自定義佈局
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {

        int id=item.getItemId();

        switch (id){
            case R.id.action_listview://(spanCount:列數   orientation:(方向)
                recyclerView.setLayoutManager(new LinearLayoutManager(this));
                break;
            case R.id.action_gridview:
                recyclerView.setLayoutManager(new GridLayoutManager(this,2));
                break;
            case R.id.action_hor_gridview://(spanCount:列數   orientation:(方向)
                recyclerView.setLayoutManager(new StaggeredGridLayoutManager(4,StaggeredGridLayoutManager.HORIZONTAL));
                break;
            case R.id.action_staggered:
                startActivity(new Intent(RecyclerViewActivity.this,StaggeredGridLayoutActivity.class));
                break;
            case R.id.action_add://新增item
                mAdapter.addData(0);
                break;
            case R.id.action_delect://刪除item
                mAdapter.delectData(0);
                break;

        }
        return super.onOptionsItemSelected(item);
    }

}
二:給RecylerView設定介面卡:

可以看到對RecylerView的設定過程,比ListView要複雜一些,這也是RecylerView高度解耦的表現,雖然程式碼抒寫上有點複雜,但它的擴充套件性是極高的。
在瞭解了RecyclerView的一些控制之後,緊接著來看看它的Adapter的寫法,RecyclerView的Adapter與ListView的Adapter還是有點區別的,RecyclerView.Adapter,需要實現3個方法:
①onCreateViewHolder()
    這個方法主要生成為每個Item inflater出一個View,但是該方法返回的是一個ViewHolder。該方法把View直接封裝在ViewHolder中,然後我們面向的是ViewHolder這個例項,當然這個ViewHolder需要我們自己去編寫。直接省去了當初的convertView.setTag(holder)和convertView.getTag()這些繁瑣的步驟。
②onBindViewHolder()
     這個方法主要用於適配渲染資料到View中。方法提供給你了一個viewHolder,而不是原來的convertView。
③getItemCount()
      這個方法就類似於BaseAdapter的getCount方法了,即總共有多少個條目。

下面是介面卡Adapter的程式碼:

/**
 * Created by Administrator on 2016/11/29.
 * <p>
 * 實現RecyclerView介面卡:
 * 1.先建立自定義ViewHolder的內部類繼承ViewHolder
 * 2.自定義RecyclerView介面卡繼承系統的介面卡<VH>:泛型為自定義的內部類ViewHolder
 */

public class RecyclerViewAdapter extends RecyclerSwipeAdapter<RecyclerViewAdapter.MyViewHolder> {

    private LayoutInflater layoutInflater;
    private Context context;
    private List<String> list;
    protected SwipeItemRecyclerMangerImpl mItemManger = new SwipeItemRecyclerMangerImpl(this);

    public RecyclerViewAdapter(Context context, List<String> list) {
        this.list = list;
        this.context = context;
        layoutInflater = LayoutInflater.from(context);

    }

    //新增item
    public void addData(int pos) {
        list.add(pos, "Insert One");
        //更新,不是notifyDataSetChanged();
        notifyItemChanged(pos);
    }

    //刪除
    public void delectData(int pos) {
        list.remove(pos);
        //更新刪除,不是notifyDataSetChanged();
        notifyItemRemoved(pos);
    }

    //onCreateViewHolder+onBindViewHolder這兩個方法其實就是ListView中的getView方法,只是分開定義了
    //建立ViewHolder
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View itemview = layoutInflater.inflate(R.layout.item_recycler_view, parent, false);
        MyViewHolder viewHolder = new MyViewHolder(itemview);
        return viewHolder;
    }

    //繫結ViewHolder(操作item)
    @Override
    public void onBindViewHolder(final MyViewHolder holder, final int position) {
         holder.swipeLayout.setShowMode(SwipeLayout.ShowMode.LayDown);
        //設定側滑方向  預設向左滑
        holder.swipeLayout.addDrag(SwipeLayout.DragEdge.Left, holder.swipeLayout.findViewById(R.id.bottom_wrapper_1));
//        holder.swipeLayout.addSwipeListener(new SimpleSwipeListener() {
//            @Override
//            public void onOpen(SwipeLayout layout) {
//                YoYo.with(Techniques.Tada).duration(5000).delay(1000).playOn(layout.findViewById(R.id.img_delete));
//            }
//        });

        //item左側滑動監聽(刪除)
        holder.imgDelete.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mItemManger.removeShownLayouts(holder.swipeLayout);
                list.remove(position);
                notifyItemRemoved(position);
                notifyItemRangeChanged(position, list.size());
                mItemManger.closeAllItems();//關閉側滑選單
                Toast.makeText(view.getContext(), "刪除: " + holder.tvTitle.getText().toString() + "!", Toast.LENGTH_SHORT).show();
            }
        });
        //設定控制元件內容

        holder.tvTitle.setText((position + 1) + ".  "+list.get(position));
        mItemManger.bindView(holder.itemView, position);

        if (listener != null) {

            //給item實體設定監聽(或給物件裡面的控制元件設定監聽,看自己需要設定那個控制元件 )
            //點選監聽
            holder.tvTitle.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                        if (holder.swipeLayout != null) {
                            mItemManger.closeAllItems();//判斷點選item是關閉側滑
                        }
                        listener.onNewItemClick(v, position);

                }
            });
            //長按監聽
            holder.tvTitle.setOnLongClickListener(new View.OnLongClickListener() {
                @Override
                public boolean onLongClick(View v) {
                        if (mItemManger != null) {
                            mItemManger.closeAllItems();//判斷點選item是關閉側滑
                        }

                        listener.OnNewItemLongClick(v, position);

                    return true;
                }
            });
        }
    }

    @Override
    public int getItemCount() {
        return list.size();
    }

    //側滑重寫方法
    @Override
    public int getSwipeLayoutResourceId(int position) {
        return R.id.swipe;
    }


    //建立自定義item佈局類優化
    class MyViewHolder extends RecyclerView.ViewHolder {

        SwipeLayout swipeLayout;
        LinearLayout linLayTitle;
        TextView tvTitle;
        ImageView imgDelete;//刪除
        ImageView imgCollect;//收藏(未對此操作)

        public MyViewHolder(View itemView) {
            super(itemView);

            swipeLayout = (SwipeLayout) itemView.findViewById(R.id.swipe);
            linLayTitle = (LinearLayout) itemView.findViewById(R.id.linearLoyout_title);
            tvTitle = (TextView) itemView.findViewById(R.id.tv_reclyer_title);
            imgDelete = (ImageView) itemView.findViewById(R.id.img_delete);
            imgCollect = (ImageView) itemView.findViewById(R.id.img_collect);
//
//            itemView.setOnClickListener(new View.OnClickListener() {
//                @Override
//                public void onClick(View view) {
//                    Toast.makeText(view.getContext(), "onItemSelected: " + tvTitle.getText().toString(), Toast.LENGTH_SHORT).show();
//                }
//            });
        }
    }

    /**
     * Recycler的介面卡中沒有Item的監聽方法,因此需要自己定義
     * 監聽介面去實現監聽效果
     */
    //點選監聽介面
    public interface OnNewItemClickListener {
        //點選時的方法
        void onNewItemClick(View view, int postion);

        //長按時的方法
        void OnNewItemLongClick(View view, int postion);
    }

    //宣告監聽介面物件
    public OnNewItemClickListener listener;

    //對外提供一個監聽方法
    public void setNewOnItemClickListener(OnNewItemClickListener listener) {
        this.listener = listener;
    }
}

需要注意的是!!!:

一、RecyclerView瀑布流(StaggeredGridLayoutManager)的實現也很簡單,給定一個集合設定隨機的高度;在介面卡RecyclerView.Adapter中的onBindViewHolder()中設定。

程式碼如下:

 public StaggeredAdapter(Context context, List<String> lists) {
        this.context = context;
        this.lists = lists;
        getRandomHeight(this.lists);

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

在介面卡RecyclerView.Adapter中的onBindViewHolder()中設定:
 //繫結ViewHolder
    @Override
    public void onBindViewHolder(final MyViewHolder holder, final int position) {

        //設定隨機高度
        ViewGroup.LayoutParams params =  holder.itemView.getLayoutParams();//得到item的LayoutParams佈局引數
        params.height = heights.get(position);//把隨機的高度賦予item佈局
        holder.itemView.setLayoutParams(params);//把params設定給item佈局

        holder.mTv.setText(lists.get(position));//為控制元件繫結資料
        if(mListener!=null){//如果設定了監聽那麼它就不為空,然後回撥相應的方法
            holder.itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    int pos = holder.getLayoutPosition();//得到當前點選item的位置pos
                    mListener.ItemClickListener(holder.itemView,pos);//把事件交給我們實現的介面那裡處理
                }
            });
            holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
                @Override
                public boolean onLongClick(View v) {
                    int pos = holder.getLayoutPosition();//得到當前點選item的位置pos
                    mListener.ItemLongClickListener(holder.itemView,pos);//把事件交給我們實現的介面那裡處理
                    return true;
                }
            });
        }
    }

最後不要忘了在Activity中設定佈局管理器樣式:
 recyclerView.setLayoutManager(new StaggeredGridLayoutManager(3, StaggeredGridLayoutManager.VERTICAL));

這裡需要注意的是:StaggeredGridLayoutManager構造的第二個引數傳一個orientation,如果傳入的是StaggeredGridLayoutManager.VERTICAL那麼前面那個引數就代表有多少列;如果傳是StaggeredGridLayoutManager.HORIZONTAL那麼前面那個引數就代表有多少行。

RecyclerView.LayoutManager是一個抽象類,系統為我們提供了三個實現類。
LinearLayoutManager即線性佈局,這個是在上面的例子中我們用到的佈局。
GridLayoutManager即表格佈局。
StaggeredGridLayoutManager即流式佈局,如瀑布流效果。
二、RecycleView增加和刪除Item的動畫

在上面也提到了控制RecyclerView增加和刪除的動畫是通過ItemAnimator這個類來實現的,ItemAnimator這類也是個抽象的類,系統預設給我們提供了一種增加和刪除的動畫,下面我們就來看看這種動畫的效果,我們需要做的修改如下,在介面卡中自定義增加和刪除的兩個方法:

//新增item
    public void addData(int pos) {
        list.add(pos, "Insert One");
        //更新,不是notifyDataSetChanged();
        notifyItemChanged(pos);
        notifyItemInserted(pos);
        notifyItemRangeChanged(pos, list.size());
    }

    //刪除
    public void delectData(int pos) {
        list.remove(pos);
        //更新刪除,不是notifyDataSetChanged();
        notifyItemRemoved(pos);
        notifyItemRangeChanged(pos, list.size());
    }
之後在Activity中新增監聽點選呼叫即可,這裡我就不給大家寫程式碼了。
這裡需要說一下RecyclerView.Adapter中重新整理資料的幾個方法,一共有這麼幾個方法:
public final void notifyDataSetChanged()  
public final void notifyItemChanged(int position)  
public final void notifyItemRangeChanged(int positionStart, int itemCount)  
public final void notifyItemInserted(int position)   
public final void notifyItemMoved(int fromPosition, int toPosition)  
public final void notifyItemRangeInserted(int positionStart, int itemCount)  
public final void notifyItemRemoved(int position)  
public final void notifyItemRangeRemoved(int positionStart, int itemCount)  

notifyDataSetChanged():這個方法跟我們平時用到的ListView的Adapter的方法一樣,這裡就不多做描述了。
notifyItemChanged(int position):當position位置的資料發生了改變時就會呼叫這個方法,就會回撥對應position的onBindViewHolder()方法了,當然,因為ViewHolder是複用的,所以如果position在當前螢幕以外,也就不會回調了,因為沒有意義,下次position滾動會當前螢幕以內的時候同樣會呼叫onBindViewHolder()方法重新整理資料了。其他的方法也是同樣的道理。notifyItemRangeChanged(int positionStart, int itemCount):顧名思義,可以重新整理從positionStart開始itemCount數量的item了(這裡的重新整理指回調onBindViewHolder()方法)。
 notifyItemInserted(int position):這個方法是在第position位置被插入了一條資料的時候可以使用這個方法重新整理,注意這個方法呼叫後會有插入的動畫,這個動畫可以使用預設的,也可以自己定義。
notifyItemMoved(int fromPosition, int toPosition):這個方法是從fromPosition移動到toPosition為止的時候可以使用這個方法重新整理。
notifyItemRangeInserted(int positionStart, int itemCount):顯然是批量新增。
notifyItemRemoved(int position):第position個被刪除的時候重新整理,同樣會有動畫。
將上述更改執行,點選新增和刪除按鈕效果圖如下:
notifyItemRangeRemoved(int positionStart, int itemCount):批量刪除。
三、 RecyclerView增加分隔線樣式:
前面我們說到可以通過RecyclerView.addItemDecoration(ItemDecoration decoration)這個方法進行設定,其中它需要的引數就是我們自己定義的繼承自ItemDecoration的一個物件。我們可以建立一個繼承RecyclerView.ItemDecoration類來繪製分隔線,通過ItemDecoration可以讓我們每一個Item從視覺上面相互分開來,例如ListView的divider非常相似的效果。當然像我們上面的例子ItemDecoration我們沒有設定也沒有報錯,那說明ItemDecoration我們並不是強制需要使用,作為我們開發者可以設定或者不設定Decoration的。實現一個ItemDecoration,系統提供的ItemDecoration是一個抽象類,內部除去已經廢棄的方法以外,我們主要實現以下三個方法:

public static abstract class ItemDecoration {   
        public void onDraw(Canvas c,RecyclerView parent,State state) {   
          onDraw(c,parent);   
      }   
      public void onDrawOver(Canvas c,RecyclerView parent,State state) {   
          onDrawOver(c,parent);   
      }   
      public void getItemOffsets(RectoutRect, View view,RecyclerView parent,State state) {   
          getItemOffsets(outRect,((LayoutParams)view.getLayoutParams()).getViewLayoutPosition(),parent);   
      }   
  }  
然而因為當我們RecyclerView在進行繪製的時候會進行繪製Decoration,那麼會去呼叫onDraw和onDrawOver方法,那麼這邊我們其實只要去重寫onDraw和getItemOffsets這兩個方法就可以實現啦。然後LayoutManager會進行Item佈局的時候,會去呼叫getItemOffset方法來計算每個Item的Decoration合適的尺寸,下面我們來具體實現一個Decoration,DividerListItemDecoration.Java:
mport android.content.Context;
        import android.content.res.TypedArray;
        import android.graphics.Canvas;
        import android.graphics.Rect;
        import android.graphics.drawable.Drawable;
        import android.support.v7.widget.LinearLayoutManager;
        import android.support.v7.widget.RecyclerView;
        import android.view.View;

        import com.wjf.cool.app.ui.R;

/**
 * Created by 王建法 on 2017/3/9.
 * 列表分割線
 */

public class DividerLineItemDecoration extends RecyclerView.ItemDecoration {

    private static final int[] ATTRS = new int[]{
            android.R.attr. listDivider     //自定義分割線樣式

    };

    public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL;

    public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL;

    private Drawable mDivider;

    private int mOrientation;

    public DividerLineItemDecoration(Context context, int orientation) {
        final TypedArray a = context.obtainStyledAttributes(ATTRS );
        mDivider = a.getDrawable(0);
        a.recycle();
        setOrientation(orientation);
    }

    public void setOrientation( int orientation) {
        if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST) {
            throw new IllegalArgumentException( "invalid orientation");
        }
        mOrientation = orientation;
    }

    @Override
    public void onDraw(Canvas c, RecyclerView parent) {
        if (mOrientation == VERTICAL_LIST) {
            drawVertical(c, parent);
        } else {
            drawHorizontal(c, parent);
        }
    }

    public void drawVertical(Canvas c, RecyclerView parent) {
        final int left = parent.getPaddingLeft();
        final int right = parent.getWidth() - parent.getPaddingRight();

        final int childCount = parent.getChildCount();
        for (int i = 0; i < childCount; i++) {
            final View child = parent.getChildAt(i);
            final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
                    .getLayoutParams();
            final int top = child.getBottom() + params.bottomMargin;
            final int bottom = top + mDivider.getIntrinsicHeight();
            mDivider.setBounds(left, top, right, bottom);
            mDivider.draw(c);
        }
    }

    public void drawHorizontal(Canvas c, RecyclerView parent) {
        final int top = parent.getPaddingTop();
        final int bottom = parent.getHeight() - parent.getPaddingBottom();

        final int childCount = parent.getChildCount();
        for (int i = 0; i < childCount; i++) {
            final View child = parent.getChildAt(i);
            final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
                    .getLayoutParams();
            final int left = child.getRight() + params.rightMargin;
            final int right = left + mDivider.getIntrinsicHeight();
            mDivider.setBounds(left, top, right, bottom);
            mDivider.draw(c);
        }
    }

    @Override
    public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {
        if (mOrientation == VERTICAL_LIST) {
            outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
        }else{
            outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
        }
    }
}
 這裡我們採用系統主題(android.R.attr.listDivider)來設定成分隔線的,然後來獲取尺寸,位置進行setBound(),繪製,接著通過outRect.set()來設定繪製整個區域範圍,當然了它是有兩種情況的一種LinearLayoutManager.HORIZONTAL另外一種LinearLayoutManager.VERTICAL需要分別對其進行處理,最後不要忘記往RecyclerView中設定該自定義的分割線,然後在MainActivity中加上一句recyclerView .addItemDecoration(new DividerListItemDecoration(MainActivity.this,LinearLayoutManager.VERTICAL))即給RecyclerView增加分隔線,效果如下:


上面的程式碼只適合listView那樣的列表樣式,而當展示成GridView那樣的效果時,每一行就不止一個條目了,而有可能是多個,所以這個類就不再適用了我們需要重新寫一個新增分割線類。
程式碼如下:

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.StaggeredGridLayoutManager;
import android.view.View;

/**
 * Created by 王建法 on 2017/3/9.
 *
 * 表格分割線
 */

public class DividerGridItemDecoration extends RecyclerView.ItemDecoration {

    private static final int[] ATTRS = new int[] {
            android.R.attr.listDivider
    };
    private Drawable mDivider;//自定義分割線樣式

    public DividerGridItemDecoration(Context context)
    {
        final TypedArray a = context.obtainStyledAttributes(ATTRS );
        mDivider = a.getDrawable(0);
        a.recycle();
    }

    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state)
    {

        drawHorizontal(c, parent);
        drawVertical(c, parent);

    }

    private int getSpanCount(RecyclerView parent)
    {
        // 列數
        int spanCount = -1;
        RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
        if (layoutManager instanceof GridLayoutManager)
        {

            spanCount = ((GridLayoutManager) layoutManager).getSpanCount();
        } else if (layoutManager instanceof StaggeredGridLayoutManager)
        {
            spanCount = ((StaggeredGridLayoutManager) layoutManager)
                    .getSpanCount();
        }
        return spanCount;
    }

    public void drawHorizontal(Canvas c, RecyclerView parent)
    {
        int childCount = parent.getChildCount();
        for ( int i = 0; i < childCount; i++)
        {
            final View child = parent.getChildAt(i);
            final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
                    .getLayoutParams();
            final int left = child.getLeft() - params.leftMargin;
            final int right = child.getRight() + params.rightMargin
                    + mDivider.getIntrinsicWidth();
            final int top = child.getBottom() + params.bottomMargin;
            final int bottom = top + mDivider.getIntrinsicHeight();
            mDivider.setBounds(left, top, right, bottom);
            mDivider.draw(c);
        }
    }

    public void drawVertical(Canvas c, RecyclerView parent)
    {
        final int childCount = parent.getChildCount();
        for ( int i = 0; i < childCount; i++)
        {
            final View child = parent.getChildAt(i);

            final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
                    .getLayoutParams();
            final int top = child.getTop() - params.topMargin;
            final int bottom = child.getBottom() + params.bottomMargin;
            final int left = child.getRight() + params.rightMargin;
            final int right = left + mDivider.getIntrinsicWidth();

            mDivider.setBounds(left, top, right, bottom);
            mDivider.draw(c);
        }
    }

    private boolean isLastColum(RecyclerView parent, int pos, int spanCount,
                                int childCount)
    {
        RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
        if (layoutManager instanceof GridLayoutManager)
        {
            if ((pos + 1) % spanCount == 0) // 如果是最後一列,則不需要繪製右邊
            {
                return true;
            }
        } else if (layoutManager instanceof StaggeredGridLayoutManager)
        {
            int orientation = ((StaggeredGridLayoutManager) layoutManager)
                    .getOrientation();
            if (orientation == StaggeredGridLayoutManager.VERTICAL )
            {
                if ((pos + 1) % spanCount == 0) // 如果是最後一列,則不需要繪製右邊
                {
                    return true;
                }
            } else
            {
                childCount = childCount - childCount % spanCount;
                if (pos >= childCount) // 如果是最後一列,則不需要繪製右邊
                    return true;
            }
        }
        return false;
    }

    private boolean isLastRaw(RecyclerView parent, int pos, int spanCount,
                              int childCount)
    {
        RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
        if (layoutManager instanceof GridLayoutManager)
        {
            childCount = childCount - childCount % spanCount;
            if (pos >= childCount) // 如果是最後一行,則不需要繪製底部
                return true;
        } else if (layoutManager instanceof StaggeredGridLayoutManager)
        {
            int orientation = ((StaggeredGridLayoutManager) layoutManager)
                    .getOrientation();
            // StaggeredGridLayoutManager 且縱向滾動
            if (orientation == StaggeredGridLayoutManager.VERTICAL )
            {
                childCount = childCount - childCount % spanCount;
                // 如果是最後一行,則不需要繪製底部
                if (pos >= childCount)
                    return true;
            } else
            // StaggeredGridLayoutManager 且橫向滾動
            {
                // 如果是最後一行,則不需要繪製底部
                if ((pos + 1) % spanCount == 0)
                {
                    return true;
                }
            }
        }
        return false;
    }

    @Override
    public void getItemOffsets(Rect outRect, int itemPosition,
                               RecyclerView parent)
    {
        int spanCount = getSpanCount(parent);
        int childCount = parent.getAdapter().getItemCount();
        if (isLastRaw(parent, itemPosition, spanCount, childCount))// 如果是最後一行,則不需要繪製底部
        {
            outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
        } else if (isLastColum(parent, itemPosition, spanCount, childCount))// 如果是最後一列,則不需要繪製右邊
        {
            outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
        } else
        {
            outRect.set(0, 0, mDivider.getIntrinsicWidth(),
                    mDivider.getIntrinsicHeight());
        }
    }
}
其它沒有什麼需改動的,最後不要忘記往RecyclerView中設定該自定義的分割線,然後在MainActivity中加上一句recyclerView.addItemDecoration(new DividerGridItemDecoration(StaggeredGridLayoutActivity.this));即給RecyclerView增加分隔線,效果如下:


可以看到已經有了分隔線,跟ListView的效果基本一致了。當然了,既然谷歌給我們提供了這個專門新增分隔線的方法,那它肯定會允許我們自定義分隔線的樣式,那麼如何自定義分割線的樣式呢?請看下面。

1、改變分隔線樣式
在上面的DividerItemDecoration這個類中可以看到這個分隔線是跟ListView一樣的,即系統的預設的樣式,因此我們可以在styles的xml檔案中進行更改,更改如下:

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>

        <!--新增分割線顏色樣式,如果不需要測刪除就是系統自帶的分割線樣式-->
        <item name= "android:listDivider">@drawable/divideritem </item >
    </style>
divideritem程式碼如下:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle" >

    <!-- 填充的顏色 -->
    <solid android:color ="#5d5"/>
    
    <!--  線條大小 -->
    <size android:height ="3dp" android:width ="3dp"/>
</shape>
修改分割線樣式之後的效果圖如下:



四、 給RecyclerView的Item新增點選事件
    到這裡還有一點從文章開頭到現在我們都沒有提及,就是Item的點選事件RecyclerView監聽事件處理在ListView使用的時候,該控制元件給我們提供一個onItemClickListener監聽器,這樣當我們點選Item的時候,會回撥相關的方法,以便我們方便處理Item點選事件。對於RecyclerView來講,非常可惜的是,該控制元件沒有給我們提供這樣的內建監聽器方法,不過我們可以進行改造實現,可以這樣實現Item的點選事件的監聽,在我們的adapter中增加這兩個方法:

/**
     * Recycler的介面卡中沒有Item的監聽方法,因此需要自己定義
     * 監聽介面去實現監聽效果
     */
    //點選監聽介面
    public interface OnNewItemClickListener {
        //點選時的方法
        void onNewItemClick(View view, int postion);

        //長按時的方法
        void OnNewItemLongClick(View view, int postion);
    }

    //宣告監聽介面物件
    public OnNewItemClickListener listener;

    //對外提供一個監聽方法
    public void setNewOnItemClickListener(OnNewItemClickListener listener) {
        this.listener = listener;
    }
然後onBindViewHolder方法要做如下更改:
 //繫結ViewHolder(操作item)
    @Override
    public void onBindViewHolder(final MyViewHolder holder, final int position) {
         
        //設定控制元件內容

        holder.tvTitle.setText((position + 1) + ".  "+list.get(position));
        mItemManger.bindView(holder.itemView, position);

        if (listener != null) {

            //給item實體設定監聽(或給物件裡面的控制元件設定監聽,看自己需要設定那個控制元件 )
            //點選監聽
            holder.tvTitle.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                        if (holder.swipeLayout != null) {
                            mItemManger.closeAllItems();//判斷點選item是關閉側滑
                        }
                        listener.onNewItemClick(v, position);

                }
            });
            //長按監聽
            holder.tvTitle.setOnLongClickListener(new View.OnLongClickListener() {
                @Override
                public boolean onLongClick(View v) {
                        if (mItemManger != null) {
                            mItemManger.closeAllItems();//判斷點選item是關閉側滑
                        }

                        listener.OnNewItemLongClick(v, position);

                    return true;
                }
            });
        }
    }
最後在MainAcitivity中增加如下程式碼:
  //給介面卡設定監聽
    RecyclerViewAdapter.OnNewItemClickListener listener=new RecyclerViewAdapter.OnNewItemClickListener() {
        @Override
        public void onNewItemClick(View view, int postion) {
            if (onScrollListener !=null){

            }
            //實現效果
            Toast.makeText(RecyclerViewActivity.this, "點選了" + postion, Toast.LENGTH_SHORT).show();
        }

        @Override
        public void OnNewItemLongClick(View view, int postion) {
            Toast.makeText(RecyclerViewActivity.this, "長按了" + postion, Toast.LENGTH_SHORT).show();
        }
    };
執行效果如下:

               

可以看到Item的onClick和onLongClick事件都觸發了。到此關於RecyclerView的基本用法就介紹的差不多了,當然,還有幾個點沒有提到,比方說瀑布流、下拉重新整理、上拉載入等,之後會給大家一一介紹!!!

大家需要什麼樣的功能和需求佈局樣式,自行改動上面的程式碼就OK了!!!