Android ListView 和 RecyclerView 詳解

1、ListView

由于手機屏幕空間有限租悄,能夠一次性在屏幕上顯示的內(nèi)容并不多谨究,當(dāng)程序中有大量的數(shù)據(jù)需要展示的時候,就可以借助 ListView 來實現(xiàn)泣棋。ListView 允許用戶通過手指上下滑動的方式將屏幕外的數(shù)據(jù)滾動到屏幕內(nèi)胶哲。

(1)、ListView 的簡單用法

首先新建一個項目潭辈,修改 activity_main.xml 中的代碼鸯屿,如下:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <ListView
        android:id="@+id/list_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"></ListView>
</LinearLayout>

然后修改 MainActivity 中代碼,如下:

public class MainActivity extends AppCompatActivity {
    private String[] data = {"Apple1", "Apple2", "Apple3", "Apple4", "Apple5", "Apple6",
            "Apple7", "Apple8", "Apple9", "Apple10", "Apple11", "Apple12", "Apple13", "Apple14",
            "Apple15", "Apple16", "Apple17", "Apple18", "Apple19", "Apple20"};
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ArrayAdapter<String> adapter = new ArrayAdapter<String>(
                MainActivity.this, android.R.layout.simple_list_item_1, data
        );
        ListView listView = (ListView) findViewById(R.id.list_view);
        listView.setAdapter(adapter);
    }
}

ListView 展示的數(shù)據(jù)可以是從網(wǎng)上下載的把敢,也可以是從數(shù)據(jù)庫讀取的碾盟,根據(jù)不同的情景來設(shè)定。這里我們使用一個data數(shù)組來測試技竟。
不過數(shù)組中的數(shù)據(jù)是無法直接傳遞給 ListView 的,我們還需要借助適配器來完成屈藐,這里我們使用 ArrayAdapter榔组。它可以使用泛型來指定要適配的數(shù)據(jù)類型,然后再構(gòu)造函數(shù)中把要適配的數(shù)據(jù)傳入联逻。這里我們提供的數(shù)據(jù)是字符串,因此將 ArrayAdapter 的泛型指定為 String,然后再 ArrayAdapter 的構(gòu)造函數(shù)中依次傳入當(dāng)前上下文徽惋、ListView 子項布局的id赘理,以及要適配的數(shù)據(jù)。注意 android.R.layout.simple_list_item_1 作為 ListView 子項布局的id,這是一個 Android 內(nèi)置的布局文件换可,里面只包含一個 TextView椎椰。
最后,調(diào)用 ListView 的 setAdapter() 方法沾鳄,將構(gòu)建好的適配器對象傳遞進去慨飘。


效果圖
(2)、定制 ListView 的界面

準(zhǔn)備一組圖片译荞,分別對應(yīng)上面提供的每一種水果瓤的。
首先新建一個實體類,作為 ListView 適配器的適配類型吞歼。新建類 Fruit圈膏,F(xiàn)ruit 只包含兩個字段,name 表示名稱篙骡,imageId表示水果對應(yīng)圖片的資源id稽坤,如下:

public class Fruit {
    private String name;
    private int imageId;
    public Fruit(String name, int imageId) {
        this.name = name;
        this.imageId = imageId;
    }
    public String getName() {
        return name;
    }
    public int getImageId() {
        return imageId;
    }
}

然后為 ListView 的子項指定一個我們自定義的布局,新建 fruit_item.xml医增,在這個布局中我們定義了一個 ImageView 來顯示水果圖片慎皱,定義了一個 TextView 用于顯示水果的名稱。代碼如下:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <ImageView
        android:id="@+id/fruit_image"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
    <TextView
        android:id="@+id/fruit_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        android:layout_marginLeft="10dp" />
</LinearLayout>

接下來創(chuàng)建一個自定義的適配器叶骨,這個適配器繼承自 ArrayAdapter茫多,并將泛型真得 Fruit 類。新建類 FruitAdapter忽刽,如下:

