RecyclerView、CardView實戰

本篇blog參照網上大神demo實現,效果一樣http://frank-zhu.github.io/android/2015/02/26/android-recyclerview-part-3/

關鍵點:
- CardView的使用,CardView繼承的是FrameLayout,所以擺放內部控件的時候需要注意一下;
- RecyclerView的簡單使用,關鍵理解RecyclerView實現的思想,對比ListView學習;
- array配置文件讀取

一、首先,引入依賴包,重新編譯

compile 'com.android.support:appcompat-v7:22.2.0'
    compile 'com.android.support:recyclerview-v7:21.0.3'
    compile 'com.android.support:cardview-v7:21.0.3'

CardView

先來簡單介紹下CardView,其實我們可以在很多App中已經看到過這樣的效果,就是把list的一個item做成卡片樣式的,之前很多人通過自定義view可以實現卡片的效果,不過現在有了現成的,Google親生。CardView繼承的是FrameLayout,所以擺放內部控件的時候需要注意一下啦。
簡單總結下CardView的參數:

<resources>
    <declare-styleable name="CardView">
        <!-- Background color for CardView. -->
        <!-- 背景色 -->
        <attr name="cardBackgroundColor" format="color" />
        <!-- Corner radius for CardView. -->
        <!-- 邊緣弧度數 -->
        <attr name="cardCornerRadius" format="dimension" />
        <!-- Elevation for CardView. -->
        <!-- 高度 -->
        <attr name="cardElevation" format="dimension" />
        <!-- Maximum Elevation for CardView. -->
        <!-- 最大高度 -->
        <attr name="cardMaxElevation" format="dimension" />
        <!-- Add padding in API v21+ as well to have the same measurements with previous versions. -->
        <!-- 設置內邊距,v21+的版本和之前的版本仍舊具有一樣的計算方式 -->
        <attr name="cardUseCompatPadding" format="boolean" />
        <!-- Add padding to CardView on v20 and before to prevent intersections between the Card content and rounded corners. -->
        <!-- 在v20和之前的版本中添加內邊距,這個屬性是爲了防止卡片內容和邊角的重疊 -->
        <attr name="cardPreventCornerOverlap" format="boolean" />
        <!-- 下面是卡片邊界距離內部的距離-->
        <!-- Inner padding between the edges of the Card and children of the CardView. -->
        <attr name="contentPadding" format="dimension" />
        <!-- Inner padding between the left edge of the Card and children of the CardView. -->
        <attr name="contentPaddingLeft" format="dimension" />
        <!-- Inner padding between the right edge of the Card and children of the CardView. -->
        <attr name="contentPaddingRight" format="dimension" />
        <!-- Inner padding between the top edge of the Card and children of the CardView. -->
        <attr name="contentPaddingTop" format="dimension" />
        <!-- Inner padding between the bottom edge of the Card and children of the CardView. -->
        <attr name="contentPaddingBottom" format="dimension" />
    </declare-styleable>
</resources>

RecyclerView

先來說說RecycleView的優點就是,他可以通過設置LayoutManager來快速實現listview、gridview、瀑布流的效果,而且還可以設置橫向和縱向顯示,添加動畫效果也非常簡單(自帶了ItemAnimation,可以設置加載和移除時的動畫,方便做出各種動態瀏覽的效果),也是官方推薦使用的。
關鍵是RecyclerView將view的操作交給了Adapter,而不在自身處理view。

OK,簡單瞭解了CardView、RecyclerView,接下來實現我們的效果!

簡單效果的實現:
很簡單的List列表樣式,外層RecycleView,item用CardView實現。

這裏寫圖片描述這裏寫圖片描述

activity_main佈局文件:

<RelativeLayout 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"
    tools:context=".MainActivity">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/m_rv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</RelativeLayout>

關鍵是我們的NormalRecyclerViewAdapter

package com.wj.adapter;

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;

import com.wj.R;

/**
 * Created by ${wj} ,
 * on 2015/7/15 0015.
 */
public class NormalRecyclerViewAdapter extends RecyclerView.Adapter<NormalRecyclerViewAdapter.NormalTextViewHolder>{

    /**
     * 視圖加載器
     */
    private LayoutInflater mInflater;
    /**
     * 內容提供者
     */
    private Context mContext;
    /**
     * Item展示的內容,這裏只是一個簡單的text.
     */
    private String[] mTitles;

    /**
     * 構造器,正常應該傳入我們Item所要展示的對象List,這裏只是簡單實現了文字展示
     * @param context
     */
    public NormalRecyclerViewAdapter(Context context) {
        this.mContext = context;

        mInflater=LayoutInflater.from(mContext);
        mTitles=mContext.getResources().getStringArray(R.array.titles);
    }

    /**
     * 創建RecyclerView的VIewHolder
     * @param viewGroup
     * @param i
     * @return
     */
    @Override
    public NormalTextViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {

        //加載佈局文件
        View view=mInflater.inflate(R.layout.item_text,viewGroup,false);
        //創建佈局文件對應的ViewHolder
        return new NormalTextViewHolder(view);
    }

    /**
     * 實例化viewHolder中各控件內容
     * @param normalTextViewHolder
     * @param i
     */
    @Override
    public void onBindViewHolder(NormalTextViewHolder normalTextViewHolder, int i) {
        normalTextViewHolder.itemText.setText(mTitles[i]);
    }

