【Android】11.0 UI開發(二)——列表控制元件ListView的簡單實現1
本篇內容看不懂的請先看上一篇博文寫的:
連結: 【Android】10.0 UI開發(一)——如何編寫程式介面、常見控制元件的使用
1.0 內容涉及特別多,寫的原因也是給自己捋思路。ListView使用好麻煩。
2.0 先看最終的執行結果:

image

image
3.0 第一介面是作為自己熟悉<ConstraintLayout>標籤,練手做的,體會如下:
Guideline控制元件超級有用,它會提供一個可以設定百分比的虛線,其他控制元件可以基於這個控制元件來擺放,這樣可以解決不同機型介面不相容的問題。下面是我的手機(華為榮耀play)執行的畫面:

image
3.1 這個介面總共加了6個控制元件:
ImageView :2張圖片
EditText :2個文字輸入框,雖然一個是密碼輸入框,但是因為學習資料沒有介紹,就沒用先。
Button :2個按鈕控制元件,剩下的都是輔助線,<Guideline>元件相當於一個強制隱藏的view塊控制元件:

image
這裡加了9條輔助線,列的分別是25%,50%,75%,行的分別是30%,50%,70%
之前控制元件的大小控制百度到只能用權重,可是在這個新的ConstraintLayout標籤中,權重沒有意義……
因為Guideline控制元件是下面語句控制百分比(30%):
app:layout_constraintGuide_percent="0.3"
沒有在其他控制元件嘗試過這行語句在其他控制元件能不能用。
3.2 新建一個專案,名字叫什麼無所謂,我的叫“Portable_Antitheft”
檔案目錄如下:

image
這裡面我的上一篇博文也說了“ActivityCollector”類和“BaseActivity”類的作用,可以無視,也不貼程式碼了,作用一個是提供活動的一次性銷燬方法,一個是隨時監控返回棧棧頂的活動是什麼,便於除錯。
其中新建資料夾“.../res/drawable-xhdpi”,裡面是這個程式開發所用的所有圖片。

image

image
3.3 先看第一個佈局檔案的程式碼:
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <ImageView android:id="@+id/imageView" android:layout_width="0dp" android:layout_height="0dp" android:background="@drawable/background" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.0" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_bias="0.0" /> <ImageView android:id="@+id/imageView2" android:layout_width="149dp" android:layout_height="140dp" android:layout_marginStart="8dp" android:layout_marginTop="8dp" android:layout_marginEnd="8dp" android:layout_marginBottom="8dp" android:src="@drawable/logo" app:layout_constraintBottom_toTopOf="@+id/guideline2" app:layout_constraintEnd_toStartOf="@+id/guideline" app:layout_constraintHorizontal_bias="0.503" app:layout_constraintStart_toStartOf="@+id/guideline" app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_bias="0.0" /> <EditText android:id="@+id/username" android:layout_width="0dp" android:layout_height="wrap_content" android:hint="郵箱/手機號" android:maxLines="1" app:layout_constraintBottom_toTopOf="@+id/guideline9" app:layout_constraintEnd_toStartOf="@+id/guideline7" app:layout_constraintHorizontal_bias="0.0" app:layout_constraintStart_toStartOf="@+id/guideline6" app:layout_constraintTop_toTopOf="@+id/guideline8" app:layout_constraintVertical_bias="0.161" /> <EditText android:id="@+id/passward" android:layout_width="0dp" android:layout_height="wrap_content" android:hint="密碼" android:maxLines="1" app:layout_constraintBottom_toTopOf="@+id/guideline2" app:layout_constraintEnd_toStartOf="@+id/guideline7" app:layout_constraintHorizontal_bias="0.0" app:layout_constraintStart_toStartOf="@+id/guideline6" app:layout_constraintTop_toBottomOf="@+id/username" app:layout_constraintVertical_bias="0.09" /> <android.support.constraint.Guideline android:id="@+id/guideline" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical" app:layout_constraintGuide_percent="0.5" /> <android.support.constraint.Guideline android:id="@+id/guideline2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" app:layout_constraintGuide_percent="0.5" /> <Button android:id="@+id/loginbutton" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginBottom="8dp" android:text="登錄" app:layout_constraintBottom_toTopOf="@+id/guideline9" app:layout_constraintEnd_toStartOf="@+id/guideline7" app:layout_constraintHorizontal_bias="0.0" app:layout_constraintStart_toStartOf="@+id/guideline6" app:layout_constraintTop_toTopOf="@+id/guideline2" app:layout_constraintVertical_bias="0.2" /> <Button android:id="@+id/registerbutton" android:layout_width="0dp" android:layout_height="wrap_content" android:text="注冊" app:layout_constraintEnd_toStartOf="@+id/guideline7" app:layout_constraintStart_toStartOf="@+id/guideline6" app:layout_constraintTop_toBottomOf="@+id/loginbutton" /> <android.support.constraint.Guideline android:id="@+id/guideline6" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical" app:layout_constraintGuide_percent="0.25" /> <android.support.constraint.Guideline android:id="@+id/guideline7" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical" app:layout_constraintGuide_percent="0.75" /> <android.support.constraint.Guideline android:id="@+id/guideline8" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" app:layout_constraintGuide_percent="0.3" /> <android.support.constraint.Guideline android:id="@+id/guideline9" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" app:layout_constraintGuide_percent="0.7" /> </android.support.constraint.ConstraintLayout>
這裡用了一張普通圖片當成背景圖,也是通過xml檔案的特性,簡單點說,程式碼的先後順序決定圖片的疊加順序。
這裡手動改的只有一個id和app:layout_constraintGuide_percent="0.什麼",因為輔助線加的時候預設多少dp,其他實現全靠Design中滑鼠的拖拽。下面圖解裡面的內容,便於理解:

