1. 程式人生 > 其它 >Android控制元件 RecyclerView簡單使用

Android控制元件 RecyclerView簡單使用

技術標籤:#Android筆記android

RecyclerView 很強大很好用,直接開整。

一、RecyclerView 準備工作

  1. Gradle 中引入 dependencies 閉包
dependencies {
	implementation 'androidx.recyclerview:recyclerview:1.1.0'
}
  1. 修改 activity_main.xml,配置按鍵 和 RecyclerView 控制元件 layout
<?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=".MainActivity"
>
<Button android:layout_width="match_parent" android:layout_height="wrap_content" android:text="新增資料" android:onClick="onAddDataClick"/> <Button android:layout_width="match_parent" android:
layout_height
="wrap_content" android:text="切換佈局" android:onClick="onChangeLayoutClick"/>
<Button android:layout_width="match_parent" android:layout_height="wrap_content" android:text="插入一條資料" android:onClick="onInsertDataClick"/> <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:text="刪除一條資料" android:onClick="onRemoveDataClick"/> <androidx.recyclerview.widget.RecyclerView android:id="@+id/recycler_view" android:layout_width="match_parent" android:layout_height="match_parent"> </androidx.recyclerview.widget.RecyclerView> </LinearLayout>
  1. 新建一個類繼承 RecyclerView.Adapter
  2. 並且建立一個內部類 MyViewHolder 傳入 RecyclerView.Adapter 泛型
  3. 重寫 MyRecyclerViewAdapter 抽象方法
public class MyRecyclerViewAdapter extends 
				RecyclerView.Adapter<MyRecyclerViewAdapter.MyViewHolder> {

	public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        return null;
    }

    @Override
    public void onBindViewHolder(@NonNull MyViewHolder holder,final int position) {
    
    }

    @Override
    public int getItemCount() {
    }
    
	//建立一個MyViewHolder繼承RecyclerView.ViewHolder
    class MyViewHolder extends RecyclerView.ViewHolder {
    
        public MyViewHolder(@NonNull View itemView) {
            super(itemView);
        }
    }
}

二、配置 Adapter 內部方法

  1. 新建一個 layout_item.xml 用於作 Adapter 的 item
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal" android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_margin="5dp"
    android:background="#A4D3EE">

    <ImageView
        android:id="@+id/image_view"
        android:layout_width="88dp"
        android:layout_height="88dp"
        android:scaleType="fitXY"
        />

    <TextView
        android:id="@+id/text_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:textSize="22sp"
        android:textColor="#FFF"
        android:gravity="center"
        android:layout_marginStart="8dp"/>

</LinearLayout>
  1. 載入佈局,建立一個 ViewHolder
	//建立並且返回 ViewHolder
	@NonNull
    @Override
    public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        //建立layout.layout_item,繫結View,返回ViewHolder給onBindViewHolder
        View view = LayoutInflater.from(mContext).inflate(R.layout.layout_item,
                parent, false);
        MyViewHolder holder = new MyViewHolder(view);
        return holder;
    }
  1. 從 ViewHolder 中 findViewById
	//建立一個MyViewHolder繼承RecyclerView.ViewHolder
    class MyViewHolder extends RecyclerView.ViewHolder {

        TextView textView;
        ImageView imageView;

        //這裡提出MyViewHolder中的View來做點選事件處理
        View itemView;

        //onCreateViewHolder中建立MyViewHolder物件,傳入View
        public MyViewHolder(@NonNull View itemView) {
            super(itemView);
            imageView = itemView.findViewById(R.id.image_view);
            textView = itemView.findViewById(R.id.text_view);
            this.itemView = itemView;
        }
    }
  1. 繫結資料
	//繫結資料
    @Override
    public void onBindViewHolder(@NonNull MyViewHolder holder,final int position) {
        //4.通過onCreateViewHolder傳來的ViewHolder來進行資料來源設定
        holder.textView.setText(dataSource.get(position));
        holder.imageView.setImageResource(getIcon(position));
    }
  1. 建立資料來源
    private final Context mContext;
    private List<String> dataSource;
    
	//或者layout_item佈局設定為vertical,然後隨機textView字串的長度
    public MyRecyclerViewAdapter(Context context) {
        //初始化資料來源,否則getItemCount會出現空指標
        this.dataSource = new ArrayList<>();
        this.mContext = context;
    }
	//設定資料來源
    public void setDataSource(List<String> dataSource) {
        this.dataSource = dataSource;
        notifyDataSetChanged();
    }
	//這裡使用自定義的五張圖片迴圈使用
	private int getIcon (int position) {
        switch (position % 5) {
            case 0:
                return R.mipmap.a;
            case 1:
                return R.mipmap.b;
            case 2:
                return R.mipmap.c;
            case 3:
                return R.mipmap.d;
            case 4:
                return R.mipmap.e;
        }
        return 0;
    }
  1. 返回資料量
	//返回資料量
    @Override
    public int getItemCount() {
        //5.返回資料總量
        return dataSource.size();
    }