    /**
     * 返回Item個數
     * @return
     */
    @Override
    public int getItemCount() {
        return mTitles==null ? 0 : mTitles.length;
    }

    /**
     * RecyclerView關鍵類,ViewHolder.這個就是RecyclerView優於ListView的關鍵點之一,封裝了ViewHolder,可以更好的實現view的重載
     * 這裏需要做的操作
     * 1.聲明我們的Item裏面的所有控件
     * 2.在構造函數中初始化我們聲明的控件
     * 3.可以設置Item的點擊事件,不再像ListView一樣,交給外層來處理
     */
    public class NormalTextViewHolder extends RecyclerView.ViewHolder{

        private TextView itemText;

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

            itemText= (TextView) itemView.findViewById(R.id.text_view);

            itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Toast.makeText(mContext,mTitles[getPosition()],Toast.LENGTH_SHORT).show();
                }
            });
        }
    }

}

爲了方便理解,我加了詳細的註釋

MainActivity中很簡單

  @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mRecyclerView= (RecyclerView) findViewById(R.id.m_rv);

        mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
        //mRecyclerView.setLayoutManager(new GridLayoutManager(this,2));

        mAdapter=new NormalRecyclerViewAdapter(this);
        mRecyclerView.setAdapter(mAdapter);
//        mAdapter1=new MultipleItemAdapter(this);
//        mRecyclerView.setAdapter(mAdapter1);

    }

關鍵注意:設置Layoutmanger方法,可以看到上面代碼我們設置了linearLayoutManger和GridLayoutManger,就可以分別實現上面的兩種效果。

接下來再來看一下Adapter的複雜用法,可以看到下面效果Item的展示形式不一樣,對的,這裏其實我們用了兩個Item的佈局,一個是圖片+文字的,一個是單有文字的(紅色背景)。感覺這樣的效果蠻讚的,那麼如何實現呢,其實很簡單。

這裏寫圖片描述

關鍵還是在Adapter中實現,MultileItemAdapter,這裏我們寫了兩個ViewHoder

 /**
     * 只顯示文本的item
     */
    public class TextViewHolder extends RecyclerView.ViewHolder{

        private TextView itemText;

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

            itemText= (TextView) itemView.findViewById(R.id.text_view);

            //Item的點擊事件
            itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Toast.makeText(mContext, mTitles[getPosition()], Toast.LENGTH_SHORT).show();
                }
            });
        }
    }
 /**
     * 顯示圖像和文本的viewhoder
     */
    public class ImageViewHolder extends RecyclerView.ViewHolder{

        private ImageView imageView;
        private TextView textView;

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

            imageView= (ImageView) itemView.findViewById(R.id.item_image_iv);
            textView= (TextView) itemView.findViewById(R.id.item_image_tv);

            //Item的點擊事件
            itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Toast.makeText(mContext, mTitles[getPosition()], Toast.LENGTH_SHORT).show();
                }
            });
        }
    }

那麼我們如何來區分加載這兩個view呢,很簡單

public class MultipleItemAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{

    /**
     * 枚舉類型,兩種顯示方式
     */
    public static enum ITEM_TYPE{
        ITEM_TYPE_IMAGE,
        ITEM_TYPE_TEXT
    }

    /**
     * 不註釋了,應該明白
     */
    private LayoutInflater mInflater;
    private Context mContext;
    private String[] mTitles;

    /**
     * 同上
     * @param context
     */
    public MultipleItemAdapter(Context context) {
        this.mContext = context;

        mInflater=LayoutInflater.from(mContext);
        //從values的array文件裏讀取數據
        mTitles=mContext.getResources().getStringArray(R.array.titles);
    }

    /**
     * 關鍵在這裏實現,onCreateViewHolder裏面帶了一個叫做viewType的參數,這個參數就是標識view的類型的
     * @param parent
     * @param viewType
     * @return
     */
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        //java.lang.Enum.ordinal()方法返回此枚舉常量的序數(其枚舉聲明中的位置,其中初始常量分配的序數爲零)。
        if(viewType==ITEM_TYPE.ITEM_TYPE_IMAGE.ordinal()){
            View view=mInflater.inflate(R.layout.item_image,parent,false);
            return new ImageViewHolder(view);
        }else {
            View view=mInflater.inflate(R.layout.item_text,parent,false);
            return new TextViewHolder(view);
        }
    }

    /**
     * 這裏判斷holder是屬於哪一個,返回對應的viewhoder
     * @param holder
     * @param position
     */
    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {

        if(holder instanceof ImageViewHolder){
            ((ImageViewHolder)holder).imageView.setImageResource(R.drawable.test);
            ((ImageViewHolder)holder).textView.setText(mTitles[position]);
        }else if(holder instanceof TextViewHolder){
            ((TextViewHolder)holder).itemText.setText(mTitles[position]);
        }

    }

    @Override
    public int getItemCount() {
        return mTitles==null ? 0 : mTitles.length;
    }

    /**
     * 關鍵方法,對應上面onCreateViewHoder裏面的newsType
     * 重寫Recycler.Adapter的getItemViewType方法
     * 這裏我們設置了單雙數區別
     * @param position
     * @return
     */
    @Override
    public int getItemViewType(int position) {
        return position%2==0 ? ITEM_TYPE.ITEM_TYPE_IMAGE.ordinal() : ITEM_TYPE.ITEM_TYPE_TEXT.ordinal();
    }

OK,基本用法介紹到這裏Over!

歡迎留言指導交流!

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