image
4.0 MainActivity.java
package com.example.portable_anti_theft; import android.content.Intent; import android.support.v7.app.ActionBar; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.view.Window; import android.widget.Button; public class MainActivity extends BaseActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.activity_main); //隱藏系統標題欄 ActionBar actionbar = getSupportActionBar(); if (actionbar != null){ actionbar.hide(); } //以下程式碼也可行,實現同樣的效果 //if (getSupportActionBar() != null){ //getSupportActionBar().hide(); //} Button loginbutton = (Button) findViewById(R.id.loginbutton); loginbutton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(MainActivity.this,Main2Activity.class); startActivity(intent); } }); } }
這裡簡單的實現了兩個功能:去掉了系統標題欄,再簡單對“登入”按鈕做了一個跳轉,跳到第二個活動介面。
5.0 重點來了,activity_main2.xml:
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".Main2Activity"> <include android:id="@+id/include" layout="@layout/title" /> <ListView android:id="@+id/list_view" android:layout_width="wrap_content" android:layout_height="0dp" app:layout_constraintBottom_toBottomOf="@+id/include" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.0" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="@+id/guideline3" app:layout_constraintVertical_bias="1.0" /> <android.support.constraint.Guideline android:id="@+id/guideline3" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" app:layout_constraintGuide_percent="0.08" /> </android.support.constraint.ConstraintLayout>
不要問我(o(╥﹏╥)o)程式碼什麼意思,好些自己也沒看懂,全靠拖拽。

