從0系統學Android--3.5 最常用和最難用的控制元件---ListView

sydMobile發表於2019-12-11

從0系統學Android-- 3.5 最常用和最難用的控制元件---ListView

從0系統學Android--3.5 最常用和最難用的控制元件---ListView

從0系統學Android--3.5 最常用和最難用的控制元件---ListView
本系列文章目錄更多精品文章分類

本系列持續更新中....

3.5 最常用和最難用的控制元件---ListView

ListView 是我們在開發中最常使用的控制元件之一。由於手機螢幕空間比較有限,能夠一次性在螢幕上顯示的內容不多,ListView 允許使用者可以通過手指上下滑動,可以呈現更多的資料。

3.5.1 ListView 的簡單使用

首先還是在 Layout 中新增 ListView

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">
    <ListView
        android:id="@+id/lv"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</LinearLayout>
複製程式碼
  private String[] data = {
            "Apple","fdsfs","fdsfssfs","wwww","wwwqqq","qqqqq","eeeee","rrrrrr","tttttt","yyyyyy","uuuuuu","aaaaaa","sssss"
    };

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_listviwe);
        ArrayAdapter<String> adapter = new ArrayAdapter<>(this,android.R.layout.simple_list_item_1,data);
        ListView listView = findViewById(R.id.lv);
        listView.setAdapter(adapter);
    }
複製程式碼

ListView 是用於展示大量資料的,因此資料需要提供好,這些往往都是從網路上或者資料庫讀取的,這裡就用假資料簡單測試一下。

資料不能直接傳遞給 ListView 需要藉助介面卡來完成。Android 中提供了許多介面卡的實現類。這裡使用最簡單的 ArrayAdapterandroid.R.layout.simple_list_itme_1 是 Android 內建的佈局檔案,裡面只有一個簡單的 TextView。然後使用 setAdapter 就將構建好的介面卡物件傳遞進去,這樣 Listview 和 資料之間的關聯就建立了。

從0系統學Android--3.5 最常用和最難用的控制元件---ListView

3.5.2 定製 LIstView 的介面

只顯示一行文字太過單調了,下面對 ListView 的介面進行定製,顯示更加複雜的內容。

定義一個實體類,作為 ListView 介面卡的適配型別。新建 Fruit

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 void setName(String name) {
        this.name = name;
    }

    public int getImgId() {
        return imgId;
    }

    public void setImgId(int imgId) {
        this.imgId = imgId;
    }
}
複製程式碼

其中 name 表示名字 imgId 對應圖片的資源id。

下面為 ListView 的子項建立一個自定義的佈局fruit_item.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="wrap_content"
    android:orientation="horizontal">
    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/iv"/>
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/tv_name"
        android:layout_marginLeft="10dp"/>
</LinearLayout>
複製程式碼

下面自己建立介面卡,這個介面卡繼承自 ArrayAdapter,並且制定泛型的型別是 Fruit 型別的。

public class FruitAdapter  extends ArrayAdapter<Fruit> {
    
    private int resourceId;
    public FruitAdapter(@NonNull Context context, int resource, @NonNull List<Fruit> objects) {
        super(context, resource, objects);
        resourceId = resource;
        
    }


    @NonNull
    @Override
    public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
        // 獲取當前項的 Fruit 例項
        Fruit fruit = getItem(position);
        View view = LayoutInflater.from(getContext()).inflate(resourceId,parent,false);
        
        TextView textView = view.findViewById(R.id.tv_name);
        ImageView imageView = view.findViewById(R.id.iv);
      	// 這裡沒有圖片,就偷懶使用了系統的
        imageView.setImageResource(R.mipmap.ic_launcher);
        textView.setText(fruit.getName());
        return view;
        
    }
}

複製程式碼

FruitAdapter 繼承 ArrayAdapter 比較簡單,只需要重寫一組構造方法和一個 getView() 方法就可以了,其他的方法 ArrayAdapter 都實現了。

getItem() 方法在每個子項被滾動到螢幕內的時候都會被呼叫。

這裡使用了 LayoutInflater 來為這個子項載入我們傳入的佈局,LayoutInflater 的 inflate 方法需要傳入三個引數,第一個引數就是要載入的佈局,第二個引數就是這個佈局要加入到這父佈局中,第三個引數指定成 false 表示只讓我們在父佈局中宣告的 layout 屬性生效,其實就是為了測量一下這個生成的佈局,但是不會將這個 View 新增到父佈局中,因為一旦 View 有了父佈局後,就不能再新增到 ListView 中了。現在不理解也沒關係,記住這是標準寫法就行了。

最後我們在 Activity 中將 ListView 與我們自己建立的介面卡繫結就可以了。

// 進行資料加工 				
initData();
        FruitAdapter fruitAdapter = new FruitAdapter(this,R.layout.fuit_item,list);
        ListView listView = findViewById(R.id.lv);
 listView.setAdapter(fruitAdapter);
複製程式碼

從0系統學Android--3.5 最常用和最難用的控制元件---ListView

3.5.3 提高 ListView 的執行效率

ListView 還有很多的細節點需要優化,不然執行效率很低。因為在 getView() 方法中每次都將佈局重新載入了一遍,當 ListView 快速滾動的時候,效能就會出現問題。

其實在 getView() 方法中還有一個 converView 引數,這個引數就是用於將之前載入好的佈局進行快取的,以便之後可以進行重複使用。修改 FruitDapter 的程式碼

    public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
        Fruit fruit = getItem(position);
        View view ;
        if (convertView == null){
            view = LayoutInflater.from(getContext()).inflate(resourceId,parent,false);
        }else {
            view = convertView;
        }
        TextView textView = view.findViewById(R.id.tv_name);
        ImageView imageView = view.findViewById(R.id.iv);
        imageView.setImageResource(R.mipmap.ic_launcher);
        textView.setText(fruit.getName());
        return view;

    }
複製程式碼

這樣我們就充分把快取的 conventView 利用起來了。

不過目前還需要優化,雖然不會再去重複的載入佈局了,但是每次 getView() 方法中還是會呼叫 ViewfindViewById() 方法來獲取一次控制元件的例項。我們可以藉助 ViewHolder 來對這部分進行效能的優化。修改程式碼

    @NonNull
    @Override
    public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
        // 獲取當前項的 Fruit 例項
        Fruit fruit = getItem(position);
        View view ;
        ViewHolder viewHolder;
        if (convertView == null){
            view = LayoutInflater.from(getContext()).inflate(resourceId,parent,false);
            viewHolder  = new ViewHolder();
            viewHolder.iv =  view.findViewById(R.id.iv);
            viewHolder.tv = view.findViewById(R.id.tv_name);
            view.setTag(viewHolder); // 將 ViewHolder 儲存在 View 中
        }else {

            view = convertView;
            viewHolder = (ViewHolder) view.getTag();
        }
        viewHolder.iv.setImageResource(R.mipmap.ic_launcher);
        viewHolder.tv.setText(fruit.getName());

        return view;

    }
    class ViewHolder{
        ImageView iv;
        TextView tv;
    }
複製程式碼

新增加一個 ViewHolder內部類,用於對控制元件的例項進行快取,當 convertView 為 null 的時候就建立一個 ViewHolder 並將它存放在 View 中。當convertView 不為 null 的時候將 ViewHolder 例項取出來。這樣就沒有必要每次都要通過 findViewById() 來獲取控制元件例項了。

通過以上兩步,ListView 就優化的非常不錯了。

3.5.4 ListView 點選事件

給 ListView 設定點選事件

    listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                // 通過 positon 我們就可以確定是點選的哪一個子項
            }
        });
複製程式碼

相關文章