public class FruitAdapter extends ArrayAdapter<Fruit> {
    private int resourceId;
    public FruitAdapter(Context context, int textViewResourceId,
                        List<Fruit> objects) {
        super(context, textViewResourceId, objects);
        resourceId = textViewResourceId;
    }
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        Fruit fruit = getItem(position); // 獲取當(dāng)前項的Fruit實例
        View view = LayoutInflater.from(getContext()).inflate(resourceId, parent, false);
        ImageView fruitImage = (ImageView) view.findViewById(R.id.fruit_image);
        TextView fruitName = (TextView) view.findViewById(R.id.fruit_name);
        fruitImage.setImageResource(fruit.getImageId());
        fruitName.setText(fruit.getName());
        return view;
    }
}

FruitAdapter 重寫了父類的一組構(gòu)造函數(shù)天揖,用于將上下文、ListView 子項布局的id和數(shù)據(jù)都傳遞進來跪帝。另外重寫了 getView() 方法今膊。這個方法在每個子項被滾到屏幕內(nèi)的時候會被調(diào)用。在 getView() 方法中伞剑,首先通過 getItem() 方法得到當(dāng)前項的 Fruit 實例斑唬,然后使用 LayoutInflater 來為這個子項加載我們傳入的布局。
然后修改 MainActivity 中的代碼黎泣,如下所示:

public class MainActivity extends AppCompatActivity {

    private List<Fruit> fruitList = new ArrayList<Fruit>();

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

        initFruits();
        FruitAdapter adapter = new FruitAdapter(MainActivity.this,R.layout.fruit_item,fruitList);
        ListView listView = (ListView) findViewById(R.id.list_view);
        listView.setAdapter(adapter);
    }

    private void initFruits() {
        for (int i = 0; i < 2; i++) {
            Fruit apple = new Fruit("Apple", R.drawable.apple_pic);
            fruitList.add(apple);
            Fruit banana = new Fruit("Banana", R.drawable.banana_pic);
            fruitList.add(banana);
            Fruit orange = new Fruit("Orange", R.drawable.orange_pic);
            fruitList.add(orange);
            Fruit watermelon = new Fruit("Watermelon", R.drawable.watermelon_pic);
            fruitList.add(watermelon);
            Fruit pear = new Fruit("Pear", R.drawable.pear_pic);
            fruitList.add(pear);
            Fruit grape = new Fruit("Grape", R.drawable.grape_pic);
            fruitList.add(grape);
            Fruit pineapple = new Fruit("Pineapple", R.drawable.pineapple_pic);
            fruitList.add(pineapple);
            Fruit strawberry = new Fruit("Strawberry", R.drawable.strawberry_pic);
            fruitList.add(strawberry);
            Fruit cherry = new Fruit("Cherry", R.drawable.cherry_pic);
            fruitList.add(cherry);
            Fruit mango = new Fruit("Mango", R.drawable.mango_pic);
            fruitList.add(mango);
        }
    }
}

在這里添加了一個 initFruits()方法恕刘,用于初始化所有的水果數(shù)據(jù)。創(chuàng)建 Fruit 對象抒倚,然后把創(chuàng)建好的對象添加到水果列表中褐着。接著在 onCreate() 方法中創(chuàng)建了 FruitAdapter 對象,并將 FruitAdapter 作為適配器傳遞給 ListView托呕。


效果圖
(3)含蓉、提升ListView 的運行效率

在使用 ListView 時有很多細節(jié)可以優(yōu)化频敛,其中運行效率就是很重要的一點。目前我們 ListView 的運行效率是很低的馅扣,因為在 FruitAdapter 的 getView() 方法中斟赚,每次都將布局重新加載了一遍,當(dāng) ListView 快速滾動的時候岂嗓,這就會成為性能的瓶頸汁展。
在 getView() 方法中有一個 convertView 參數(shù),這個參數(shù)用于將之前加載喊得布局進行緩存厌殉,以便之后可以進行重用食绿,修改 FruitAdapter,如下所示:

