【FastDev4Android框架開發】RecyclerView完全解析之打造新版類Gallery效果(二十九)

轉載請標明出處:

http://blog.csdn.net/developer_jiangqq/article/details/49946589

本文出自:【江清清的博客】

().前言:   

           【好消息】個人網站已經上線運行,後面博客以及技術乾貨等精彩文章會同步更新,請大家關注收藏:http://www.lcode.org

         話說RecyclerView已經面市很久,也在很多應用中得到廣泛的使用,在整個開發者圈子裏面也擁有很不錯的口碑,那說明RecyclerView擁有比ListView,GridView之類控件有很多的優點,例如:數據綁定,Item View創建,View的回收以及重用等機制。本系列文章會包括到以下三個部分:

  1. RecyclerView控件的基本使用,包括基礎,進階,高級部分,動畫之類(點擊進入)
  2. RecyclerView控件的實戰實例
  3. RecyclerView控件集合AA(Android Annotations)注入框架實例

         今天使我們本系列文章的第二講主要是我們通過RecyclerView來打造一個新版類似Gallery控件的效果。本次講解所有用的Demo例子已經全部更新到下面的項目中了,歡迎大家starfork

         FastDev4Android框架項目地址:https://github.com/jiangqqlmj/FastDev4Android

().基本實現

       上一講我們已經對於RecyclerView的基本使用和進階部分做了講解(點擊進入),下面我們一步步的來打造一個新版Gallery效果控件。先來看一下和RecyclerView相關類:

類名

說明

RecyclerView.Adapter

可以託管數據集合,爲每一項Item創建視圖並且綁定數據

RecyclerView.ViewHolder

承載Item視圖的子佈局

RecyclerView.LayoutManager

負責Item視圖的佈局的顯示管理

RecyclerView.ItemDecoration

給每一項Item視圖添加子View,可以進行畫分隔線之類的東西

RecyclerView.ItemAnimator

負責處理數據添加或者刪除時候的動畫效果

       那如果要實現Gallery的效果,裏面的Item是橫向滑動的,也就是說我們的RecyclerView可以支持橫向滑動,這邊我們直接採用了LinearLayoutManager佈局管理器,同時設置方向爲:HORIZONTAL(水平)

下面來具體看代碼:

1.作爲RecyclerView控件,我們需要設置每一項Item的佈局:

<?xmlversion="1.0" encoding="utf-8"?>
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
   android:layout_height="wrap_content"
   android:layout_width="wrap_content"
    android:gravity="center"
    android:padding="8.0dip">
    <ImageView
        android:id="@+id/item_img"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:scaleType="fitXY"
       android:adjustViewBounds="true"
       android:src="@drawable/ic_item_gallery"/>
    <TextView
        android:id="@+id/item_tv"
        android:text="標題1"
       android:layout_marginTop="5dp"
        android:textSize="15sp"
       android:layout_gravity="center_horizontal"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
        />
</LinearLayout>

這個佈局中我們比較簡單,定義了一個圖片和一個標題,垂直方向佈局。

2.間接着,和ListView寫法差不多,需要自定義適配器,來創建每一項佈局視圖以及把數據和視圖綁定起來,所以這邊繼承RecyclerView.Adapter類創建一個自定義適配器GalleryRecyclerAdapter.java。那麼需要實現基類中的三個方法:

  • onCreateViewHolder(ViewGroup parent,int viewType) 創建Item View然後通過ViewHolder來承載
  • onBindViewHolder(ViewHolder holder,int position)進行視圖和數據綁定
  • getItemCount()獲取列表中視圖Item的數量

具體GallerRecyclerAdapter實現代碼如下:

public class GalleryRecyclerAdapter extends RecyclerView.Adapter<GalleryRecyclerAdapter.ViewHolder> {
 
    private List<GalleryModel> models;
    private LayoutInflater mInflater;
 
    public GalleryRecyclerAdapter(Context context){
        models=new ArrayList<GalleryModel>();
        for (int i=0;i<20;i++){
            int index=i+1;
            models.add(new GalleryModel(R.drawable.ic_item_gallery,"Item"+index));
        }
        mInflater=LayoutInflater.from(context);
    }
 
