1. 程式人生 > >Android控制元件RecyclerView的基本用法

Android控制元件RecyclerView的基本用法

  RecyclerView是Android提供的一個功能強大的滾動控制元件,是增強版的ListView和GridView,不僅可以輕鬆實現和ListView相同的效果,還優化了ListView中存在的各種不足之處;目前Android官方更加推薦使用RecyclerView,本文介紹一下Android Studio中RecyclerView的基本用法。
  RecyclerView需要通過setLayoutManager()方法設定佈局管理器,RecyclerView有三個預設佈局管理器LinearLayoutManager、GridLayoutManager、StaggeredGridLayoutManager,它們都支援橫向和縱向排列以及反向滑動。如果想把RecyclerView改為橫向滑動,可以通過呼叫:
layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL); // 改變RecyclerView的方向


  RecyclerView是Android新增的控制元件,為了讓RecyclerView在所有的Android版本上都可以使用,Android團隊採取了將RecyclerView定義在support庫中,所以,我們如果要使用RecyclerView這個控制元件,首先需要在專案中的build.gradle(Module:app)中新增相應的依賴庫;
  開啟build.gradle(Module:app)檔案,在dependencies閉包中新增如下內容:

apply plugin: 'com.android.application'

android {
    compileSdkVersion 25
buildToolsVersion "25.0.2" defaultConfig { applicationId "neu.cn.myrecyclerview" minSdkVersion 15 targetSdkVersion 25 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } buildTypes { release { minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { exclude group: 'com.android.support', module: 'support-annotations' }) compile 'com.android.support:appcompat-v7:25.3.1' compile 'com.android.support:recyclerview-v7:25.3.1' testCompile 'junit:junit:4.12' }

  新增過之後點選Sync Now進行同步,同步完後,接下來修改主佈局activity_main.xml,如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:orientation="vertical"
    tools:context="neu.cn.myrecyclerview.MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <Button
            android:id="@+id/add_item"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="Add Item"
            android:textAllCaps="false" />

        <Button
            android:id="@+id/delete_item"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="Delete Item"
            android:textAllCaps="false" />

    </LinearLayout>

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

</LinearLayout>

  在佈局中加入RecyclerView控制元件,需要注意的是,由於RecyclerView並不是內建在系統SDK中的,所以需要把完整的包路徑寫出來;
  為RecyclerView新建一個介面卡類FruitAdapter,並讓其繼承自RecyclerView.Adapter,並指定其泛型為FruitAdapter.MyViewHolder,MyViewHolder是FruitAdapter的內部類;如下:

package neu.cn.myrecyclerview;

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;
import java.util.Random;

/**
 * Created by neuHenry on 2017/6/1.
 */

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

    private List<Fruit> mFruitList;

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

    @Override
    public MyViewHolder onCreateViewHolder(final ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.fruit_item, parent, false);
        final MyViewHolder holder = new MyViewHolder(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(), "you clicked fruitView " + fruit.getImageName(), Toast.LENGTH_SHORT).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(), "you clicked fruitImage " + fruit.getImageName(), Toast.LENGTH_SHORT).show();
            }
        });
        holder.fruitName.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                int position = holder.getAdapterPosition();
                Fruit fruit = mFruitList.get(position);
                Toast.makeText(v.getContext(), "you clicked fruitName " + fruit.getImageName(), Toast.LENGTH_SHORT).show();
            }
        });
        return holder;
    }

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

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

    /**
     * RecyclerView 中動態新增Item
     * @param position
     */
    public void addItem(int position) {
        Random random = new Random(mFruitList.size());
        int index = random.nextInt(mFruitList.size());
        Fruit fruit = mFruitList.get(index);
        mFruitList.add(fruit);
        notifyItemInserted(position);
    }

    /**
     * RecyclerView 中動態刪除Item
     * @param position
     */
    public void deleteItem(int position) {
        mFruitList.remove(position);
        notifyItemRemoved(position);
    }

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

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

fruit_item.xml的程式碼如下:

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

    <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>

Fruit.java的程式碼如下:

package neu.cn.myrecyclerview;

