日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

Android入门(九)| 滚动控件 ListView 与 RecyclerView

發布時間:2023/12/13 Android 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Android入门(九)| 滚动控件 ListView 与 RecyclerView 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

  • ListView
    • 內置類型的簡單運用
    • 定制數據類型
    • 提升效率
    • 點擊事件
  • RecyclerView
    • 布局管理器
    • 點擊事件


ListView

內置類型的簡單運用

由于手機屏幕空間有限,能夠一次性在屏幕上顯示的內容不多,當我們的程序有大量數據需要顯示的時候就可以借助 ListView 來實現。

布局文件 listview_layout.xml :

<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"><ListViewandroid:id="@+id/list_view"android:layout_width="match_parent"android:layout_height="match_parent" /></androidx.constraintlayout.widget.ConstraintLayout>

活動文件 ListViewTest.java :

public class ListViewTest extends AppCompatActivity {private String[] data = {"Apple","Banana","Orange","Watermelon","Pear","Grape","Pineapple","Strawberry","Cherry", "Mango","Apple","Banana","Orange","Watermelon","Pear","Grape","Pineapple","Strawberry","Cherry","Mango"};// 數據無法直接傳遞給 ListView@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.listview_layout);ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, data);// 借助適配器傳遞數據ListView listView = (ListView)findViewById(R.id.list_view);listView.setAdapter(adapter);} }
  • ListView 子項布局的 id 為 android.R.layout.simple_list_item_1 ,其是一個 Android 內置的布局文件,內容只有一個 TextView,可以用于簡單地顯示一段文本。
  • ArrayAdapter 構造函數
  • 參數一:當前 Context
  • 參數二:ListView 子項布局的 id,此例中為上面提到的 TextView
  • 參數三:適配布局的數據

運行結果:


定制數據類型

如果想要每個水果的名字都對應一張圖片,那么內置的 String 類型就無法滿足需求了,因此需要自定義一個 Fruit 類:

package com.example.activitytest;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 的子項指定一個自定義布局,在 layout 目錄下新建 fruit_item.xml:

<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"android:layout_width="wrap_content"android:layout_height="wrap_content"><ImageViewandroid:id="@+id/fruit_image"android:layout_width="300dp"android:layout_height="200dp"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent"app:layout_constraintVertical_bias="0.0" /><TextViewandroid:id="@+id/fruit_name"android:layout_width="wrap_content"android:layout_height="wrap_content"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="@+id/fruit_image"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toBottomOf="@+id/fruit_image" /></androidx.constraintlayout.widget.ConstraintLayout>
  • ImageView:顯示圖片。
  • TextView :顯式名稱,文字居中。

接下來參考書上的內容創建一個自定義適配器:

public class FruitAdapter extends ArrayAdapter<Fruit> {private List<Fruit> fruitList; // 數據private int resourceId; // 子項xml布局文件public FruitAdapter(Context context, int textViewResourceId, List<Fruit> objects){super(context, textViewResourceId, objects);resourceId = textViewResourceId;fruitList = objects;}// 重寫getView方法,加載每個處于屏幕內的子項時調用public View getView(int position, View convertView, ViewGroup parent){Fruit fruit = getItem(position); // 獲取當前的 Fruit 實例,還有一種寫法:fruitList.get(position),從list中獲取單個節點元素View view = LayoutInflater.from(getContext()).inflate(resourceId, parent, false);ImageView fruitImage = (ImageView) view.findViewById(R.id.fruit_image);fruitImage.setImageResource(fruit.getImageId());TextView fruitName = (TextView) view.findViewById(R.id.fruit_name);fruitName.setText(fruitList.get(position).getName());return view;} }

構造函數 FruitAdapter 本質上還是雷同于被它 extends 的 ArrayAdapter構造函數,參數分析:

  • context:指定當前上下文。
  • textViewResourceIdListView 子項布局的 id
  • objects:適配布局的數據

加載每個處于屏幕內的子項時調用的 getView,由于我們是重寫已有函數,因此參數是固定的:

  • position:當前 Item 在屏幕中的位置,通常搭配 getItem() 返回一個子View以獲取當前實例。
  • convertView:緩存視圖 View,如果非null,則直接再次對 convertView 復用,否則才創建新的 View。
  • parent:Item 的 View 的父視圖,用處的話私以為就是為了充當 inflate 的第二個參數。

LayoutInflater 是根據 Layout XML文件 來生成 對應 View 對象 的系統服務。一般用它之作一件事: inflate(加載布局)。

inflate 方法:

public View inflate (int resource, ViewGroup root, boolean attachToRoot)
  • resource:要加載的布局對應的資源 id
  • root:在參數一對應布局外部嵌套一個父布局,如果不需要則為 null。
  • attachToRoot:是否為加載的布局添加一個 root 的外層容器
  • 為 true 時將子布局添加到父布局中并保存子布局的 layout 配置
  • 為 false 時表示只讓在子布局中聲明的 layout 屬性生效,但不將子布局添加到父布局中,此時想要將子布局添加到父布局中需要調用 addView() 方法。

上述代碼中參數三就被賦值為 false,這是因為 ListView 繼承自 AdapterView,繼承了 AdapterView 的控件是不支持 AddView() 方法的,因為一旦 resource 對應的 View 有了父布局,就不能再被添加到 ListView 中了。而參數三為 true 時會自動調用 AddView() 方法(關于inflate參數的相關信息詳見本文)。

// We are supposed to attach all the views we found (int temp) // to root. Do that now. if (root != null && attachToRoot) { // attachToRoot 為 true 時自動調用 addView 方法root.addView(temp, params); }

關于 LayoutInflater 與 inflate 可參考本文。

設置顯示的圖片和文字

  • 通過 findViewById 方法分別獲取到 ImagViewTextView 的實例
  • 分別調用它們的 setImageResource 和 setText 方法來設置顯示的圖片和文字
  • 最后將布局返回

最后修改活動文件 ListViewTest.java ,以自定義適配器為 extends ArrayAdapter<Fruit> 的情況為例:

public class ListViewTest extends AppCompatActivity {private List<Fruit> fruitList = new ArrayList<>();@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.listview_layout);initFruits(); // 初始化數據集// 借助適配器傳遞數據FruitAdapter adapter = new FruitAdapter(this, R.layout.fruit_item, fruitList);// FruitAdapter adapter = new FruitAdapter(this, fruitList);ListView listView = (ListView)findViewById(R.id.list_view);listView.setAdapter(adapter);}private void initFruits(){Fruit apple = new Fruit("Apple", R.drawable.ic_launcher_background);fruitList.add(apple);Fruit banana = new Fruit("Banana", R.drawable.ic_launcher_foreground);fruitList.add(banana);Fruit orange = new Fruit("Orange", R.drawable.cmy1);fruitList.add(orange);Fruit Watermelon = new Fruit("Watermelon", R.drawable.cmy2);fruitList.add(Watermelon);Fruit pear = new Fruit("Pear", R.drawable.cmy3);fruitList.add(pear);Fruit grape = new Fruit("Grape", R.drawable.cmy4);fruitList.add(grape);} }

提升效率