    /**
     * 創建Item View  然後使用ViewHolder來進行承載
     * @param parent
     * @param viewType
     * @return
     */
    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view=mInflater.inflate(R.layout.item_gallery_recycler,parent,false);
        ViewHolder viewHolder=new ViewHolder(view);
        return viewHolder;
    }
 
    /**
     * 進行綁定數據
     * @param holder
     * @param position
     */
    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
       holder.item_img.setImageResource(models.get(position).getImgurl());
       holder.item_tv.setText(models.get(position).getTitle());
    }
 
    @Override
    public int getItemCount() {
        return models.size();
    }
 
 
    //自定義的ViewHolder,持有每個Item的的所有界面元素
    public static class ViewHolder extends RecyclerView.ViewHolder {
        private ImageView item_img;
        private TextView item_tv;
        public ViewHolder(View view){
            super(view);
           item_img=(ImageView)view.findViewById(R.id.item_img);
           item_tv=(TextView)view.findViewById(R.id.item_tv);
        }
    }
 
}

3.注意看上面的代碼,我們繼承了RecyclerView.ViewHolder實現一個自定義類ViewHolder,這個用來承載我們的子Item視圖,現在Google已經要求開發者必須要使用ViewHolder了。在ViewHolder中我們進行控件的初始化工作,然後保存View視圖。

//自定義的ViewHolder,持有每個Item的的所有界面元素
    public static class ViewHolder extends RecyclerView.ViewHolder {
        private ImageView item_img;
        private TextView item_tv;
        public ViewHolder(View view){
            super(view);
           item_img=(ImageView)view.findViewById(R.id.item_img);
           item_tv=(TextView)view.findViewById(R.id.item_tv);
        }
    }

4.最後在Activity中控件設置,例如佈局管理器,Adapter綁定即可,完整代碼如下:

public class RecyclerGalleryActivity extends BaseActivity {
    private RecyclerView gallery_recycler;
    private LinearLayout top_bar_linear_back;
    private TextView top_bar_title;
    @Override
    protected void onCreate(BundlesavedInstanceState) {
        super.onCreate(savedInstanceState);
       setContentView(R.layout.recycler_gallery_layout);
       top_bar_linear_back=(LinearLayout)this.findViewById(R.id.top_bar_linear_back);
       top_bar_linear_back.setOnClickListener(new CustomOnClickListener());
       top_bar_title=(TextView)this.findViewById(R.id.top_bar_title);
       top_bar_title.setText("RecyclerView打造Gallery效果");
        //初始化RecyclerView控件
       gallery_recycler=(RecyclerView)this.findViewById(R.id.gallery_recycler);
        //固定高度
        gallery_recycler.setHasFixedSize(true);
        //創建佈局管理器
        LinearLayoutManager linearLayoutManager=new LinearLayoutManager(this);
        //設置橫向
       linearLayoutManager.setOrientation(OrientationHelper.HORIZONTAL);
        //設置佈局管理器
       gallery_recycler.setLayoutManager(linearLayoutManager);
        //創建適配器
        GalleryRecyclerAdapter adapter=new GalleryRecyclerAdapter(this);
        //綁定適配器
        gallery_recycler.setAdapter(adapter);
    }
    class CustomOnClickListener implements View.OnClickListener{
        @Override
        public void onClick(View v) {
           RecyclerGalleryActivity.this.finish();
        }
    }
}

5.在看運行效果之前,我們先來看下上面的代碼,上面的代碼基本註釋已經全部加了,相應大家可以看的懂,不過我們需要來講一下上面的LayoutManager(佈局管理器)

       在上一講中我們也講到了,LayoutManger(佈局管理器)該類負責將每一個Item視圖在RecyclerView中的佈局。目前RecyclerView已經給我們提供三個內置管理器:LinearLayoutManger,GridLayoutManger以及StaggeredGridLayoutManager。這邊的例子中我們是採用LinearLayoutManger而且設置了橫向水平佈局了。當然LinearLayoutManger還給我們提供了以下幾個方法來讓開發者方便的獲取到屏幕上面的頂部item和頂部item相關的信息:

  • findFirstVisibleItemPosition()
  • findFirstCompletlyVisibleItemPosition()
  • findLastVisibleItemPosition()
  • findLastCompletlyVisibleItemPosition()

這邊的具體設置代碼如下:

        //創建佈局管理器
        LinearLayoutManagerlinearLayoutManager=new LinearLayoutManager(this);
        //設置橫向
       linearLayoutManager.setOrientation(OrientationHelper.HORIZONTAL);
        //設置佈局管理器
       gallery_recycler.setLayoutManager(linearLayoutManager);

6.初步運行效果如下:

 