image
這裡的ListView控制元件最上面的邊邊,約束於8%的輔助線,這樣就不會蓋住線上面的東西——自制標題欄。大家看到原始碼裡面總共才插入三個標籤,第二個是滾動列表,第三個是輔助線,第一個是重點,劃線的那種。
<include android:id="@+id/include" layout="@layout/title" />
先不管這行程式碼,我們作為一個自創標題欄的尊嚴,首先不能每個介面都寫一個自己的控制元件吧,當然希望只要想用自創標題欄的時候簡單載入一下最好,也能更好的降低程式碼的耦合性。
在res/layout資料夾中新建title.xml佈局檔案,右擊layout資料夾,new→Layout Resource File,
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent"> <Button android:id="@+id/title_back" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="5dp" android:layout_marginStart="8dp" android:layout_marginTop="4dp" android:layout_marginEnd="8dp" android:layout_marginBottom="8dp" android:gravity="center" android:text="後退" android:textColor="#ffffff" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toStartOf="@+id/title_edit" app:layout_constraintHorizontal_bias="0.0" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_bias="0.0" /> <TextView android:id="@+id/title_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="8dp" android:layout_marginTop="8dp" android:layout_marginEnd="8dp" android:layout_marginBottom="8dp" android:gravity="center" android:text="隨身監控" android:textColor="#6A8759" android:textSize="24sp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="@+id/title_edit" app:layout_constraintHorizontal_bias="0.506" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_bias="0.0" /> <Button android:id="@+id/title_edit" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="5dp" android:layout_marginTop="8dp" android:layout_marginEnd="16dp" android:layout_marginBottom="8dp" android:gravity="center" android:text="設定" android:textColor="#ffffff" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_bias="0.0" /> </android.support.constraint.ConstraintLayout>
3個控制元件:"後退"Button、"隨身監控"TextView、"設定"Button。
拖拽加約束:

image
搞定,然後回到activity_main2.xml檔案新增載入程式碼即可。
<include android:id="@+id/include" layout="@layout/title" />
沒錯,用include關鍵詞就匯入了,相當方便。
6.0 沒有用資料庫來讀取,用陣列模擬。Main2Activity.java
package com.example.portable_anti_theft; import android.support.v7.app.ActionBar; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.LayoutInflater; import android.widget.ArrayAdapter; import android.widget.ListView; import java.util.ArrayList; import java.util.List; public class Main2Activity extends AppCompatActivity { private List<Fruit> fruitList = new ArrayList<>(); private String[] data = { "廣西壯族自治區", "內蒙古自治區", "寧夏回族自治區", "西藏藏族自治區", "新疆維吾爾自治區", "香港特別行政區", "澳門特別行政區", "北京市", "天津市", "上海市", "重慶市", "吉林省", "遼寧省", "黑龍江省", "河北省", "河南省", "安徽省", "甘肅省", "山東省", "湖南省", "湖北省", "江蘇省", "浙江省", "江西省", "雲南省", "廣西省", "貴州省", "海南省", "臺灣省",}; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main2); ActionBar actionbar = getSupportActionBar(); if (actionbar != null) { actionbar.hide(); } ArrayAdapter<String> adapter = new ArrayAdapter<String>( Main2Activity.this,android.R.layout.simple_list_item_1,data ); ListView listView = (ListView) findViewById(R.id.list_view); listView.setAdapter(adapter); }
執行:

image