三、顯示資料

  1. MainActivity 中給 RecyclerView 配置 Adapter
public class MainActivity extends AppCompatActivity {

    private RecyclerView mRecyclerView;
    private MyRecyclerViewAdapter mAdapter;
    private List<String> mDataSource = new ArrayList<>();

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

        mRecyclerView = findViewById(R.id.recycler_view);

        //LayoutManager用於指定RecyclerView的佈局方式
        //LinearLayoutManager為線性佈局
        LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);

        //設定RecyclerView的佈局
        mRecyclerView.setLayoutManager(linearLayoutManager);

        mAdapter = new MyRecyclerViewAdapter(this);
        mRecyclerView.setAdapter(mAdapter);
    }

    //使用按鍵建立並設定資料來源
    public void onAddDataClick(View view) {
        for (int i = 0; i < 20; i++) {
            String s = "第" + i + "條資料";
            mDataSource.add(s);
        }
        mAdapter.setDataSource(mDataSource);
    }
}
  1. 橫向顯示資料,需要在載入 Adapter 之前配置 linearLayoutManager
		...
		//橫向排列ItemView,最好修改layout_item為vertical
        linearLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
        //資料反向展示(從右向左滑動)
        linearLayoutManager.setReverseLayout(true);
        ...
  1. 執行結果
    在這裡插入圖片描述

四、切換佈局

  1. RecyclerView 除了 LinearLayout 外,還有 GridLayout 網格佈局和 StaggeredGridLayout 瀑布流佈局
    GridLayout 在這裡插入圖片描述

  2. 修改 MyRecyclerViewAdapter 構造方法, 新增傳入mRecyclerView用來設定textView隨機高度

	private RecyclerView mRecyclerView;
	//引數2 傳入mRecyclerView用來設定textView隨機高度;
    //或者layout_item佈局設定為vertical,然後隨機textView字串的長度
    public MyRecyclerViewAdapter(Context context, RecyclerView mRecyclerView) {
        //初始化資料來源,否則getItemCount會出現空指標
        this.dataSource = new ArrayList<>();
        this.mContext = context;
        this.mRecyclerView = mRecyclerView;
    }
  1. 隨機控制元件高度
	//獲取TextView隨機高度
    private int getRandomHeight() {
        return (int)(Math.random() * 1000);
    }
  1. 在 onBindViewHolder 方法增加條件判斷
	@Override
    public void onBindViewHolder(@NonNull MyViewHolder holder,final int position) {
		...
        //如果是瀑布流就設定隨機高度
        if (mRecyclerView.getLayoutManager().getClass() == StaggeredGridLayoutManager.class) {
            LinearLayout.LayoutParams params
                    = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getRandomHeight());
            holder.textView.setLayoutParams(params);
        } else {
            LinearLayout.LayoutParams params
                    = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
            holder.textView.setLayoutParams(params);
        }
    }
  1. 修改 MainActivity 中的方法
	protected void onCreate(Bundle savedInstanceState) {
		...
		//onCreate方法中修改
		mAdapter = new MyRecyclerViewAdapter(this, mRecyclerView);
		...
	}
	//改變mAdapter的佈局方式
    public void onChangeLayoutClick(View view) {
        //從線性佈局 --> 網格佈局
        if (mRecyclerView.getLayoutManager().getClass() == LinearLayoutManager.class) {
            GridLayoutManager gridLayoutManager = new GridLayoutManager(this, 2);
            mRecyclerView.setLayoutManager(gridLayoutManager);
        }
        //網格佈局 --> 瀑布流佈局
        else if (mRecyclerView.getLayoutManager().getClass() == GridLayoutManager.class) {
            StaggeredGridLayoutManager staggeredGridLayoutManager
                    = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);
            mRecyclerView.setLayoutManager(staggeredGridLayoutManager);
        }
        //瀑布流佈局 --> 線性佈局
        else if (mRecyclerView.getLayoutManager().getClass() == StaggeredGridLayoutManager.class) {
            LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
            mRecyclerView.setLayoutManager(linearLayoutManager);
        }
    }

