1. 程式人生 > >Android中利用RecyclerView實現瀑布流效果

Android中利用RecyclerView實現瀑布流效果

RecyclerView相比於傳統的ListView,功能更加強大,使用也比較方便,因此Android官方更加推薦使用RecycleView,未來也會有更多的程式逐漸從ListView轉向RecycleView。為此,首先先來了解下RecycleView的用法。當然,最先看的是效果圖:


RecycleView屬於新增的控制元件,為了讓RecyclerView在所有的Android版本上都能使用,系統將RecyclerView定義在support庫中,因此想要使用RecycleView這個控制元件,首先Android Studio要在專案的build.gradle中新增相應的依賴庫才行。eclipse的話,專案中要有V7包。

在專案的dependencies閉包中新增

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

新增完成後,在佈局檔案中新增控制元件時一定要寫全包名。如下所示:

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

和ListView一樣,複雜一點佈局都需要我們自己寫介面卡,為RecyclerView準備介面卡,讓其繼承自RecyclerView.Adapter,並將泛型指定為FruitAdapter.ViewHolder.其中ViewHolder是在FruitAdapter中定義的一個內部類,程式碼如下:
package com.example.administrator.myapplication;

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

import java.util.List;

public class FruitAdapterRecyclerView extends RecyclerView.Adapter<FruitAdapterRecyclerView.ViewHolder> {
    private List<Fruit> mFruitList;
    static  class  ViewHolder extends RecyclerView.ViewHolder{
        View fruitItemView;
        ImageView imageView;
        TextView textView;
        public ViewHolder(View itemView) {
            super(itemView);
            fruitItemView=itemView;
            imageView= (ImageView) itemView.findViewById(R.id.img);
            textView= (TextView) itemView.findViewById(R.id.tv);
        }
    }

    public FruitAdapterRecyclerView(List<Fruit> mFruitList) {
        this.mFruitList = mFruitList;
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
        View view= LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.list_item,viewGroup,false);
        final ViewHolder viewHolder=new ViewHolder(view);
        viewHolder.fruitItemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                int position=viewHolder.getAdapterPosition();
                Fruit fruit=mFruitList.get(position);
                Toast.makeText(v.getContext(),"點選的是itemview--"+fruit.getName(),Toast.LENGTH_SHORT).show();
            }
        });
        viewHolder.imageView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                int position=viewHolder.getAdapterPosition();
                Fruit fruit=mFruitList.get(position);
                Toast.makeText(v.getContext(),"點選的是image--"+fruit.getName(),Toast.LENGTH_SHORT).show();
            }
        });

        return viewHolder;
    }

    @Override
    public void onBindViewHolder(ViewHolder viewHolder, int i) {
        Fruit fruit=mFruitList.get(i);
        viewHolder.imageView.setImageResource(fruit.getImgId());
        viewHolder.textView.setText(fruit.getName());
    }

    @Override
    public int getItemCount() {
        return mFruitList.size();
    }
}
其中Fruit.java實體類程式碼如下:
package com.example.administrator.myapplication;

public class Fruit {
    private String name;
    private int imgId;

    public Fruit(String name,int imgId) {
        this.name = name;
        this.imgId = imgId;
    }

    public String getName() {
        return name;
    }

    public int getImgId() {
        return imgId;
    }
}

item的佈局如下

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_margin="5dp"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <ImageView
        android:id="@+id/img"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    <TextView
        android:id="@+id/tv"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:layout_gravity="left" />
</LinearLayout>
 剛開始的時候不容易理解,但是用的多了後,就發現比較好理解了,這裡和ListView中的介面卡類似首先定義了一個內部類ViewHolder,但是不同的是這裡的ViewHolder要繼承自RecyclerView.ViewHolder.然後在ViewHolder的建構函式中傳入View引數,這個引數通常是RecyclerView的子項ItemView的佈局,然後通過findViewById()方法來獲取佈局中的ImageView和TextView的例項。

       和ListView中的介面卡一樣,RecyclerView的介面卡也有一個建構函式,通過這個方法將要展示的資料來源傳遞進去並賦值給全域性變數list,後續的操作都是基於此資料來源進行操作的。

      由於FruitAdapter是繼承自RecyclerView.Adaper的,那麼就必須重寫onCreatViewHolder()、onBindViewHolder()和getItemCount()這3個方法,onCreatViewHolder()方法是用於建立ViewHolder例項的,我們在這個方法中將list_item的佈局載入進去。然後建立一個ViewHolder例項,並把加載出來的佈局傳入到建構函式中,最後將ViewHolder的例項返回;onBindViewHolder()方法是用於對RecyclerView子項的資料進行賦值的,會在每個子項被滾動到螢幕內的時候執行,這裡我們通過position引數,利用資料來源獲取子項的資料,然後將資料設定到子項的控制元件中;而getItemCount()方法就和ListView中的一樣了,它用於告訴RecyclerView一共有多少個子項。

     相比於ListView,RecyclerView在Activity的實現方面有個小的不同,就是多了個LayoutManager。程式碼如下:

package com.example.administrator.myapplication;

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

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

public class RecycleViewActivity extends Activity{
    List<Fruit> list=new ArrayList<>();
    RecyclerView recyclerView;
    FruitAdapterRecyclerView fruitAdapterRecyclerView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_recycler);
        initFruits();
        recyclerView= (RecyclerView) findViewById(R.id.recycler_view);
        LinearLayoutManager linearLayoutManager=new LinearLayoutManager(this);
//      linearLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
       // recyclerView.setLayoutManager(linearLayoutManager);
        StaggeredGridLayoutManager layoutManager = new StaggeredGridLayoutManager(3,StaggeredGridLayoutManager.VERTICAL);
        recyclerView.setLayoutManager(layoutManager);
        fruitAdapterRecyclerView=new FruitAdapterRecyclerView(list);
        recyclerView.setAdapter(fruitAdapterRecyclerView);
    }

    private void initFruits() {
        for (int i=0;i<3;i++){
            Fruit fruit=new Fruit(setRandomLength("apple"),R.mipmap.apple_pic);
            Fruit fruit2=new Fruit(setRandomLength("banana"),R.mipmap.banana_pic);
            Fruit fruit3=new Fruit(setRandomLength("cherry"),R.mipmap.cherry_pic);
            Fruit fruit4=new Fruit(setRandomLength("grape"),R.mipmap.grape_pic);
            Fruit fruit5=new Fruit(setRandomLength("mango"),R.mipmap.mango_pic);
            Fruit fruit6=new Fruit(setRandomLength("orange"),R.mipmap.orange_pic);
            Fruit fruit7=new Fruit(setRandomLength("pear"),R.mipmap.pear_pic);
            Fruit fruit8=new Fruit(setRandomLength("strawberry"),R.mipmap.strawberry_pic);
            list.add(fruit);
            list.add(fruit2);
            list.add(fruit3);
            list.add(fruit4);
            list.add(fruit5);
            list.add(fruit6);
            list.add(fruit7);
            list.add(fruit8);
        }
    }

    public String setRandomLength(String str){
        StringBuilder sb=new StringBuilder();
        Random random=new Random();
        int num=random.nextInt(19)+1;
        for (int i = 0; i <num; i++) {
            sb.append(str).append(" ");
        }
        return sb.toString();
    }
}
這裡我們建立了一個LinearLayoutManager物件,並將它設定到RecyclerView當中,LayoutManager用於指定RecyclerView的佈局方式,這裡如果使用LinearLayoutManager是線性佈局的意思,可以實現和ListView中類似的效果。接下來就和ListView一樣了,建立FruitAdapter例項,然後將資料傳入到建構函式中,最後呼叫RecyclerView的setAdapter()方法來完成介面卡的設定。

        當然,這裡我們要實現的是瀑布流效果,所以選用的是StaggeredGridLayoutManager來實現的,它接受兩個引數,第一個引數用於指定佈局的列數,第二個引數指定佈局的排列方向,最後還是將建立的例項設定到RecyclerView當中就行了。實現的效果如下:   

RecyclerView的點選事件

和ListView一樣,控制元件要有點選事件才可以,不同於ListView的是,RecyclerView並沒有提供類似於setOnItemClickListener()方法,採用的是點選事件都是交給具體的View去註冊處理,相比於ListView中item中控制元件的點選事件的實現的話,要方便的多,這也就是RecyclerView的強大之處,可以輕鬆實現子項中任意控制元件或佈局的點選事件。

       其中上面Adapter的第35行,註冊的是整體item的點選事件,而第43行設定的是item中控制元件的點選事件。

原始碼 點我下載