public class FruitAdapter extends ArrayAdapter<Fruit> {
    ...
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        Fruit fruit = getItem(position); // 獲取當(dāng)前項的Fruit實例
        View view;
        if (convertView == null) {
            view = LayoutInflater.from(getContext()).inflate(resourceId, parent, false);
        } else {
            view = convertView;
        }
        ImageView fruitImage = (ImageView) view.findViewById(R.id.fruit_image);
        TextView fruitName = (TextView) view.findViewById(R.id.fruit_name);
        fruitImage.setImageResource(fruit.getImageId());
        fruitName.setText(fruit.getName());
        return view;
    }
}

在getView()方法中進行了判斷公罕,如果 convertView 為null器紧,則使用 LayoutInflater 去加載布局,如果不為null則直接對 convertView 進行重用楼眷。這樣就大大提高了 ListView 的運行效率铲汪,在快速滾動的時候也可以表現(xiàn)出更好的性能。
目前雖然已經(jīng)不會再重復(fù)去加載布局罐柳,但是每次在 getView() 方法中還是會調(diào)用 View 的 findViewById() 方法來獲取一次控件的實例掌腰。我們可以借助一個 ViewHolder 來對這部分性能進行優(yōu)化修改代碼,修改代碼张吉,如下如下:

public class FruitAdapter extends ArrayAdapter<Fruit> {
    private int resourceId;

    public FruitAdapter(Context context, int textViewResourceId,
                        List<Fruit> objects) {
        super(context, textViewResourceId, objects);
        resourceId = textViewResourceId;
    }
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        Fruit fruit = getItem(position); // 獲取當(dāng)前項的Fruit實例
        View view;
        ViewHolder viewHolder;
        if (convertView == null) {
            view = LayoutInflater.from(getContext()).inflate(resourceId, parent, false);
            viewHolder = new ViewHolder();
            viewHolder.fruitImage = (ImageView) view.findViewById (R.id.fruit_image);
            viewHolder.fruitName = (TextView) view.findViewById (R.id.fruit_name);
            view.setTag(viewHolder); // 將ViewHolder存儲在View中
        } else {
            view = convertView;
            viewHolder = (ViewHolder) view.getTag(); // 重新獲取ViewHolder
        }
        viewHolder.fruitImage.setImageResource(fruit.getImageId());
        viewHolder.fruitName.setText(fruit.getName());
        return view;
    }
    class ViewHolder {

        ImageView fruitImage;

        TextView fruitName;
    }
}

我們新增了一個內(nèi)部類 ViewHolder 齿梁,用于對控件的實例進行緩存。當(dāng) convertView 為null時肮蛹,創(chuàng)建 ViewHolder 對象勺择,并將控件的實例存放在 ViewHolder 里,然后調(diào)用 View 的 setTag() 方法伦忠,將 ViewHolder 對象存儲在View 中省核。當(dāng) convertView 部null的時候,則調(diào)用View的 getTag() 方法昆码,把 ViewHolder 重新取出气忠。這樣所有控件的實例緩存在了 ViewHolder 里,就不用每次都通過 findViewById() 方法來獲取控件的實例了赋咽。

(4)旧噪、ListView 的點擊事件

修改 MainActivity 中的 onCreate() 方法,如下:

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initFruits(); // 初始化水果數(shù)據(jù)
        FruitAdapter adapter = new FruitAdapter(MainActivity.this, R.layout.fruit_item, fruitList);
        ListView listView = (ListView) findViewById(R.id.list_view);
        listView.setAdapter(adapter);
        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view,
                                    int position, long id) {
                Fruit fruit = fruitList.get(position);
                Toast.makeText(MainActivity.this, fruit.getName(), Toast.LENGTH_SHORT).show();
            }
        });
    }

我們使用 setOnItemClickListener() 方法為 ListView 注冊了一個監(jiān)聽器冬耿,當(dāng)用戶點擊了 ListView 中任何一個子項時,就會回調(diào) onItemClick() 方法萌壳。在這個方法中可以通過 position 參數(shù)判斷出用戶點擊的是哪一個子項亦镶。

