1. 程式人生 > >RecyclerView(一)最簡單的recyclerview

RecyclerView(一)最簡單的recyclerview

RecyclerView相比listview有很多靈活性,可以完全取代listview。

RecyclerView is a more advanced and flexible version of ListView. This widget is a container for large sets of views that can be recycled and scrolled very efficiently.

最簡單例子 Project: RecycleViewTest

先來看個最簡單的例子,首先在專案的依賴庫內加入recycleview的庫。

activity的layout如下

<?xml version="1.0" encoding="utf-8"?>
<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"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.fish.recycleviewtest.MainActivity">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recycle_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"></android.support.v7.widget.RecyclerView>
</RelativeLayout>

recycleview的item的佈局如下
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:background="#44ff0000"
    android:layout_height="wrap_content" >

    <TextView
        android:id="@+id/id_num"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:gravity="center"
        android:text="1" />
</FrameLayout>
MainActivity程式碼
package com.fish.recycleviewtest;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;

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

public class MainActivity extends AppCompatActivity {
    private List<String> mDatas;
    private RecyclerView recyclerView;
    private FishAdapter adapter;

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

        recyclerView = (RecyclerView) findViewById(R.id.recycle_view);
        initData();
        initRecycleView();
    }

    private void initRecycleView() {
        adapter = new FishAdapter(this);
        //必須指定adaoter
        recyclerView.setAdapter(adapter);
        //必須指定layoutmanager
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        adapter.setData(mDatas);
    }

    protected void initData() {
        mDatas = new ArrayList<String>();
        for (int i = 'A'; i < 'z'; i++) {
            mDatas.add("" + (char) i);
        }
    }
}

FishAdapter程式碼
package com.fish.recycleviewtest;

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

import java.util.List;

/**
 * Created by fish on 16/6/4.
 */
public class FishAdapter extends RecyclerView.Adapter<FishViewHolder> {
    private List<String> data;
    private LayoutInflater inflater;

    public FishAdapter(Context context) {
        inflater = LayoutInflater.from(context);
    }

    @Override
    public FishViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        LogUtil.fish("onCreateViewHolder");
        FishViewHolder holder = new FishViewHolder(inflater.inflate(
                R.layout.item, parent, false));
        return holder;
    }

    @Override
    public void onBindViewHolder(FishViewHolder holder, int position) {
        LogUtil.fish("onBindViewHolder " + position);
        holder.tv.setText(data.get(position));
    }

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

    public void setData(List<String> pDatas) {
        data = pDatas;
    }
}

FishViewHolder程式碼
package com.fish.recycleviewtest;

import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.TextView;

public class FishViewHolder extends RecyclerView.ViewHolder {

    TextView tv;

    public FishViewHolder(View view) {
        super(view);
        tv = (TextView) view.findViewById(R.id.id_num);
    }
}

最簡單的recycleview就這麼誕生了,非常簡單。效果如下


看看MainActivity的initRecycleView,為recycleview依次設定了adapter,layoutmanager,recycleview必須要設定adapter和layoutManager。adapter用來管理資料,layoutmanager用來管理各個item view的佈局,比如佈局成listview或者gridview或者瀑布流。

adapter內部的onCreateViewHolder負責為某個item建立viewholder(和listview裡面的viewholder概念一樣),在使用listview的時候我們經常還要getTag,setTag,這裡系統幫我們做了,我們只要往裡面放一個view,就得到了一個viewholder。

  FishViewHolder holder = new FishViewHolder(inflater.inflate(
                R.layout.item, parent, false));

如果沒有設定adapter,會怎麼樣呢?

可以看下這個方法RecyclerView#dispatchLayout,這個方法會在layout過程中呼叫,如果發現沒有adapter會報錯No adapter attached; skipping layout

如果發現沒有layoutManager會報錯    No layout manager attached; skipping layout

   void dispatchLayout() {
        if (mAdapter == null) {
            Log.e(TAG, "No adapter attached; skipping layout");
            // leave the state in START
            return;
        }
        if (mLayout == null) {
            Log.e(TAG, "No layout manager attached; skipping layout");
            // leave the state in START
            return;
        }
        mState.mIsMeasuring = false;
        onEnterLayoutOrScroll();
        if (mState.mLayoutStep == State.STEP_START) {
            dispatchLayoutStep1();
            mLayout.setExactMeasureSpecsFrom(this);
            dispatchLayoutStep2();
        } else if (mAdapterHelper.hasUpdates() || mLayout.getWidth() != getWidth() ||
                mLayout.getHeight() != getHeight()) {
            // First 2 steps are done in onMeasure but looks like we have to run again due to
            // changed size.
            mLayout.setExactMeasureSpecsFrom(this);
            dispatchLayoutStep2();
        } else {
            // always make sure we sync them (to ensure mode is exact)
            mLayout.setExactMeasureSpecsFrom(this);
        }
        dispatchLayoutStep3();
        onExitLayoutOrScroll();
    }