五、新增點選事件

  1. MyRecyclerViewAdapter 中建立 ItemView 點選事件回撥介面
public class MyRecyclerViewAdapter extends 
				RecyclerView.Adapter<MyRecyclerViewAdapter.MyViewHolder> {
	private OnItemClickListener onItemClickListener;
    private OnImageClickListener onImageClickListener;
    
	...
	
	//ItemView點選事件回撥介面
    interface OnItemClickListener {
        void onItemClick(int position);
    }

    interface OnImageClickListener {
        void onItemClick(int position);
    }

    public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
        this.onItemClickListener = onItemClickListener;
    }

    public void setOnImageClickListener(OnImageClickListener onImageClickListener) {
        this.onImageClickListener = onImageClickListener;
    }
}
  1. 在 onBindViewHolder 中使用回撥方法
    @Override
    public void onBindViewHolder(@NonNull MyViewHolder holder,final int position) {
    
		...
		
        //點選事件處理
        holder.itemView.setOnClickListener(v -> {
            if (onItemClickListener != null) {
                //這裡使用回撥方法,具體怎麼使用還得從MainActivity中重寫
                onItemClickListener.onItemClick(position);
            }
        });

        holder.imageView.setOnClickListener(v -> {
            if (onImageClickListener != null) {
                //這裡使用回撥方法,具體怎麼使用還得從MainActivity中重寫
                onImageClickListener.onItemClick(position);
            }
        });
    }
  1. 或者在 onCreateViewHolder 中使用回撥方法
    @NonNull
    @Override
    public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        //建立layout.layout_item,繫結View,返回ViewHolder給onBindViewHolder
        View view = LayoutInflater.from(mContext).inflate(R.layout.layout_item,
                parent, false);
        MyViewHolder holder = new MyViewHolder(view);

        //onClick方法寫在onCreateViewHolder中
        holder.imageView.setOnClickListener(v -> {
            int position = holder.getAdapterPosition();
            if (onItemClickListener != null) {
                //這裡使用回撥方法,具體怎麼使用還得從MainActivity中重寫
                onItemClickListener.onItemClick(position);
            }
        });
        return holder;
    }
  1. MainActivity 重寫回調方法
	@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        ...
        
        mAdapter.setOnItemClickListener(position -> {
		Toast.makeText(MainActivity.this, "第" + position + "資料被點選", Toast.LENGTH_SHORT).show();
        });
        
        mAdapter.setOnImageClickListener(position -> {
            Toast.makeText(MainActivity.this, "第" + position + "圖片被點選", Toast.LENGTH_SHORT).show();
        });
    }

六、增加 \ 刪除 Item

  1. MyRecyclerViewAdapter 中增加方法
//新增的資料位置,用來改變新加資料的背景色
    private int addDataPosition = -1;
    
    //新增一條資料
    public void addData(int position) {
        addDataPosition = position;
        dataSource.add(position, "新增的資料");
        //通知插入資料
        notifyItemInserted(position);
        //通知資料長度變更
        // 引數1:是起始位置,從哪裡開始更新,引數2:更新的總數
        notifyItemRangeChanged(position, dataSource.size() - position);
        //新增資料時滾動到第一行
        mRecyclerView.smoothScrollToPosition(position);
    }

    //刪除一條資料
    public void deleteData(int position){
        addDataPosition = -1;
        dataSource.remove(position);
        //通知插入資料
        notifyItemRemoved(position);
        //通知資料長度變更
        // 引數1:是起始位置,從哪裡開始更新,引數2:更新的總數
        notifyItemRangeChanged(position, dataSource.size() - position);
    }
  1. 修改 onBindViewHolder 中的程式碼
	public void onBindViewHolder(@NonNull MyViewHolder holder,final int position) {
		...
        //改變新新增的顏色
        if (addDataPosition == position) {
            holder.itemView.setBackgroundColor(Color.RED);
        } else {
            holder.itemView.setBackgroundColor(Color.parseColor("#A4D3EE"));
        }
    }
  1. MainActivity 中增加方法
	public void onRemoveDataClick(View view) {
        mAdapter.deleteData(0);
    }

    public void onInsertDataClick(View view) {
        mAdapter.addData(0);
    }