2日月、RecyclerView

RecyclerView 可以說是一個增強版的ListView,不僅可以輕松實現(xiàn)和 ListView 同樣的效果缤骨,還優(yōu)化了 ListView 中存在的各種不足之處爱咬。

(1)、RecyclerView 的基本用法

和百分比布局類似绊起,RecyclerView 也屬于新增控件精拟,為了讓 RecyclerView 在所有 Android 版本上都能使用,將 RecyclerView 定義在了 support 庫當(dāng)中虱歪。因此首先要在項目的 build.gradle 中添加相應(yīng)的依賴庫才行蜂绎。

dependencies {
    ...
    implementation 'com.android.support:recyclerview-v7:24.2.1'
}

然后修改 antivity_main.xml 中的代碼,

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <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>
</LinearLayout>

將之前的 Fruit 類和 fruit_item.xml 也復(fù)制過來笋鄙。然后為 RecyclerView 準(zhǔn)備一個適配器师枣,新建 FruitAdapter 類,讓這個適配器繼承自 RecyclerView.Adapter萧落,并將泛型指定為 FruitAdapter.ViewHolder践美。其中 ViewHolder 是我們在 FruitAdapter 中定義的一個內(nèi)部類,代碼如下:

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

    private List<Fruit> mFruitList;

    static class ViewHolder extends RecyclerView.ViewHolder {
        ImageView fruitImage;
        TextView fruitName;

        public ViewHolder(View view) {
            super(view);
            fruitImage = (ImageView) view.findViewById(R.id.fruit_image);
            fruitName = (TextView) view.findViewById(R.id.fruit_name);
        }
    }

    public FruitAdapter(List<Fruit> fruitList) {
        mFruitList = fruitList;
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.fruit_item, parent, false);
        ViewHolder holder = new ViewHolder(view);
        return holder;
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        Fruit fruit = mFruitList.get(position);
        holder.fruitImage.setImageResource(fruit.getImageId());
        holder.fruitName.setText(fruit.getName());
    }

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

這里首先定義了一個內(nèi)部類 ViewHolder 找岖,ViewHolder 要繼承自 RecyclerView.ViewHolder陨倡,然后 ViewHolder 的構(gòu)造函數(shù)中要傳入一個 View 參數(shù),這個參數(shù)通常就說 RecyclerView 子項的最外層布局许布,那么我們就可以通過 findViewById() 方法來獲取到布局中的ImageView和TextView的實例了兴革。
在 FruitAdapter 的構(gòu)造函數(shù)中用于將要展示的數(shù)據(jù)源傳進來,并賦值給一個全局變量 mFruitList爹脾。由于 FruitAdapter 繼承自 RecyclerView.ViewHolder 的帖旨,就必須重寫 onCreateViewHolder()、onBindViewHolder()灵妨、getItemCount()這3個方法解阅。onCreateViewHolder() 方法用于創(chuàng)建 ViewHolder 實例,我們在這個方法中將 fruit_item 布局加載進來泌霍,然后創(chuàng)建一個 ViewHolder 實例货抄,并把加載出來的布局傳入到構(gòu)造函數(shù)當(dāng)中,最后將 ViewHolder 實例返回朱转。onBindViewHolder() 方法是用于對RecyclerView子項的數(shù)據(jù)進行賦值的蟹地,會在每個子項被滾動到屏幕內(nèi)的時候執(zhí)行,這里我們通過 position 參數(shù)得到當(dāng)前 Fruit 實例藤为,然后再將數(shù)據(jù)設(shè)置到 ViewHolder 的 ImageView 和 TetView 中怪与。getItemCount() 返回數(shù)據(jù)源的長度。
修改 MainActivity 中的代碼:

public class MainActivity extends AppCompatActivity {

    private List<Fruit> fruitList = new ArrayList<Fruit>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initFruits();
        RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
        LinearLayoutManager layoutManager = new LinearLayoutManager(this);
        recyclerView.setLayoutManager(layoutManager);
        FruitAdapter adapter = new FruitAdapter(fruitList);
        recyclerView.setAdapter(adapter);
    }

    private void initFruits() {
        for (int i = 0; i < 2; i++) {
            Fruit apple = new Fruit("Apple", R.drawable.apple_pic);
            fruitList.add(apple);
            Fruit banana = new Fruit("Banana", R.drawable.banana_pic);
            fruitList.add(banana);
            Fruit orange = new Fruit("Orange", R.drawable.orange_pic);
            fruitList.add(orange);
            Fruit watermelon = new Fruit("Watermelon", R.drawable.watermelon_pic);
            fruitList.add(watermelon);
            Fruit pear = new Fruit("Pear", R.drawable.pear_pic);
            fruitList.add(pear);
            Fruit grape = new Fruit("Grape", R.drawable.grape_pic);
            fruitList.add(grape);
            Fruit pineapple = new Fruit("Pineapple", R.drawable.pineapple_pic);
            fruitList.add(pineapple);
            Fruit strawberry = new Fruit("Strawberry", R.drawable.strawberry_pic);
            fruitList.add(strawberry);
            Fruit cherry = new Fruit("Cherry", R.drawable.cherry_pic);
            fruitList.add(cherry);
            Fruit mango = new Fruit("Mango", R.drawable.mango_pic);
            fruitList.add(mango);
        }
    }
}

這里我們同樣使用了 initFruits() 方法缅疟,用于初始化數(shù)據(jù)分别。然后在 onCreate() 方法中遍愿,我們先獲取到 RecyclerView 的實例,然后創(chuàng)建了一個 LinearLayoutManager 對象耘斩,并將它設(shè)置到 RecyclerView 當(dāng)中沼填。 LayoutManager 用于指定 RecyclerView 的布局方式,LinearLayoutManager 是線性布局的意思括授,可以實現(xiàn)和 ListView 類似的效果坞笙。接下來創(chuàng)建了 FruitAdapter 實例,并將數(shù)據(jù)傳入到其構(gòu)造函數(shù)中荚虚,最后調(diào)用 RecyclerView 的 setAdapter() 方法來完成適配器設(shè)置薛夜。


效果圖
(2)、實現(xiàn)橫向滾動和瀑布流布局

使用 RecyclerView 實現(xiàn)橫向滾動曲管。因為目前這個布局里的元素是水平排列的却邓,適用于縱項滾動的場景,如果需要實現(xiàn)橫向滾動的話院水,應(yīng)該把 fruit_item 里的元素改為成垂直排列腊徙。修改 fruit_item.xml 中的代碼:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="100dp"
    android:layout_height="wrap_content">
    <ImageView
        android:id="@+id/fruit_image"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"/>
    <TextView
        android:id="@+id/fruit_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="10dp" />
</LinearLayout>

接下來修改 MainActivity 中的代碼

public class MainActivity extends AppCompatActivity {
    ...
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initFruits();
        RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
        LinearLayoutManager layoutManager = new LinearLayoutManager(this);
        //新增
        layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);

        recyclerView.setLayoutManager(layoutManager);
        FruitAdapter adapter = new FruitAdapter(fruitList);
        recyclerView.setAdapter(adapter);
    }
    ...
}

MainActivity 中加入了一行代碼,調(diào)用 LinearLayoutManager 的 setOrientation() 方法來設(shè)置布局的排列方向檬某,默認為縱向排列撬腾,LinearLayoutManager.HORIZONTAL 表示橫向排列。


效果圖

RecyclerView 能實現(xiàn) ListView 很難或者根本不能實現(xiàn)的效果主要是因為 ListView 的布局排列是由自身去管理的恢恼,而 RecyclerView 則將這個工作交給了 LayoutManager 民傻,LayoutManager 中定制了一套可擴展的布局排列接口,子類只要按照接口的規(guī)范來實現(xiàn)场斑,就能定制出各種不同排列方式的布局了漓踢。