/**
 * Created by neuHenry on 2017/6/1.
 */

public class Fruit {

    private int imageID;
    private String imageName;

    public Fruit(int imageID, String imageName) {
        this.imageID = imageID;
        this.imageName = imageName;
    }

    public int getImageID() {
        return imageID;
    }

    public String getImageName() {
        return imageName;
    }
}

修改MainActivity中的程式碼,如下:

package neu.cn.myrecyclerview;

import android.content.Context;
import android.graphics.Canvas;
import android.os.Build;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.StaggeredGridLayoutManager;
import android.support.v7.widget.helper.ItemTouchHelper;
import android.util.DisplayMetrics;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;

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

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    // 水果資料列表,存放水果資料
    private List<Fruit> fruitList = new ArrayList<>();
    private RecyclerView recyclerView;
    private FruitAdapter fruitAdapter;
    // 處理RecyclerView中Item的滑動和拖拽
    private ItemTouchHelper itemTouchHelper;
    private WindowManager windowManager;

    private Button addItem;
    private Button deleteItem;
    private int screenwidth;
    private Boolean remove;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initFruits();
        fruitAdapter = new FruitAdapter(fruitList);
        remove = false;
        addItem = (Button) findViewById(R.id.add_item);
        deleteItem = (Button) findViewById(R.id.delete_item);
        windowManager = (WindowManager) MainActivity.this.getSystemService(Context.WINDOW_SERVICE);
        DisplayMetrics outMetrics = new DisplayMetrics();
        windowManager.getDefaultDisplay().getMetrics(outMetrics);
        screenwidth = outMetrics.widthPixels; // 獲取螢幕寬度
        addItem.setOnClickListener(this);
        deleteItem.setOnClickListener(this);
        itemTouchHelper = new ItemTouchHelper(new ItemTouchHelper.Callback() {

            // 用於設定拖拽和滑動的方向
            @Override
            public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
                int dragFlags = 0, swipeFlags = 0;
                if (recyclerView.getLayoutManager() instanceof StaggeredGridLayoutManager || recyclerView.getLayoutManager() instanceof GridLayoutManager) {
                    // 瀑布流和網格佈局有四個拖拽方向
                    dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
                } else if (recyclerView.getLayoutManager() instanceof LinearLayoutManager) {
                    // 線性佈局有兩個拖拽方向
                    dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
                    // 設定側滑方向為從兩個方向都可以
                    swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END;
                }
                return makeMovementFlags(dragFlags, swipeFlags);
            }

            // 長摁Item拖拽時會回撥這個方法
            @Override
            public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
                int from = viewHolder.getAdapterPosition();
                int to = target.getAdapterPosition();
                Collections.swap(fruitList, from, to); // 交換fruitList中資料的位置
                fruitAdapter.notifyItemMoved(from, to); // 更新介面卡中item的位置
                return true;
            }

            // 處理滑動刪除操作
            @Override
            public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
                fruitAdapter.deleteItem(viewHolder.getAdapterPosition());
                fruitAdapter.notifyDataSetChanged();
            }

            @Override
            public boolean isLongPressDragEnabled() {
                return true; // 返回true則為所有Item都設定可以拖拽
            }

            // 當Item拖拽開始時呼叫
            @Override
            public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
                super.onSelectedChanged(viewHolder, actionState);
                if (actionState == ItemTouchHelper.ACTION_STATE_DRAG) {
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                        viewHolder.itemView.setElevation(100);
                    }
                }
            }

            // 當Item拖拽完成時呼叫
            @Override
            public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
                super.clearView(recyclerView, viewHolder);
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                    viewHolder.itemView.setElevation(0);
                }
            }

            // 當Item檢視變化時呼叫
            @Override
            public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {

                viewHolder.itemView.scrollTo(-(int) dX, -(int) dY); // 根據Item的滑動偏移修改HorizontalScrollView的滾動
                if (Math.abs(dX) > screenwidth / 5 && !remove && isCurrentlyActive) {
                    // 使用者收滑動Item超過螢幕5分之1,標記為要刪除
                    remove = true;
                } else if (Math.abs(dX) < screenwidth / 5 && remove && !isCurrentlyActive) {
                    // 使用者收滑動Item沒有超過螢幕5分之1,標記為不刪除
                    remove = false;
                }
                if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE && remove == true && !isCurrentlyActive) {
                    // 當用戶滑動Item超過螢幕5分之1,並且鬆手時,執行刪除Item
                    if (viewHolder != null && viewHolder.getAdapterPosition() >= 0) {
                        fruitAdapter.deleteItem(viewHolder.getAdapterPosition());
                        remove = false;
                    }
                }
                super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
            }
        });

        recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
        // 與RecyclView繫結
        itemTouchHelper.attachToRecyclerView(recyclerView);
        // 線性佈局
        LinearLayoutManager layoutManager = new LinearLayoutManager(this);
//        layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL); // 改變RecyclerView的方向
        // 網格佈局
//        GridLayoutManager layoutManager = new GridLayoutManager(this, 3); // 第二個引數表示每行顯示的個數
        // 瀑布流佈局
//        StaggeredGridLayoutManager layoutManager = new StaggeredGridLayoutManager(3, StaggeredGridLayoutManager.VERTICAL);
        recyclerView.setLayoutManager(layoutManager);
        recyclerView.setAdapter(fruitAdapter);
        recyclerView.setItemAnimator(new DefaultItemAnimator()); // 設定Item載入或移除時的動畫
    }

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

    private String getRandomName(String name) {
        StringBuffer buffer = new StringBuffer();
        Random random = new Random(20);
        int length = random.nextInt(20);
        for (int i = 0; i < length; i++) {
            buffer.append(name);
        }
        return buffer.toString();
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.add_item: // RecyclerView中新增Item
                fruitAdapter.addItem(fruitList.size());
                break;
            case R.id.delete_item: // // RecyclerView中刪除Item
                fruitAdapter.deleteItem(fruitList.size() - 1);
                break;
            default:
                break;
        }
    }
}

執行,效果如下:
 這裡寫圖片描述

1、修改程式碼,使的RecyclerView實現水平滾動效果,把fruit_item.xml中的元素改成垂直排列,修改佈局的相關屬性等,如下:

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

    <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>

2、修改MainActivity的程式碼實現水平滾動,加入:
layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL); // 改變RecyclerView的方向
效果如下:
這裡寫圖片描述

3、修改MainActivity的程式碼實現網格佈局,如下:
GridLayoutManager layoutManager = new GridLayoutManager(this, 3); // 第二個引數表示每行顯示的個數
效果如下:
這裡寫圖片描述

4、再次修改MainActivity的程式碼和fruit_item的程式碼,實現瀑布流佈局,上面的MainActivity程式碼就是修改後的,瀑布流佈局需要各個子項高度不一致才能看出來,圖片大小都一樣,為此只有在TextView中多加一些字啦!效果如下所示:
這裡寫圖片描述
  RecyclerView不同於ListView的點選事件,RecyclerView不像ListView一樣提供item的點選監聽,所以需要我們自己實現給子項具體的View去註冊點選事件。RecyclerView的item點選事件監聽可以向我程式碼中為item的view設定監聽,也可以在recyclerView.addOnItemTouchListener裡去判斷手勢來實現。
  另外,程式碼後續實現了通過點選按鈕動態的新增和刪除RecyclerView中的Item子項,通過新增和刪除可以更好的理解RecyclerView的三種佈局管理器;
這裡寫圖片描述
  ItemTouchHelper是一個處理RecyclerView的滑動刪除和拖拽的輔助類,RecyclerView 的item拖拽移動和滑動刪除就靠它來實現。ItemTouchHelper需要在RecyclerView初始化的時候呼叫
itemTouchHelper.attachToRecyclerView(recyclerView);
通過attachToRecyclerView與RecyclerView 繫結後生效,如果想要為Item設定拖拽和滑動時的響應動畫效果,可以重寫ItemTouchHelper的三個方法來實現:onSelectedChanged、clearView、onChildDraw,具體的實現見程式碼;
  文中程式碼地址:MyRecyclerView