在這裡插入圖片描述

七、配置動畫 setItemAnimator

可以參考學習:
https://www.jianshu.com/p/2a82b0341138

八、所有程式碼預覽

MyRecyclerViewAdapter

/**
 * 1、繼承RecyclerView.Adapter
 * 2、繫結ViewHolder
 * 3、實現Adapter的相關方法
 */
public class MyRecyclerViewAdapter extends RecyclerView.Adapter<MyRecyclerViewAdapter.MyViewHolder> {

    private final Context mContext;
    private List<String> dataSource;
    private RecyclerView mRecyclerView;

    private OnItemClickListener onItemClickListener;
    private OnImageClickListener onImageClickListener;

    //新增的資料位置,用來改變新加資料的背景色
    private int addDataPosition = -1;

    //引數2 傳入mRecyclerView用來設定textView隨機高度;
    //或者layout_item佈局設定為vertical,然後隨機textView字串的長度
    public MyRecyclerViewAdapter(Context context, RecyclerView mRecyclerView) {
        //初始化資料來源,否則getItemCount會出現空指標
        this.dataSource = new ArrayList<>();
        this.mContext = context;
        this.mRecyclerView = mRecyclerView;
    }

    //設定資料來源
    public void setDataSource(List<String> dataSource) {
        this.dataSource = dataSource;
        notifyDataSetChanged();
    }

    //獲取圖片資料
    private int getIcon (int position) {
        switch (position % 5) {
            case 0:
                return R.mipmap.a;
            case 1:
                return R.mipmap.b;
            case 2:
                return R.mipmap.c;
            case 3:
                return R.mipmap.d;
            case 4:
                return R.mipmap.e;
        }
        return 0;
    }

    //獲取TextView隨機高度
    private int getRandomHeight() {
        return (int)(Math.random() * 1000);
    }

    //建立並且返回 ViewHolder
    @NonNull
    @Override
    public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        //2.建立layout.layout_item,繫結View,返回ViewHolder給onBindViewHolder
        View view = LayoutInflater.from(mContext).inflate(R.layout.layout_item,
                parent, false);
        MyViewHolder holder = new MyViewHolder(view);

        //7.onClick方法寫在onCreateViewHolder中
//        holder.imageView.setOnClickListener(v -> {
//            int position = holder.getAdapterPosition();
//            if (onItemClickListener != null) {
//                //這裡使用回撥方法,具體怎麼使用還得從MainActivity中重寫
//                onItemClickListener.onItemClick(position);
//            }
//        });
        return holder;
    }
    
    //繫結資料
    @Override
    public void onBindViewHolder(@NonNull MyViewHolder holder,final int position) {
        //4.通過onCreateViewHolder傳來的ViewHolder來進行資料來源設定
        holder.textView.setText(dataSource.get(position));
        holder.imageView.setImageResource(getIcon(position));

        //8.改變新新增的顏色
        if (addDataPosition == position) {

            holder.itemView.setBackgroundColor(Color.RED);
        } else {
            holder.itemView.setBackgroundColor(Color.parseColor("#A4D3EE"));
        }
        
        //6.如果是瀑布流就設定隨機高度
        if (mRecyclerView.getLayoutManager().getClass() == StaggeredGridLayoutManager.class) {
            LinearLayout.LayoutParams params
                    = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getRandomHeight());
            holder.textView.setLayoutParams(params);
        } else {
            LinearLayout.LayoutParams params
                    = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
            holder.textView.setLayoutParams(params);
        }
        
        //7.點選事件處理
        holder.itemView.setOnClickListener(v -> {
            if (onItemClickListener != null) {
                //這裡使用回撥方法,具體怎麼使用還得從MainActivity中重寫
                onItemClickListener.onItemClick(position);
            }
        });

        holder.imageView.setOnClickListener(v -> {
            if (onImageClickListener != null) {
                //這裡使用回撥方法,具體怎麼使用還得從MainActivity中重寫
                onImageClickListener.onItemClick(position);
            }
        });

    }

    //返回資料量
    @Override
    public int getItemCount() {
        //5.返回資料總量
        return dataSource.size();
    }

    //1.建立一個MyViewHolder繼承RecyclerView.ViewHolder
    class MyViewHolder extends RecyclerView.ViewHolder {

        TextView textView;
        ImageView imageView;

        //7.這裡提出MyViewHolder中的View來做點選事件處理
        View itemView;

        //3.onCreateViewHolder中建立MyViewHolder物件,傳入View
        public MyViewHolder(@NonNull View itemView) {
            super(itemView);
            imageView = itemView.findViewById(R.id.image_view);
            textView = itemView.findViewById(R.id.text_view);
            this.itemView = itemView;
        }
    }

    //7.ItemView點選事件回撥介面
    interface OnItemClickListener {
        void onItemClick(int position);
    }

    interface OnImageClickListener {
        void onItemClick(int position);
    }

    public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
        this.onItemClickListener = onItemClickListener;
    }

    public void setOnImageClickListener(OnImageClickListener onImageClickListener) {
        this.onImageClickListener = onImageClickListener;
    }

    //8.新增一條資料
    public void addData(int position) {
        addDataPosition = position;
        dataSource.add(position, "新增的資料");
        //通知插入資料
        notifyItemInserted(position);
        //通知資料長度變更
        // 引數1:是起始位置,從哪裡開始更新,引數2:更新的總數
        notifyItemRangeChanged(position, dataSource.size() - position);
        mRecyclerView.smoothScrollToPosition(position);
    }

    //8.刪除一條資料
    public void deleteData(int position){
        addDataPosition = -1;
        dataSource.remove(position);
        //通知插入資料
        notifyItemRemoved(position);
        //通知資料長度變更
        // 引數1:是起始位置,從哪裡開始更新,引數2:更新的總數
        notifyItemRangeChanged(position, dataSource.size() - position);
    }
}