().升級加入點擊事件

          通過上面的方式我們顯示了一個類似於Gallery的效果,但是還遠遠不如實際Gallery的效果,現在只是可以有多項Item以及可以左右滑動,但是沒有點擊事件,下面我們來加入點擊事件操作。

          對於ListView來講,我們可以爲ListView加入setOnItemClickListener監聽事件,但是對於RecyclerView控件來講,RecyclerView已經不再負載Item視圖的佈局和顯示,這些工作已經交給了LayoutManger來做了。所以RecyclerView也沒有給我們提供類似onItemClick事件,這樣如果非得要實現類似的功能,我們開發者也可以自定義模擬實現。來,我們繼續往下看….

           1.我們最終要實現點擊列表上面每一項Item來回調點擊方法,那麼我們可以在Adapter中的每一項View上面做文章,首先我們來看一下Adapter中的onCreateViewHolder()方法:

 public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        Viewview=mInflater.inflate(R.layout.item_gallery_recycler,parent,false);
        ViewHolder viewHolder=new ViewHolder(view);
        return viewHolder;
    }
           2.該方法創建出了Item 視圖,然後通過ViewHolder來進行承載了,既然這樣那我們可以在View加載出來之後給它設置一些屬性例如:顏色,大小,當然也可以是點擊事件等等。那這邊我們給View添加onClick事件,然後在onClick方法把View點擊觸發的事件回調出去,同時可以回調一些參數內容出去。OK,那麼我們這邊就需要一個自定義的接口了,我們創建一個GallerRecyclerAdapter的內部類接口:

/**
     * 類似ListView的 onItemClickListener接口
     */
    public interface OnRecyclerViewItemClickListener{
        /**
         * Item View發生點擊回調的方法
         * @param view   點擊的View
         * @paramposition  具體Item View的索引
         */
        void onItemClick(View view,intposition);
    }

        3.然後定義接口,同時提供set和get方法,來讓外部傳入該接口,初始化:

private OnRecyclerViewItemClickListener onRecyclerViewItemClickListener;
 
    public OnRecyclerViewItemClickListener getOnRecyclerViewItemClickListener() {
        return onRecyclerViewItemClickListener;
    }
    public void setOnRecyclerViewItemClickListener(OnRecyclerViewItemClickListener onRecyclerViewItemClickListener) {
        this.onRecyclerViewItemClickListener =onRecyclerViewItemClickListener;
    }

       4.現在開始在onCreateViewHolder()方法中給View添加一個onClick事件,然後相應處理,判斷onRecyclerViewItemClickListener是否存在,把事件回調出去:

 view.setOnClickListener(newView.OnClickListener() {
            @Override
            public void onClick(View v) {
               if(onRecyclerViewItemClickListener!=null){
                   onRecyclerViewItemClickListener.onItemClick(view,(int)view.getTag());
                }
            }
        });

      5.上面的代碼中大家可能注意到onItemClick()方法中的第二個參數,獲取了tag,因爲這邊的position索引值是在onBindViewHolder()方法中設置的:

public voidonBindViewHolder(ViewHolder holder, int position) {
       holder.item_img.setImageResource(models.get(position).getImgurl());
       holder.item_tv.setText(models.get(position).getTitle());
        holder.itemView.setTag(position);
    }

      6.OK這邊我們搞定了一個Item點擊監聽方法,接下去就是使用了,

adapter.setOnRecyclerViewItemClickListener(new GalleryRecyclerAdapter.OnRecyclerViewItemClickListener() {
            @Override
            public void onItemClick(View view,int position) {
               Toast.makeText(RecyclerGalleryActivity.this,"您點擊的Item的索引爲:"+position,Toast.LENGTH_SHORT).show();
            }
        });

     7.現在該功能代碼整完了,運行效果如下:


().升級之加入分割線

         上面我們已經給每一項Item加入了點擊回調事件,但是總感覺還缺少點什麼東西,例如分隔線。很遺憾的是,RecyclerView沒有提供ListView控件這樣設置分割線的方法,不過它給我們提供了ItemDecoration類。這個ItemDecoration可以使得每一個Item在視覺上面進行分隔開來。RecyclerView沒有要求ItemDecoration必須要設置,同樣作爲開發者可以選擇不設置或者設置多個Decoration。然後RecyclerView會進行相應的繪製。

          我們這邊定義了一個TestDecoration類,該類繼承自RecyclerView.Decoration。只需要實現一下的兩個方法即可:

  • onDraw(Canvas c,RecyclerView parent,RecyclerView.State state)
  • getItemOffset(Rect outRect,int itemPosition,RecyclerView parent)

 具體實現代碼如下:

public class TestDecoration extends RecyclerView.ItemDecoration {
    //採用系統內置的風格的分割線
    private static final int[] attrs=newint[]{android.R.attr.listDivider};
    private Drawable mDivider;
 
    public TestDecoration(Context context) {
        TypedArray typedArray=context.obtainStyledAttributes(attrs);
        mDivider=typedArray.getDrawable(0);
        typedArray.recycle();
    }
 
    /**
     * 進行自定義繪製
     * @param c
     * @param parent
     * @param state
     */
    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        int top=parent.getPaddingTop();
        intbottom=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();
            intleft=child.getRight()+layoutParams.rightMargin;
            intright=left+mDivider.getIntrinsicWidth();
           mDivider.setBounds(left,top,right,bottom);
            mDivider.draw(c);
        }
    }
 
    @Override
    public void getItemOffsets(Rect outRect,View view, RecyclerView parent, RecyclerView.State state) {
       outRect.set(0,0,mDivider.getIntrinsicWidth(),0);
    }
}

最後給RecyclerView添加該分隔線即可:

//設置分割線
gallery_recycler.addItemDecoration(new TestDecoration(this));

運行效果如下:


().升級之分割線改造

          仔細看上面的運行效果,我們會發現一個問題,那就是分割線垂直分佈,但是沒有自適應控件的高度,直接延伸到界面的底部了。重新檢查了有關的所有佈局文件發現,高度都設置成了warp_content,但是實際的效果還是沒有自適應。原來在哪裏呢?

          真正的原因是因爲RecyclerView控件已經不負責每一項VIew的顯示了,那我們來看LayoutManger(佈局管理器)該進行負責Item的佈局顯示了,所以我們需要進行實現一個LayoutManger,然後重寫裏邊的onMeasure()方法。計算高度即可,具體代碼如下:

public class CustomLinearLayoutManager extends LinearLayoutManager {
    public CustomLinearLayoutManager(Contextcontext) {
        super(context);
    }
 
    @Override
    public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state, int widthSpec, int heightSpec) {
        Viewview=recycler.getViewForPosition(0);
        if(view!=null){
           measureChild(view,widthSpec,heightSpec);
            int mWidth=View.MeasureSpec.getSize(widthSpec);
            intmHeight=view.getMeasuredHeight();
           setMeasuredDimension(mWidth,mHeight);
        }
    }
}

然後RecyclerView使用CustomLinearLayoutManger即可,運行效果如下:

().升級之添加刪除Item動畫

        RecyclerView控件的一個優美之處就是當裏邊Item發生變化的時候可以加入相應的動畫效果,涉及的類爲RecyclerView.ItemAnimatior。一般當存在以下三種操作的時候可以加入動畫效果:

  • Item刪除
  • Item添加
  • Item移動

 當我們的數據,或者移動的時候,去掉用Adapter給我們提供的以下兩個方法即可:

  •  notifyItemInserted(int position)
  • notifyItemRemoved(int position)

那我們可以在Adapter中加入兩個方法,分別爲添加Item和刪除Item的方法:

  //添加數據
    public void addItem(GalleryModel model, intposition) {
        models.add(position, model);
        notifyItemInserted(position);
    }
    //刪除數據
    public void removeItem(int position) {
        models.remove(position);
        notifyItemRemoved(position);
    }

    然後在外部進行調用這兩個方法:

    adapter.addItem(newGalleryModel(R.drawable.ic_item_gallery,"Item Add"),3);
   adapter.removeItem(2);

最後千萬不要忘記給RecyclerView設置動畫效果,我這邊就直接採用默認動畫了。

 //設置動畫
 gallery_recycler.setItemAnimator(newDefaultItemAnimator());

 最終運行效果如下:


 ().最後總結

           今天通過實例帶大家又重新把RecyclerView的相關使用講解了一遍,實現類似Gallery效果,當然實例中還有很多缺點,需要進一步優化,後面的文章中也會繼續更新的~

           本次具體實例註釋過的全部代碼已經上傳到FastDev4Android項目中了。同時歡迎大家去Github站點進行clone或者下載瀏覽:

https://github.com/jiangqqlmj/FastDev4Android 同時歡迎大家starfork整個開源快速開發框架項目~下一講我們會進行RecyclerView集合AA(Android Annotations)注入框架來實現實例,敬請期待!

尊重原創,轉載請註明:From Sky丶清(http://blog.csdn.net/developer_jiangqq) 侵權必究!

關注我的訂閱號,每天分享移動開發技術(Android/IOS),項目管理以及博客文章!


關注我的微博,可以獲得更多精彩內容


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章