image
7.0 這時候,優化程式碼,找足夠行數的圖片,放在drawable-xhdpi目錄下,做出一開始圖片帶文字的效果。
7.1 新建佈局檔案fruit_item.xml,裡面就1個圖片控制元件,1個文字控制元件,兩根輔助線:
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" 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" android:layout_marginStart="8dp" android:layout_marginTop="8dp" android:layout_marginEnd="8dp" android:layout_marginBottom="8dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toStartOf="@+id/guideline4" app:layout_constraintHorizontal_bias="0.0" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_bias="0.016" /> <TextView android:id="@+id/fruit_name" android:layout_width="wrap_content" android:layout_height="50sp" android:layout_marginStart="16dp" android:gravity="center_vertical" app:layout_constraintBottom_toBottomOf="@+id/fruit_image" app:layout_constraintStart_toStartOf="@+id/guideline4" app:layout_constraintTop_toTopOf="@+id/fruit_image" app:layout_constraintVertical_bias="0.0" /> <android.support.constraint.Guideline android:id="@+id/guideline4" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical" app:layout_constraintGuide_percent="0.15" /> <android.support.constraint.Guideline android:id="@+id/guideline5" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical" app:layout_constraintGuide_percent="0.4"/> </android.support.constraint.ConstraintLayout>
輔助線都是垂直方向的15%、40%,能放進內容。
然後將圖片約束於父節點到15%,將文字約束到圖片、父節點、15%和40%
7.2 在src/main/java/com/example/portable_anti_theft目錄下,新建Fruit.java,水果的建構函式:
package com.example.portable_anti_theft; public class Fruit { private String name; private int imageId; public Fruit(String name,int imageId){ this.imageId =imageId; this.name = name; } public String getName(){ return name; } public int getImageId(){ return imageId; } }
因為原來的學習程式碼用的是水果作為內容,我這邊改成省份而已。
程式碼裡面可以看出,建構函式提供一個雙參,名字和圖片,並提供呼叫方法getName()和getImageId()。
7.3 同層目錄下新建FruitAdapter.java
package com.example.portable_anti_theft; import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.ImageView; import android.widget.TextView; import java.util.List; public class FruitAdapter extends ArrayAdapter<Fruit> { private int resourceId; @Override public View getView(int position, View convertView, ViewGroup parent) { Fruit fruit = getItem(position);//獲取當前項的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; } public FruitAdapter(Context context, int textViewResourceId, List<Fruit> objects) { super(context, textViewResourceId, objects); resourceId = textViewResourceId; } }
這是個自定義介面卡,繼承自ArrayAdapter,並將泛型指向Fruit類
可見,提供一個三參的建構函式,第一個是活動或者說上下文,第二個是ListView子項佈局的id,第三份傳一個list陣列,具體水果名稱/資料。下面還將id賦值給resourceId。
Adapter是什麼 :介面卡,因為 ListView 是一個 View ,不能新增子項,因此在呈現資料的時候就需要某種工具將資料呈現在 ListView 上,而 Adapter 就能充當此角色。常用的 Adapter:ArrayAdapter、BaseAdapter等。
重寫方法getView(),通過getItem()方法得到例項Fruit。然後LayoutInflater為這個子項載入傳入的佈局。 這裡的LayoutInflater的inflate()方法接收3個引數,第3個意思是隻讓在父佈局中宣告的layout屬性生效,但不會為這個View新增父佈局。因為一旦View有了父佈局之後,就不會新增到ListView中。
不過沒理解也沒關係(因為我也沒理解),這是標準寫法,想用的時候,兩個字,盤他!
7.4 修改Main2Activity.java程式碼:
package com.example.portable_anti_theft; import android.support.v7.app.ActionBar; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.LayoutInflater; import android.widget.ArrayAdapter; import android.widget.ListView; import java.util.ArrayList; import java.util.List; public class Main2Activity extends AppCompatActivity { private List<Fruit> fruitList = new ArrayList<>(); private String[] data = { "廣西壯族自治區", "內蒙古自治區", "寧夏回族自治區", "西藏藏族自治區", "新疆維吾爾自治區", "香港特別行政區", "澳門特別行政區", "北京市", "天津市", "上海市", "重慶市", "吉林省", "遼寧省", "黑龍江省", "河北省", "河南省", "安徽省", "甘肅省", "山東省", "湖南省", "湖北省", "江蘇省", "浙江省", "江西省", "雲南省", "廣西省", "貴州省", "海南省", "臺灣省",}; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main2); ActionBar actionbar = getSupportActionBar(); if (actionbar != null) { actionbar.hide(); } initFruits();//初始化水果資料 FruitAdapter adapter = new FruitAdapter(Main2Activity.this, R.layout.fruit_item, fruitList); ListView listview = (ListView) findViewById(R.id.list_view); listview.setAdapter(adapter); } private void initFruits() { Fruit apple1 = new Fruit(data[0], R.drawable.png001); fruitList.add(apple1); Fruit apple2 = new Fruit(data[1], R.drawable.png002); fruitList.add(apple2); Fruit apple3 = new Fruit(data[2], R.drawable.png003); fruitList.add(apple3); Fruit apple4 = new Fruit(data[3], R.drawable.png004); fruitList.add(apple4); Fruit apple5 = new Fruit(data[4], R.drawable.png005); fruitList.add(apple5); Fruit apple6 = new Fruit(data[5], R.drawable.png006); fruitList.add(apple6); Fruit apple7 = new Fruit(data[6], R.drawable.png007); fruitList.add(apple7); Fruit apple8 = new Fruit(data[7], R.drawable.png008); fruitList.add(apple8); Fruit apple = new Fruit(data[8], R.drawable.png009); fruitList.add(apple); Fruit apple9 = new Fruit(data[9], R.drawable.png0010); fruitList.add(apple9); Fruit apple10 = new Fruit(data[10], R.drawable.png0011); fruitList.add(apple10); Fruit apple11 = new Fruit(data[11], R.drawable.png0012); fruitList.add(apple11); Fruit apple12 = new Fruit(data[12], R.drawable.png0013); fruitList.add(apple12); Fruit apple13 = new Fruit(data[13], R.drawable.png0014); fruitList.add(apple13); Fruit apple14 = new Fruit(data[14], R.drawable.png0015); fruitList.add(apple14); Fruit apple15 = new Fruit(data[15], R.drawable.png0016); fruitList.add(apple15); Fruit apple16 = new Fruit(data[16], R.drawable.png0017); fruitList.add(apple16); Fruit apple17 = new Fruit(data[17], R.drawable.png0018); fruitList.add(apple17); Fruit apple18 = new Fruit(data[18], R.drawable.png0019); fruitList.add(apple18); Fruit apple19 = new Fruit(data[19], R.drawable.png0020); fruitList.add(apple19); Fruit apple20 = new Fruit(data[20], R.drawable.png0021); fruitList.add(apple20); Fruit apple21 = new Fruit(data[21], R.drawable.png0022); fruitList.add(apple21); Fruit apple22 = new Fruit(data[22], R.drawable.png0023); fruitList.add(apple22); Fruit apple23 = new Fruit(data[23], R.drawable.png0024); fruitList.add(apple23); Fruit apple24 = new Fruit(data[24], R.drawable.png0025); fruitList.add(apple24); Fruit apple25 = new Fruit(data[25], R.drawable.png0026); fruitList.add(apple25); Fruit apple26 = new Fruit(data[26], R.drawable.png0027); fruitList.add(apple26); Fruit apple27 = new Fruit(data[27], R.drawable.png0028); fruitList.add(apple27); Fruit apple28 = new Fruit(data[28], R.drawable.png0029); fruitList.add(apple28); } }
這裡新建了一個方法initFruits(),作用就是初始化資料化載入,將名字和圖片id傳入。
初始化資料,然後建立FruitAdapter例項,傳入3個引數,1個是活動本身,1個是擺放佈局,1個空資料。
建立下拉列表,載入下拉列表資料,接著將FruitAdapter例項的資料載入到下拉列表中。
執行在開始已。
7.5 這裡有個問題,在初始化方法initFruits()中,可見時一個一個數據載入的特別四班,可是圖片id傳入嗎,沒找到用引數傳的辦法,沒辦法一個個操作的。
8.0 這裡需要對ListView的執行效率進行優化,在程式碼中,FruitAdapter中的getView()方法中,每次佈局都用重新載入一遍,當列表快速滾動時,將會出現效能的瓶頸。
在getView()方法中還有一個convertView引數,這個引數用於將之前載入好的佈局進行快取,以便以後可以重用,修改FruitAdapter中getView()方法的程式碼:
public View getView(int position, View convertView, ViewGroup parent) { Fruit fruit = getItem(position); View view; if (convertView != null){ view = LayoutInflater.from(getContext()).inflate(resourceId, parent, false); }else{ view = convertView; } TextView fruitName = (TextView) view.findViewById(R.id.fruit_name); fruitImage.setImageResource(fruit.getImageId()); fruitName.setText(fruit.getName()); return view; }
9.0 雖然在快速滾動中可以表現出更好的效能,但我們還可以更加優化,藉助ViewHolder對每次呼叫getview()方法呼叫findViewById()方法來獲取一次控制元件進行優化。
這裡可以先參考: