android學習八(ListView的高階使用)
ListView在android開放中用的比較多,所以接下來就進行ListView的使用的講解。
首先建立一個android專案,專案名為ListViewTest.
ListView的簡單使用
修改佈局檔案,修改後程式碼如下:
<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" > <ListView android:id="@+id/list_view" android:layout_width="match_parent" android:layout_height="match_parent" ></ListView> </LinearLayout>
修改MainActivity的程式碼:
package com.wj.listviewtest; import android.app.Activity; import android.os.Bundle; import android.view.Menu; import android.widget.ArrayAdapter; import android.widget.ListView; public class MainActivity extends Activity { private String [] data={"apple","banana","orange", "watermelon","pear","grape","pineapple","strawberry", "cherry","mango"}; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //建立介面卡 ArrayAdapter<String> adapter=new ArrayAdapter<String>( MainActivity.this,android.R.layout.simple_list_item_1, data); ListView listView=(ListView) findViewById(R.id.list_view); listView.setAdapter(adapter); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } }
執行程式結果如下:
ListView是用於顯示大量資料的,這些資料我們可以事先準備好,也可以從網上或者資料中中讀取。
android.R.layout.simple_list_item_1是作為ListView子項佈局的id,這是android內建的佈局檔案裡面只有一個TextView,可用於簡單地顯示一段文字。
2.定製ListView的介面
首先準備一組圖片,分別對應上面提供的水果。
接著定義一個實體類,作為ListView介面卡的適配型別,新建Fruit類,程式碼如下:
package com.wj.listviewtest; public class Fruit { private String name;//水果名 private int imageId;//水果圖片的資源id //無參建構函式 public Fruit(){} //有參建構函式 public Fruit(String name,int imageId){ this.name=name; this.imageId=imageId; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getImageId() { return imageId; } public void setImageId(int imageId) { this.imageId = imageId; } }
Fruit類中只有2個欄位,name表示水果的名字,imageId表示水果對應圖片的資源id,然後需要為ListView的子項指定一個我們自定義的佈局,在layout目錄下面新建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="match_parent"
>
<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"
android:layout_marginLeft="10dip"
/>
</LinearLayout>
這個佈局中我們定義了一個ImageView用於顯示水果的圖片,又定義了一個TextView用於顯示水果的名稱。
接著我們要建立一個自定義的介面卡,這個介面卡繼承自ArrayAdapter,並將泛型指定為Fruit。新建一個類FruitAdapter程式碼如下:
package com.wj.listviewtest;
import java.util.List;
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;
public class FruitAdapter extends ArrayAdapter<Fruit> {
private int resourceId;
public FruitAdapter(Context context, int textViewResourceId,
List<Fruit> objects) {
super(context, textViewResourceId, objects);
// TODO Auto-generated constructor stub
/*
* 重寫了父類的建構函式,用於將上下文,ListView子項佈局的id和資料都傳進來。
* */
resourceId=textViewResourceId;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
//return super.getView(position, convertView, parent);
/*
* 重寫getView方法,這個方法在每個子項被滾動到螢幕內的時候會被呼叫,在getView方法中
* ,首先通過getItem方法得到當前項的Fruit例項,然後使用LayoutInflater來為這個子項載入
* 我們傳入的佈局,接著呼叫View的findViewById方法分別獲取到ImageView和TextView的例項,
* 並分別呼叫他們的setImageResource和setText方法來設定顯示的圖片和文字,最後返回佈局
* */
Fruit fruit=getItem(position);//獲取當前項的Fruit例項
//初始話ListView的子項佈局
View view=LayoutInflater.from(getContext()).inflate(resourceId, null);
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;
}
}
修改MainActivity的程式碼如下:
package com.wj.listviewtest;
import java.util.ArrayList;
import java.util.List;
import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.widget.ArrayAdapter;
import android.widget.ListView;
public class MainActivity extends Activity {
/*private String [] data={"apple","banana","orange",
"watermelon","pear","grape","pineapple","strawberry",
"cherry","mango"};*/
private List<Fruit> fruitList=new ArrayList<Fruit>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
/*//建立介面卡
ArrayAdapter<String> adapter=new ArrayAdapter<String>(
MainActivity.this,android.R.layout.simple_list_item_1,
data);
ListView listView=(ListView) findViewById(R.id.list_view);
listView.setAdapter(adapter);*/
initFruits();//初始化水果
FruitAdapter adapter=new FruitAdapter(MainActivity.this,
R.layout.fruit_item, fruitList);
ListView listView=(ListView) findViewById(R.id.list_view);
//設定介面卡
listView.setAdapter(adapter);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
public void initFruits(){
Fruit apple=new Fruit("apple",R.drawable.apple_pic);
fruitList.add(apple);
Fruit banana=new Fruit("banana",R.drawable.banana_pic);
fruitList.add(banana);
Fruit orange=new Fruit("orange",R.drawable.orange_pic);
fruitList.add(orange);
Fruit watermelon=new Fruit("watermelon",R.drawable.watermelon_pic);
fruitList.add(watermelon);
Fruit pear=new Fruit("pear",R.drawable.pear_pic);
fruitList.add(pear);
Fruit grape=new Fruit("grape",R.drawable.grape_pic);
fruitList.add(grape);
Fruit pineapple=new Fruit("pineapple",R.drawable.pineapple_pic);
fruitList.add(pineapple);
Fruit strawberry=new Fruit("strawberry",R.drawable.strawberry_pic);
fruitList.add(strawberry);
Fruit cherry=new Fruit("cherry",R.drawable.cherry_pic);
fruitList.add(cherry);
Fruit mango=new Fruit("mango",R.drawable.mango_pic);
fruitList.add(mango);
}
}
執行程式,結果如下:
這是一個簡單的介面,不過更加複雜的介面也可以通過修改fruit_item.xml檔案來實現更加複雜的ListView。
下面我們來提示下ListView的執行效率。
目前我們的ListView的執行效率是很低的,因為在FruitAdapter的getView方法中每次都要將佈局重寫載入了一遍,當ListView快速滾動的時候這就會成為效能的瓶頸。仔細觀察,getView方法中還有一個convertView引數,這個引數用於將之前載入好的佈局進行快取,以便之後可以進行重用,修改FruitAdapter中的程式碼,帶入如下所示:
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
//return super.getView(position, convertView, parent);
/*
* 重寫getView方法,這個方法在每個子項被滾動到螢幕內的時候會被呼叫,在getView方法中
* ,首先通過getItem方法得到當前項的Fruit例項,然後使用LayoutInflater來為這個子項載入
* 我們傳入的佈局,接著呼叫View的findViewById方法分別獲取到ImageView和TextView的例項,
* 並分別呼叫他們的setImageResource和setText方法來設定顯示的圖片和文字,最後返回佈局
* */
Fruit fruit=getItem(position);//獲取當前項的Fruit例項
View view;
/*
* 在getView()方法中進行判斷,如果convertView為空,則使用LayoutInflater去載入佈局,
* 如果不為空,則直接對convertView進行重用。這樣可以大大提升ListView的效率,在快速滾動的時候
* 也可以表現更好的效能。
* */
if(convertView==null){
//初始話ListView的子項佈局
view=LayoutInflater.from(getContext()).inflate(resourceId, null);
}else{
view=convertView;
}
/*//初始話ListView的子項佈局
View view=LayoutInflater.from(getContext()).inflate(resourceId, null);*/
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;
}
上面的程式碼進行了部分的優化,雖然現在已經不用在重複的去載入佈局了,但是每次在getView方法中還是會呼叫View的view.findViewById()方法來獲取一次控制元件的例項。我們可以藉助一個ViewHolder來對這部分效能進行優化,修改FruitAdapter中的程式碼,如下所示:
package com.wj.listviewtest;
import java.util.List;
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;
public class FruitAdapter extends ArrayAdapter<Fruit> {
private int resourceId;
public FruitAdapter(Context context, int textViewResourceId,
List<Fruit> objects) {
super(context, textViewResourceId, objects);
// TODO Auto-generated constructor stub
/*
* 重寫了父類的建構函式,用於將上下文,ListView子項佈局的id和資料都傳進來。
* */
resourceId=textViewResourceId;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
//return super.getView(position, convertView, parent);
/*
* 重寫getView方法,這個方法在每個子項被滾動到螢幕內的時候會被呼叫,在getView方法中
* ,首先通過getItem方法得到當前項的Fruit例項,然後使用LayoutInflater來為這個子項載入
* 我們傳入的佈局,接著呼叫View的findViewById方法分別獲取到ImageView和TextView的例項,
* 並分別呼叫他們的setImageResource和setText方法來設定顯示的圖片和文字,最後返回佈局
* */
Fruit fruit=getItem(position);//獲取當前項的Fruit例項
View view;
ViewHolder viewHolder;
/*
* 在getView()方法中進行判斷,如果convertView為空,則使用LayoutInflater去載入佈局,
* 如果不為空,則直接對convertView進行重用。這樣可以大大提升ListView的效率,在快速滾動的時候
* 也可以表現更好的效能。
* */
if(convertView==null){
//初始話ListView的子項佈局
view=LayoutInflater.from(getContext()).inflate(resourceId, null);
viewHolder=new ViewHolder();
viewHolder.fruitImage=(ImageView) view.findViewById(R.id.fruit_image);
viewHolder.fruitName=(TextView) view.findViewById(R.id.fruit_name);
view.setTag(viewHolder);//將ViewHolder儲存在View中
}else{
view=convertView;
viewHolder=(ViewHolder) view.getTag();//重新獲取ViewHolder
}
/*//初始話ListView的子項佈局
View view=LayoutInflater.from(getContext()).inflate(resourceId, null);*/
/*ImageView fruitImage=(ImageView) view.findViewById(R.id.fruit_image);
TextView fruitName=(TextView) view.findViewById(R.id.fruit_name);*/
viewHolder.fruitImage.setImageResource(fruit.getImageId());
viewHolder.fruitName.setText(fruit.getName());
return view;
}
class ViewHolder{
ImageView fruitImage;
TextView fruitName;
}
}
通過上面兩步優化後,ListView的執行效率已經不錯了。
ListView的點選事件
修改程式碼如下:
package com.wj.listviewtest;
import java.util.ArrayList;
import java.util.List;
import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;
public class MainActivity extends Activity {
/*private String [] data={"apple","banana","orange",
"watermelon","pear","grape","pineapple","strawberry",
"cherry","mango"};*/
private List<Fruit> fruitList=new ArrayList<Fruit>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
/*//建立介面卡
ArrayAdapter<String> adapter=new ArrayAdapter<String>(
MainActivity.this,android.R.layout.simple_list_item_1,
data);
ListView listView=(ListView) findViewById(R.id.list_view);
listView.setAdapter(adapter);*/
initFruits();//初始化水果
FruitAdapter adapter=new FruitAdapter(MainActivity.this,
R.layout.fruit_item, fruitList);
ListView listView=(ListView) findViewById(R.id.list_view);
//設定介面卡
listView.setAdapter(adapter);
/*
* setOnItemClickListener()方法來為ListView註冊一個監聽器,當用戶點選了ListView
* 中的任何一個子項時就會回撥nItemClick()方法,在這個方法中可以通過position引數判斷出使用者點選
* 的是哪一個子項,然後獲取相應的水果,並通過Toast將水果的名字顯示出來。
* */
listView.setOnItemClickListener(new OnItemClickListener(){
@Override
public void onItemClick(AdapterView<?> parent, View view, int position,
long id) {
// TODO Auto-generated method stub
Fruit fruit=fruitList.get(position);
Toast.makeText(MainActivity.this,
fruit.getName(), Toast.LENGTH_SHORT).show();
}
});
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
public void initFruits(){
Fruit apple=new Fruit("apple",R.drawable.apple_pic);
fruitList.add(apple);
Fruit banana=new Fruit("banana",R.drawable.banana_pic);
fruitList.add(banana);
Fruit orange=new Fruit("orange",R.drawable.orange_pic);
fruitList.add(orange);
Fruit watermelon=new Fruit("watermelon",R.drawable.watermelon_pic);
fruitList.add(watermelon);
Fruit pear=new Fruit("pear",R.drawable.pear_pic);
fruitList.add(pear);
Fruit grape=new Fruit("grape",R.drawable.grape_pic);
fruitList.add(grape);
Fruit pineapple=new Fruit("pineapple",R.drawable.pineapple_pic);
fruitList.add(pineapple);
Fruit strawberry=new Fruit("strawberry",R.drawable.strawberry_pic);
fruitList.add(strawberry);
Fruit cherry=new Fruit("cherry",R.drawable.cherry_pic);
fruitList.add(cherry);
Fruit mango=new Fruit("mango",R.drawable.mango_pic);
fruitList.add(mango);
}
}
執行結果如下;
好了ListView的使用就總結到這裡了。
單位和尺寸
px是畫素的意思,即螢幕中可以顯示的最小單位,我們應用裡任何可見的東西都是由一個個畫素點組成的。
pt是磅數的意思,1磅等於1/72英寸,一般pt都會作為字型的單位來使用。
dp是密度無關的畫素的意思,也被稱作為dip,和px相比,它在不同密度的螢幕中的顯示比例保持一致。
sp是可伸縮畫素的意思,它採用了和dp同樣的設計理念,解決了文字大小的適配問題。
android中的密度就是螢幕每英寸所包含的畫素數,通常以dpi為單位。
根據android的規定,在160dpi的螢幕上,1dp等於1px,而在320dpi的螢幕上,1dp就等於2px。因此,使用dp來指定控制元件的寬和高,就可以保證控制元件在不同密度的螢幕中的顯示比例儲存一致。所以dp可以理解為顯示比例(畫素除以英寸)
在編寫android程式的時候,儘量將控制元件或佈局的大小指定成match_parent或wrap_content,如果必須要指定一個固定的值,則使用dp來作為單位,指定文字的大小的時候使用sp。