  • FruitAdapter 的 getView 方法中每次都通過 LayoutInflaterinflate 將布局重新加載了一遍, 當 ListView 快速滾動的時候就會成為性能的瓶頸。而之前提到 getView 方法中的 convertView 參數可以緩存 View,因此可以利用該參數來提高效率。
  • getView 方法中我們每次都要通過 findViewById 方法創建控件 fruit_image 和 fruit_name 的實例。不如用一個 內部類ViewHolder 來對控件實例進行緩存。
public View getView(int position, View convertView, ViewGroup parent){Fruit fruit = (Fruit) getItem(position);ViewHolder viewHolder;if(convertView == null){convertView = LayoutInflater.from(context).inflate(resourceId, parent, false);viewHolder = new ViewHolder();viewHolder.fruitImage = convertView.findViewById(R.id.fruit_image);viewHolder.fruitName = convertView.findViewById(R.id.fruit_name);convertView.setTag(viewHolder);}else{viewHolder = (ViewHolder) convertView.getTag();}viewHolder.fruitImage.setImageResource(fruit.getImageId());viewHolder.fruitName.setText(fruit.getName());return convertView;}class ViewHolder{ImageView fruitImage;TextView fruitName;}

總而言之,之前每次加載屏幕外的 子項 時都需要進行前文提到的兩種操作,但在進行改進之后,只有第一次加載(比如往下滑屏幕)的時候執行 convertView == null 的代碼才需進行前文的兩項操作,之后加載時(比如滑到底了再往上滑)就是 convertView 非空的情況了。


點擊事件

滾動只是視覺效果,子項還可以點擊。修改活動文件 ListViewTest.java :

protected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.listview_layout);initFruits();FruitAdapter adapter = new FruitAdapter(this, R.layout.fruit_item, fruitList);// FruitAdapter adapter = new FruitAdapter(this, fruitList);// 借助適配器傳遞數據ListView listView = (ListView)findViewById(R.id.list_view);listView.setAdapter(adapter);listView.setOnItemClickListener(new AdapterView.OnItemClickListener(){@Overridepublic void onItemClick(AdapterView<?> parent, View view, int position, long id) {Fruit fruit = fruitList.get(position);Toast.makeText(ListViewTest.this, fruit.getName(), Toast.LENGTH_SHORT).show();}});/* lambda表達式代替內聯函數listView.setOnItemClickListener((AdapterView<?> parent, View view, int position, long id)->{Fruit fruit = fruitList.get(position);Toast.makeText(ListViewTest.this, fruit.getName(), Toast.LENGTH_SHORT).show();});*/}
  • 使用 setOnItemClickListener 方法為 ListView 注冊了一個監聽器
  • 點擊任何一個子項時回調 onItemClick 方法,通過 position 參數獲得點擊的是哪一個子項
  • 獲取相應 Fruit 名稱并通過 Toast 輸出

RecyclerView

布局管理器

ListView性能容易變差、數據只能縱向滾動 的缺點。而 RecyclerView 就支持橫向滾動。

為了讓所有 Android 版本都能使用,RecyclerView 被定義在 support 庫中。因此,需要使用前要在項目的 build.gradle 文件中添加相應依賴庫:

修改布局文件 listview_layout.xml 中控件 ListView 為 RecyclerView,并修改布局文件名稱為 recyclerview_layout.xml:

<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"><androidx.recyclerview.widget.RecyclerViewandroid:id="@+id/recycler_view"android:layout_width="match_parent"android:layout_height="match_parent"/></androidx.constraintlayout.widget.ConstraintLayout>

子項布局文件 fruit_item.xml 和自定義的 Fruit 類無需修改,但適配器 FruitAdapter 類需要重新實現,原本 getView 方法的職責被三個方法替代:

public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.ViewHolder> {private List<Fruit> FruitList;// 構造函數public FruitAdapter(List<Fruit> fruitList){FruitList = fruitList;}// 三個繼承自父類的函數// 創建內部類實例@NonNull@Overridepublic ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.fruit_item, parent, false);return new ViewHolder(view);}// 將獲得的Fruit實例作為RecyclerView子項控件的值@Overridepublic void onBindViewHolder(@NonNull ViewHolder holder, int position) {Fruit fruit = FruitList.get(position); // 獲得子項holder.fruitImage.setImageResource(fruit.getImageId());holder.fruitName.setText(fruit.getName());}// 統計子項數目@Overridepublic int getItemCount() {return FruitList == null ? 0 : FruitList.size();}// 緩存Fruit屬性對應控件的內部類static class ViewHolder extends RecyclerView.ViewHolder{ImageView fruitImage;TextView fruitName;public ViewHolder(@NonNull View itemView) {super(itemView);fruitImage = itemView.findViewById(R.id.fruit_image);fruitName = itemView.findViewById(R.id.fruit_name);}} }

活動文件 ListViewTest.java :

public class ListViewTest extends AppCompatActivity {private List<Fruit> fruitList = new ArrayList<>();@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.recyclerview_layout);initFruits();RecyclerView recyclerView = findViewById(R.id.recycler_view);// 指定RecyclerView布局方式為線性布局LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);recyclerView.setLayoutManager(linearLayoutManager);// 設置適配器recyclerView.setAdapter(new FruitAdapter(fruitList));}private void initFruits(){for(int i = 0; i < 10; i++){Fruit fruit = new Fruit("apple", R.drawable.cmy1);fruitList.add(fruit);}} }

與 ListView 不同的是,需要傳入一個布局管理器 LinearLayoutManager 來規定 RecyclerView 是何種布局,一般有三種:

  • GridLayoutManager:網格布局
  • LinearLayoutManager:線性布局
  • StaggeredGridLayoutManager:瀑布流布局

這里以瀑布流布局為例:

StaggeredGridLayoutManager layoutManager = newStaggeredGridLayoutManager(3, StaggeredGridLayoutManager.VERTICAL);// 表示會把布局分為三列,并縱向排列 recyclerView.setLayoutManager(layoutManager);

運行結果:

這里看起來像網格布局是因為每個子項的長寬是一樣的,當長寬不一樣時就會呈現這樣的效果:

此外,還可實現橫向滾動:

protected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.recyclerview_layout);initFruits();RecyclerView recyclerView = findViewById(R.id.recycler_view);// 指定RecyclerView布局方式為線性布局LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);// 布局橫向排列以便橫向滾動linearLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);recyclerView.setLayoutManager(linearLayoutManager);// 設置適配器recyclerView.setAdapter(new FruitAdapter(fruitList));}

點擊事件

不同于 ListView,RecyclerView 沒有類似于 setOnItemClickListener 這樣的注冊監聽器方法,這需要給子項具體的 View 注冊點擊事件。

修改適配器類 FruitAdapter.java 的 onCreateViewHolder 函數,實現點擊 Fruit 子項的文字部分會彈出 Toast 文本;點擊 Fruit 子項的圖片部分會顯示大圖:

public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.fruit_item, parent, false);final ViewHolder holder = new ViewHolder(view);holder.itemView.setOnClickListener((View v)->{Toast.makeText(v.getContext(), "clicked view", Toast.LENGTH_SHORT).show();});holder.fruitImage.setOnClickListener((View v)->{// 獲取當前子項實例int position = holder.getAdapterPosition();Fruit fruit = FruitList.get(position);// 使用Dialog顯示大圖final Dialog dialog = new Dialog(v.getContext());// 設置緩存圖片的ImageView控件ImageView img = new ImageView(v.getContext());// 用當前Fruit實例的成員(圖片id)為img控件賦值img.setImageResource(fruit.getImageId());/* 也可以不使用position一連串操作,而是通過setImageDrawable將holder.fruitImage的圖片顯示到img中img.setImageDrawable(holder.fruitImage.getDrawable());*/// 設置dialog彈出內容dialog.setContentView(img);// 對話框背景為透明dialog.getWindow().setBackgroundDrawableResource(android.R.color.transparent);// 顯示dialogdialog.show();// 點擊圖片取消img.setOnClickListener((View vi)->{dialog.cancel();});});return holder;}

點擊圖片的運行結果:

總結

以上是生活随笔為你收集整理的Android入门(九)| 滚动控件 ListView 与 RecyclerView的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。