TO BE SMART

上邊的程式碼雖然沒問題,但是我們完全可以寫的更好一點,比如把和viewholder相關的東西都放到Viewholder裡面去,而不是在adapter內部處理。再比如,把recycleview的設定layoutmanager放到adapter內部(onAttachedToRecyclerView方法)去。

修改之後的程式碼如下所示

adapter的程式碼

package com.fish.recycleviewtest;

import android.content.Context;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.ViewGroup;

import java.util.List;

/**
 * Created by fish on 16/6/4.
 */
public class FishAdapter extends RecyclerView.Adapter<FishViewHolder> {
    private List<String> data;

    public FishAdapter() {

    }

    @Override
    public void onAttachedToRecyclerView(RecyclerView recyclerView) {
        super.onAttachedToRecyclerView(recyclerView);
        //必須指定layoutmanager
        recyclerView.setLayoutManager(new LinearLayoutManager(recyclerView.getContext()));
    }

    @Override
    public FishViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        LogUtil.fish("onCreateViewHolder");
        FishViewHolder holder = new FishViewHolder(parent);
        return holder;
    }

    @Override
    public void onBindViewHolder(FishViewHolder holder, int position) {
        LogUtil.fish("onBindViewHolder " + position);
        holder.bind(data.get(position));
    }

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

    public void setData(List<String> pDatas) {
        data = pDatas;
    }
}

viewholder的程式碼
package com.fish.recycleviewtest;

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

public class FishViewHolder extends RecyclerView.ViewHolder {

    TextView tv;

    public FishViewHolder(ViewGroup parent) {
        super(LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false));
        tv = (TextView) itemView.findViewById(R.id.id_num);
    }

    public void bind(String s){
        tv.setText(s);
    }
}

這樣看起來思路更清晰一點,   FishViewHolder負責所有和viewholder相關的操作,主要是一個建立一個bind,而adapter不直接操作viewholder。和recycleview初始化相關的操作放入adapter的onAttachedToRecyclerView內部,而不是放在activity裡面

多種item project:FishRecycleViewMultiType

listview支援多種item,recyclerview同樣支援,寫起來很簡單 寫2個viewholder,然後把資料新增進去,就OK了,效果如下
package com.fish.fishrecycleviewmultitype;

import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.ViewGroup;

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

/**
 * Created by fish on 16/6/4.
 */
public class MyAdapter extends RecyclerView.Adapter<FishViewHolder> {
    private List<FishData> data = new ArrayList<>();

    public static final int TYPE_TEXT = 1;
    public static final int TYPE_IMAGE = 2;

    public MyAdapter() {

    }

    @Override
    public void onAttachedToRecyclerView(RecyclerView recyclerView) {
        super.onAttachedToRecyclerView(recyclerView);
        //必須指定layoutmanager
        recyclerView.setLayoutManager(new LinearLayoutManager(recyclerView.getContext()));
    }

    @Override
    public FishViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        LogUtil.fish("onCreateViewHolder");
        switch (viewType) {
            case TYPE_TEXT:
                return new TextViewHolder(parent);
            case TYPE_IMAGE:
                return new ImageViewHolder(parent);
        }
        return null;
    }

    @Override
    public void onBindViewHolder(FishViewHolder holder, int position) {
        holder.bind(data.get(position).data);
    }


    public void addData(int type, Object o) {
        data.add(new FishData(type, o));
    }

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

    @Override
    public int getItemViewType(int position) {
        return data.get(position).type;
    }

}

package com.fish.fishrecycleviewmultitype;

import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import android.widget.TextView;

public class TextViewHolder extends FishViewHolder {

    TextView tv;

    public TextViewHolder(ViewGroup parent) {
        super(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_text, parent, false));
        tv = (TextView) itemView.findViewById(R.id.id_num);
    }


    @Override
    protected void bind(Object object) {
        String s = (String) object;
        tv.setText(s);
    }
}

package com.fish.fishrecycleviewmultitype;

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

public class ImageViewHolder extends FishViewHolder {

    ImageView tv;
    Context context;
    public ImageViewHolder(ViewGroup parent) {
        super(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_image, parent, false));
        context = parent.getContext();
        tv = (ImageView) itemView.findViewById(R.id.image);
    }


    @Override
    protected void bind(Object object) {
        int drawableId = (int) object;
        tv.setImageDrawable(context.getDrawable(drawableId));
    }
}
item_image
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="#44ff77">

    <ImageView
        android:id="@+id/image"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:gravity="center"
        android:text="1" />
</FrameLayout>
item_text
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:background="#44ff0000"
    android:layout_height="wrap_content" >

    <TextView
        android:id="@+id/id_num"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:gravity="center"
        android:text="1" />
</FrameLayout>

以後如果需要更多種類的佈局,就再新增viewholder就可以 了