使用 RecyclerView 實現(xiàn)瀑布流布局。除了 LinearLayoutManager 之外 RecyclerView 還提供了 GridLayoutManager 用于實現(xiàn)網(wǎng)格布局漏隐,StaggeredGridLayoutManager 用于實現(xiàn)瀑布流布局喧半。下面實現(xiàn)瀑布流布局,修改 fruit_item.xml 中的部分屬性 :

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_margin="5dp">
    <ImageView
        android:id="@+id/fruit_image"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"/>
    <TextView
        android:id="@+id/fruit_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="left"
        android:layout_marginTop="10dp" />
</LinearLayout>

接著修改 MainActivity 中的代碼青责,如下所示:

public class MainActivity extends AppCompatActivity {

    private List<Fruit> fruitList = new ArrayList<Fruit>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initFruits();
        RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
        StaggeredGridLayoutManager layoutManager = new
                StaggeredGridLayoutManager(3, StaggeredGridLayoutManager.VERTICAL);
        recyclerView.setLayoutManager(layoutManager);
        FruitAdapter adapter = new FruitAdapter(fruitList);
        recyclerView.setAdapter(adapter);
    }

    private void initFruits() {
        for (int i = 0; i < 2; i++) {
            Fruit apple = new Fruit(getRandomLengthName("Apple"), R.drawable.apple_pic);
            fruitList.add(apple);
            Fruit banana = new Fruit(getRandomLengthName("Banana"), R.drawable.banana_pic);
            fruitList.add(banana);
            Fruit orange = new Fruit(getRandomLengthName("Orange"), R.drawable.orange_pic);
            fruitList.add(orange);
            Fruit watermelon = new Fruit(getRandomLengthName("Watermelon"), R.drawable.watermelon_pic);
            fruitList.add(watermelon);
            Fruit pear = new Fruit(getRandomLengthName("Pear"), R.drawable.pear_pic);
            fruitList.add(pear);
            Fruit grape = new Fruit(getRandomLengthName("Grape"), R.drawable.grape_pic);
            fruitList.add(grape);
            Fruit pineapple = new Fruit(getRandomLengthName("Pineapple"), R.drawable.pineapple_pic);
            fruitList.add(pineapple);
            Fruit strawberry = new Fruit(getRandomLengthName("Strawberry"), R.drawable.strawberry_pic);
            fruitList.add(strawberry);
            Fruit cherry = new Fruit(getRandomLengthName("Cherry"), R.drawable.cherry_pic);
            fruitList.add(cherry);
            Fruit mango = new Fruit(getRandomLengthName("Mango"), R.drawable.mango_pic);
            fruitList.add(mango);
        }
    }

    private String getRandomLengthName(String name) {
        Random random = new Random();
        int length = random.nextInt(20) + 1;
        StringBuilder builder = new StringBuilder();
        for (int i = 0; i < length; i++) {
            builder.append(name);
        }
        return builder.toString();
    }
}

在 onCreate() 方法中挺据,創(chuàng)建了一個 StaggeredGridLayoutManager 的實例。StaggeredGridLayoutManager 的構(gòu)造函數(shù)接收兩個參數(shù)脖隶,第一個參數(shù)用于指定布局的列數(shù)扁耐,傳入3表示3列;第二個參數(shù)用于指定布局的排列方向产阱,StaggeredGridLayoutManager.VERTICAL 表示縱向布局婉称。不過由于瀑布流布局需要各個子項高度不一致才能看出想過,所以修改 initFruits() 方法。


效果圖

使用 RecyclerView 實現(xiàn)網(wǎng)格布局王暗。修改 MainActivity 中的代碼

GridLayoutManager layoutManager=new GridLayoutManager(this,3);

可以看到GridLayoutManager需要傳遞兩個參數(shù)榨乎,一個是上下文對象,另一個是一行顯示幾列的參數(shù)常量瘫筐。通過效果圖能夠很清晰的看出網(wǎng)格布局與瀑布流布局直接的差別。


效果圖
(3)铐姚、RecyclerView 的點擊事件

不同與 ListView 的是策肝,RecyclerView 并沒有提供類似與 setOnItemClickListener() 這樣的注冊監(jiān)聽方法,而是需要我們自己給子項具體的 View 去注冊監(jiān)聽事件隐绵。
修改 FruitAdapter 中的代碼:

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

    private List<Fruit> mFruitList;

    static class ViewHolder extends RecyclerView.ViewHolder {
        View fruitView;
        ImageView fruitImage;
        TextView fruitName;

        public ViewHolder(View view) {
            super(view);
            fruitView = view;
            fruitImage = (ImageView) view.findViewById(R.id.fruit_image);
            fruitName = (TextView) view.findViewById(R.id.fruit_name);
        }
    }

    public FruitAdapter(List<Fruit> fruitList) {
        mFruitList = fruitList;
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.fruit_item, parent, false);
        final ViewHolder holder = new ViewHolder(view);
        holder.fruitView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                int position = holder.getAdapterPosition();
                Fruit fruit = mFruitList.get(position);
                Toast.makeText(v.getContext(),"View "+ fruit.getName(),Toast.LENGTH_LONG).show();
            }
        });
        holder.fruitImage.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                int position = holder.getAdapterPosition();
                Fruit fruit = mFruitList.get(position);
                Toast.makeText(v.getContext(),"Image  "+ fruit.getName(),Toast.LENGTH_LONG).show();
            }
        });
        return holder;
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        Fruit fruit = mFruitList.get(position);
        holder.fruitImage.setImageResource(fruit.getImageId());
        holder.fruitName.setText(fruit.getName());
    }

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

我們先修改了 ViewHolder之众,在 ViewHolder 中添加了 fruitView 變量來保存子項最外層的實例,然后在 onCreateViewHolder() 方法中注冊點擊事件就行了依许。這里分別為最外層布局和 ImagView 都注冊了點擊事件棺禾,。RecyclerView 可以輕松實現(xiàn)子項任意控件或布局的點擊事件峭跳。在點擊事件中膘婶,先獲取用戶點擊的 position ,然后通過 position 獲取Fruit的實例蛀醉。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末悬襟,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子拯刁,更是在濱河造成了極大的恐慌脊岳,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件垛玻,死亡現(xiàn)場離奇詭異割捅,居然都是意外死亡,警方通過查閱死者的電腦和手機帚桩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進店門亿驾,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人朗儒,你說我怎么就攤上這事颊乘。” “怎么了醉锄?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵乏悄,是天一觀的道長。 經(jīng)常有香客問我恳不,道長檩小,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任烟勋,我火速辦了婚禮规求,結(jié)果婚禮上筐付,老公的妹妹穿的比我還像新娘。我一直安慰自己阻肿,他們只是感情好瓦戚,可當(dāng)我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著丛塌,像睡著了一般较解。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上赴邻,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天印衔,我揣著相機與錄音,去河邊找鬼姥敛。 笑死奸焙,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的彤敛。 我是一名探鬼主播与帆,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼墨榄!你這毒婦竟也來了鲤桥?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤渠概,失蹤者是張志新(化名)和其女友劉穎茶凳,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體播揪,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡贮喧,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了猪狈。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片箱沦。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖雇庙,靈堂內(nèi)的尸體忽然破棺而出谓形,到底是詐尸還是另有隱情,我是刑警寧澤疆前,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布寒跳,位于F島的核電站,受9級特大地震影響竹椒,放射性物質(zhì)發(fā)生泄漏童太。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望书释。 院中可真熱鬧翘贮,春花似錦、人聲如沸爆惧。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽扯再。三九已至肴捉,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間叔收,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工傲隶, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留饺律,地道東北人。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓跺株,卻偏偏與公主長得像复濒,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子乒省,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,033評論 2 355

推薦閱讀更多精彩內(nèi)容