MainActivity

public class MainActivity extends AppCompatActivity {

    private RecyclerView mRecyclerView;
    private MyRecyclerViewAdapter mAdapter;
    private List<String> mDataSource = new ArrayList<>();

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

        mRecyclerView = findViewById(R.id.recycler_view);

        //LayoutManager用於指定RecyclerView的佈局方式
        //LinearLayoutManager為線性佈局
        LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);

        //橫向排列ItemView,最好修改layout_item為vertical
        //linearLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
        //資料反向展示
        //linearLayoutManager.setReverseLayout(true);

        //設定RecyclerView的佈局
        mRecyclerView.setLayoutManager(linearLayoutManager);

        mAdapter = new MyRecyclerViewAdapter(this, mRecyclerView);
        mRecyclerView.setAdapter(mAdapter);

        //mRecyclerView.setItemAnimator(new DefaultItemAnimator());

//        mAdapter.setOnItemClickListener(position -> {
//            Toast.makeText(MainActivity.this, "第" + position + "資料被點選", Toast.LENGTH_SHORT).show();
//        });

        mAdapter.setOnImageClickListener(position -> {
            Toast.makeText(MainActivity.this, "第" + position + "圖片被點選", Toast.LENGTH_SHORT).show();
        });
    }

    public void onRemoveDataClick(View view) {
        mAdapter.deleteData(0);
    }

    public void onInsertDataClick(View view) {
        mAdapter.addData(0);
    }

    //改變mAdapter的佈局方式
    public void onChangeLayoutClick(View view) {
        //從線性佈局 --> 網格佈局
        if (mRecyclerView.getLayoutManager().getClass() == LinearLayoutManager.class) {
            GridLayoutManager gridLayoutManager = new GridLayoutManager(this, 2);
            mRecyclerView.setLayoutManager(gridLayoutManager);
        }
        //網格佈局 --> 瀑布流佈局
        else if (mRecyclerView.getLayoutManager().getClass() == GridLayoutManager.class) {
            StaggeredGridLayoutManager staggeredGridLayoutManager
                    = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);
            mRecyclerView.setLayoutManager(staggeredGridLayoutManager);
        }
        //瀑布流佈局 --> 線性佈局
        else if (mRecyclerView.getLayoutManager().getClass() == StaggeredGridLayoutManager.class) {
            LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
            mRecyclerView.setLayoutManager(linearLayoutManager);
        }
    }

    //建立並設定資料來源
    public void onAddDataClick(View view) {
        for (int i = 0; i < 20; i++) {
            String s = "第" + i + "條資料";
            mDataSource.add(s);
        }
        mAdapter.setDataSource(mDataSource);
    }
}