ListView
ListView 是一個控件,一個在垂直滾動的列表中顯示條目的一個控件,這些條目的內容來自于一個ListAdapter。
一個簡單的例子
布局文件里新增ListView
<ListView
android:id="@+id/lv_simple"
android:layout_width="match_parent"
android:layout_height="match_parent">
</ListView>
適配器繼承自BaseAdapter
package com.example.listviewdemo;
// ...import;略
public class MyAdapter extends BaseAdapter {
private Map<Integer, Integer> map = new HashMap<>();
//getCount方法:告訴listview要顯示多少個條目
@Override
public int getCount() {
return 100;
}
//根據postion獲取listview上條目對應的Bean數據,該方法不影響數據的展示,可以先不實現(xiàn)
@Override
public Object getItem(int position) {
return null;
}
//getItemId:用來獲取條目postion行的id,該方法不影響數據的展示,可以先不實現(xiàn)
@Override
public long getItemId(int position) {
return 0;
}
//getview:告訴listview條目上顯示的內容;返回一個View對象作為條目上的內容展示,該方法返回什么樣的view,Listview的條目上就顯示什么樣的view。必須實現(xiàn)
//屏幕上每顯示一個條目getview方法就會被調用一次;convertView:曾經使用過的view對象,可以被重復使用,使用前要判斷。
@Override
public View getView(int position, View convertView, ViewGroup parent) {
TextView view = null;
if(convertView != null){//判斷converView是否為空,不為空重新使用
view = (TextView) convertView;
}else{
view = new TextView(mContext);//不能復用old view,就創(chuàng)建一個textView對象
}
view.setText("postion:"+position);//設置textview的內容
view.setTextSize(20);
map.put(view.hashCode(), 1);
// 從打印結果來看,就new了22個TextView而已
Log.d("ListView", "創(chuàng)建了"+map.size()+"個TextView對象");
return view;
}
}
MainActivity
package com.example.listviewdemo;
import android.content.Context;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.SparseArray;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.TextView;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class MainActivity extends AppCompatActivity {
private Context mContext;
private ListView listView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mContext = this;
// 1. 找到ListView
listView = (ListView) findViewById(R.id.lv_simple);
// 2. 創(chuàng)建適配器Adapter
MyAdapter adapter = new MyAdapter();
// 3. listView設置adapter
listView.setAdapter(adapter);
}
}
這里需要一個適配器來顯示條目,本例使用的適配器繼承BaseAdapter,需要實現(xiàn)上面的四個方法。其中getCount()設置顯示的條目數,必須設置不然默認為0不會顯示。getView()也必須實現(xiàn),用來告訴listview顯示的具體內容。
ListView的優(yōu)化
上例我們使用了convertView。adapter中getview方法會傳進來一個convertView,convertView是指曾經使用過的view對象,可以被重復使用,但是在使用前需要判斷是否為空,不為空直接復用,并作為getview方法的返回對象。
使用ArrayAdapter<T>
稍微改變一下代碼就行
這里需要新建一個layout文件來傳入ArrayAdapter,這個布局文件決定了每一個子項的布局。為實現(xiàn)和上面一樣的功能。只是簡單的放置一個TextView。
<?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">
<TextView
android:id="@+id/tv_item"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
讓MyAdapter繼承ArrayAdapter<String>,必須重寫構造方法。下面的這個構造方法最簡單,傳入上下文和上面的layout文件就行。但是沒有指定顯示的條目數,所以還必須實現(xiàn)getCount()。還有一個常用的構造方法public MyAdapter(@NonNull Context context, @LayoutRes int resource, @NonNull List<T> objects),可以傳入對象的列表。這樣就不用重寫getCount()了。因為內部已經實現(xiàn)好,這里的mObjects就是我們傳入的
public int getCount() {
return mObjects.size();
}
public class MyAdapter extends ArrayAdapter<String> {
private Map<Integer, Integer> map = new HashMap<>();
private int resourseId;
public MyAdapter(@NonNull Context context, @LayoutRes int resource) {
super(context, resource);
resourseId = resource;
}
@Override
public int getCount() {
return 100;
}
@NonNull
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
View view;
if(convertView != null){//判斷converView是否為空,不為空重新使用
view = convertView;
}else{
view = LayoutInflater.from(getContext()).inflate(resourseId, parent, false);
}
TextView textView = (TextView) view.findViewById(R.id.tv_item);
textView.setText("postion:"+position);//設置textview的內容
textView.setTextSize(20);
map.put(view.hashCode(), 1);
// 從打印結果來看,就new了22個TextView而已
Log.d("ListView", "創(chuàng)建了"+map.size()+"個TextView對象");
return view;
}
}
老虎機例子
三個listview均分布局。
<?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"
tools:context="com.example.tigergame.MainActivity">
<ListView
android:id="@+id/lv_1"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="match_parent">
</ListView>
<ListView
android:id="@+id/lv_2"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="match_parent">
</ListView>
<ListView
android:id="@+id/lv_3"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="match_parent">
</ListView>
</LinearLayout>
適配器,同樣使用了ArrayAdapter
package com.example.tigergame;
import android.content.Context;
import android.graphics.Color;
import android.support.annotation.LayoutRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;
import com.example.tigergame.bean.Fruit;
import java.util.List;
public class MyAdapter extends ArrayAdapter<Fruit> {
private int resourceId;
// 將布局文件給resourceId方便下面使用
public MyAdapter(@NonNull Context context, @LayoutRes 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 = getItem(position); // 獲得當前位置的實例
View view;
if (convertView != null) {
view = convertView;
} else {
view = LayoutInflater.from(getContext()).inflate(resourceId, parent, false);
}
TextView textView = (TextView) view.findViewById(R.id.tv);
if ("香蕉".equals(fruit.name)) {
textView.setText(fruit.name);
textView.setTextSize(20);
textView.setTextColor(Color.BLUE);
} else if ("橘子".equals(fruit.name)) {
textView.setText(fruit.name);
textView.setTextSize(20);
textView.setTextColor(Color.RED);
} else if ("蘋果".equals(fruit.name)) {
textView.setText(fruit.name);
textView.setTextSize(20);
textView.setTextColor(Color.RED);
} else if ("葡萄".equals(fruit.name)) {
textView.setText(fruit.name);
textView.setTextSize(20);
textView.setTextColor(Color.GREEN);
} else if ("梨".equals(fruit.name)) {
textView.setText(fruit.name);
textView.setTextSize(20);
textView.setTextColor(Color.BLUE);
} else if ("芒果".equals(fruit.name)) {
textView.setText(fruit.name);
textView.setTextSize(20);
textView.setTextColor(Color.BLACK);
} else if ("草莓".equals(fruit.name)) {
textView.setText(fruit.name);
textView.setTextSize(20);
textView.setTextColor(Color.RED);
} else if ("櫻桃".equals(fruit.name)) {
textView.setText(fruit.name);
textView.setTextSize(20);
textView.setTextColor(Color.parseColor("#ff00ff"));
}
return view;
}
}
上面使用到了Fruit的bean類
package com.example.tigergame.bean;
public class Fruit {
public String name;
public Fruit(String name) {
this.name = name;
}
}
ArrayAdapter需要傳入一個子項布局文件,只是顯示水果名稱而已。
<?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">
<TextView
android:id="@+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
MainActivity
package com.example.tigergame;
import android.content.Context;
import android.support.annotation.LayoutRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;
import com.example.tigergame.bean.Fruit;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private Context mContext;
private ListView listView1;
private ListView listView2;
private ListView listView3;
private List<Fruit> fruitList = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mContext = this;
// 初始化水果名稱
initFruits();
listView1 = (ListView) findViewById(R.id.lv_1);
listView2 = (ListView) findViewById(R.id.lv_2);
listView3 = (ListView) findViewById(R.id.lv_3);
// 適配器,傳入水果列表,以列表的長度來決定getCount()顯示的條目數
MyAdapter adapter = new MyAdapter(mContext, R.layout.list_item, fruitList);
listView1.setAdapter(adapter);
listView2.setAdapter(adapter);
listView3.setAdapter(adapter);
}
private void initFruits() {
for (int i = 0; i < 10; i++) {
Fruit apple = new Fruit("蘋果");
fruitList.add(apple);
Fruit grape = new Fruit("葡萄");
fruitList.add(grape);
Fruit pear = new Fruit("梨");
fruitList.add(pear);
Fruit mango = new Fruit("芒果");
fruitList.add(mango);
Fruit strawberry = new Fruit("草莓");
fruitList.add(strawberry);
Fruit cherry = new Fruit("櫻桃");
fruitList.add(cherry);
Fruit banana = new Fruit("香蕉");
fruitList.add(banana);
Fruit orange = new Fruit("橘子");
fruitList.add(orange);
}
}
}
例子完成,看下長啥樣。

listView與MVC
listview也是符合MVC思想的,和javaweb對比下。
javaweb mvc
m....mode....javabean
v....view....jsp
c....control...servlet
listview mvc
m....mode....Bean
v....view....listview
c....control...adapter
ListView的顯示原理
- 要考慮listview顯示的條目數 getcount
- 考慮listview每個條目顯示的內容 getview
- 考慮每個item的高度,因為屏幕的多樣化
- 還要考慮listview的滑動,監(jiān)聽一個舊的條目消失,一個新的條目顯示。
注意:ListView的寬高能match_parent的就match_parent。
如果使用的wrap_content。比如我們的子項數為3,getView()顯示時不止調用3次??赡苁畮状?。因為使用wrap_content并不能確切知道listview占據的布局空間,不能計算出三個條目能不能放到這個布局空間,也不能計算出能放多少個條目。它就會去嘗試,嘗試了好幾次之后,確認能放下,計算出能放下多少個條目之后,才停止嘗試。
而使用match _parent,直接就知道屏幕的寬高,除以一個條目的高度,就能知道布局空間能放下多少個條目。一次性計算出來,一次性顯示出來,所以只會調用三次getView()。
上面說的我也很暈,記住ListView控件的寬高盡量match_parent就好。
inflate(int resource, ViewGroup root, boolean attachToRoot)
在這之前,需要了解:我們在開發(fā)的過程中給控件所指定的layout_width和layout_height到底是什么意思?該屬性的表示一個控件在容器中的大小,就是說這個控件必須在容器中,這個屬性才有意義,否則無意義。這就意味著如果我直接將Linearlayout加載進來而不給它指定一個父布局,則inflate布局的根節(jié)點的layout_width和layout_height屬性將會失效(因為這個時候linearlayout將不處于任何容器中,那么它的根節(jié)點的寬高自然會失效)。如果我想讓來布局的根節(jié)點有效,又不想加載到父布局,就可以設置root不為null,而attachToRoot為false。
第一個參數就是要加載的布局id,第二個參數是指給該布局的指定一個父布局,如果不需要就直接傳null。那么這第三個參數attachToRoot又是什么意思呢?
1. 如果root為null,attachToRoot將失去作用,設置任何值都沒有意義。第三個參數不填,填寫前兩個就行。因為沒有父布局作為容器,布局文件的layout屬性失效。
2. 如果root不為null,attachToRoot設為true,則會給加載的布局文件的指定一個父布局,即root。會自動將第一個參數所指定的View添加到第二個參數所指定的View中。不用再addView
3. 如果root不為null,attachToRoot設為false,表示不將第一個參數所指定的View添加到root中。當該view被添加到父view當中時,因為這個控件在一個布局容器之中(root),所以這些layout屬性會生效。這個時候我需要手動的將inflate加載進來的view添加到容器中,因為inflate的最后一個參數false表示不將view添加到父布局。
4. 在不設置attachToRoot參數的情況下,如果root不為null,attachToRoot參數默認為true。
為什么Activity布局的根節(jié)點的寬高屬性會生效?
布局文件activity_main.xml假設使用了LineraLayout。在對應的MainActivity中一開始調用setContentView(R.layout.activity_main)。由于我們的頁面中有一個頂級View叫做DecorView,DecorView中包含一個豎直方向的LinearLayout,LinearLayout由兩部分組成,第一部分是標題欄,第二部分是內容欄,內容欄是一個FrameLayout,我們在Activity中調用setContentView就是將View添加到這個FrameLayout中,所以activity_main.xml是有父布局的,因此設置layout_width和layout_height才有效 。

再來看上面的例子,MyAdapter中如果view = LayoutInflater.from(getContext()).inflate(resourceId, parent, true);則會拋出出異常。指定為true的話,源碼中會調用root.addView(temp, params);而此時的root是我們的ListView,ListView為AdapterView的子類??聪翧dapterView的源碼。調用addView直接拋出異常!
@Override
public void addView(View child) {
throw new UnsupportedOperationException("addView(View) is not supported in AdapterView");
}
新聞案例
寫著寫著就不是新聞了...看吧

這個例子展示了一些網站,點擊會跳轉到其首頁。依然使用ArrayAdapter
主布局
<?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"
tools:context="com.example.newsdemo.MainActivity">
<ListView
android:id="@+id/lv_news"
android:layout_width="match_parent"
android:layout_height="match_parent">
</ListView>
</LinearLayout>
子項布局 -- list_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:layout_margin="16dp">
<ImageView
android:id="@+id/iv_news"
android:layout_width="80dp"
android:layout_height="80dp" />
<LinearLayout
android:layout_gravity="center_vertical"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:weightSum="1">
<TextView
android:maxLines="1"
android:id="@+id/tv_news_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="20sp"
android:textColor="#000" />
<TextView
android:maxLines="2"
android:layout_marginTop="3dp"
android:id="@+id/tv_news_des"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="#555"
/>
</LinearLayout>
</LinearLayout>
Bean封裝子項信息
package com.example.newsdemo.bean;
public class NewsBean {
// 圖片資源id
public int imgId;
// 標題
public String title;
// 標題的描述
public String des;
// 標題所指鏈接
public String url;
}
處理子項信息的工具類,返回一個子項列表
package com.example.newsdemo.utils;
import com.example.newsdemo.R;
import com.example.newsdemo.bean.NewsBean;
import java.util.ArrayList;
import java.util.List;
public class NewsUtils {
public static List<NewsBean> getAllNews() {
List<NewsBean> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
NewsBean beanBaidu = new NewsBean();
beanBaidu.imgId = R.drawable.baidusousuo;
beanBaidu.title = "百度搜索";
beanBaidu.des = "全球最大的中文搜索引擎、致力于讓網民更便捷地獲取信息,找到所求。百度超過千億的中文網頁數據庫,可以瞬間找到相關的搜索結果。";
beanBaidu.url = "https://www.baidu.com/";
list.add(beanBaidu);
NewsBean beanAmap = new NewsBean();
beanAmap.imgId = R.drawable.amap;
beanAmap.title = "高德地圖";
beanAmap.des = "高德地圖官方網站,提供全國地圖瀏覽,地點搜索,公交駕車查詢服務??赏瑫r查看商家團購、優(yōu)惠信息。高德地圖,您的出行、生活好幫手。";
beanAmap.url = "http://ditu.amap.com/";
list.add(beanAmap);
NewsBean beanBaiduyun = new NewsBean();
beanBaiduyun.imgId = R.drawable.baiduyun;
beanBaiduyun.title = "百度云盤";
beanBaiduyun.des = "百度網盤為您提供文件的網絡備份、同步和分享服務??臻g大、速度快、安全穩(wěn)固,支持教育網加速,支持手機端。";
beanBaiduyun.url = "https://pan.baidu.com/";
list.add(beanBaiduyun);
NewsBean beanDouban = new NewsBean();
beanDouban.imgId = R.drawable.douban;
beanDouban.title = "豆瓣電影";
beanDouban.des = "豆瓣電影提供最新的電影介紹及評論包括上映影片的影訊查詢及購票服務。你可以記錄想看、在看和看過的電影電視劇,順便打分、寫影評。";
beanDouban.url = "https://movie.douban.com/";
list.add(beanDouban);
NewsBean beanZhihu = new NewsBean();
beanZhihu.imgId = R.drawable.zhihu;
beanZhihu.title = "知乎";
beanZhihu.des = "一個真實的網絡問答社區(qū),幫助你尋找答案,分享知識";
beanZhihu.url = "https://www.zhihu.com/";
list.add(beanZhihu);
NewsBean beanGoogletranslate = new NewsBean();
beanGoogletranslate.imgId = R.drawable.google;
beanGoogletranslate.title = "谷歌翻譯";
beanGoogletranslate.des = "Google 的免費翻譯服務可提供簡體中文和另外 100 多種語言之間的互譯功能,可讓您即時翻譯字詞、短語和網頁內容。";
beanGoogletranslate.url = "https://translate.google.cn/";
list.add(beanGoogletranslate);
}
return list;
}
}
適配器
package com.example.newsdemo.adapter;
import android.content.Context;
import android.support.annotation.LayoutRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
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 com.example.newsdemo.R;
import com.example.newsdemo.bean.NewsBean;
import java.util.List;
public class NewsAdapter extends ArrayAdapter<NewsBean> {
private int resourceId;
public NewsAdapter(@NonNull Context context, @LayoutRes int resource, @NonNull List<NewsBean> objects) {
super(context, resource, objects);
resourceId = resource;
}
@NonNull
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
// 根據所在位置獲得新聞實例
NewsBean newsBean = getItem(position);
View view;
if (convertView == null) {
view = LayoutInflater.from(getContext()).inflate(resourceId, parent, false);
} else {
view = convertView;
}
ImageView newsImg = (ImageView) view.findViewById(R.id.iv_news);
TextView newsTitle = (TextView) view.findViewById(R.id.tv_news_title);
TextView newsDes = (TextView) view.findViewById(R.id.tv_news_des);
newsImg.setImageResource(newsBean.imgId);
newsTitle.setText(newsBean.title);
newsDes.setText(newsBean.des);
return view;
}
}
MainActivity
package com.example.newsdemo;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import com.example.newsdemo.adapter.NewsAdapter;
import com.example.newsdemo.bean.NewsBean;
import com.example.newsdemo.utils.NewsUtils;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private Context mContext;
private List<NewsBean> news;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mContext = this;
ListView listView = (ListView) findViewById(R.id.lv_news);
// 獲取所有新聞
news = NewsUtils.getAllNews();
NewsAdapter adapter = new NewsAdapter(mContext, R.layout.list_item, news);
listView.setAdapter(adapter);
// 設置子項點擊事件監(jiān)聽,點擊跳轉到瀏覽器
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
/**
* listview的條目點擊時會調用該方法
* @param parent 代表listviw
* @param view 擊的條目上的那個view對象
* @param position 條目的位置
* @param id 條目的id
*/
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
// 下面注釋的寫法也可以獲取子項
// NewsBean bean = (NewsBean) parent.getItemAtPosition(position);
NewsBean bean = news.get(position);
String url = bean.url;
// 這個構造器接收action和uri,相當于setAction和setData兩個方法
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
startActivity(intent);
}
});
}
}
常用獲取inflate的寫法
// 1.
//context:上下文, resource:要轉換成view對象的layout的id, root:為這個布局指定一個父布局
view = View.inflate(context, R.layout.item_news_layout, null);
// 2.
//通過LayoutInflater將布局轉換成view對象
view = LayoutInflater.from(context).inflate(R.layout.item_news_layout, parent, false);
// 3.
//通過context獲取系統(tǒng)服務得到一個LayoutInflater,通過LayoutInflater將一個布局轉換為view對象
LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = layoutInflater.inflate(R.layout.item_news_layout, parent, false);
SimpleAdapter(了解)
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ListView listView = (ListView) findViewById(R.id.lv_news);
//創(chuàng)建一個simpleAdapter,封裝simpleAdapter的數據
ArrayList<Map<String, Object>> arrayList = new ArrayList<>();
HashMap<String, Object> hashMap = new HashMap<>();
hashMap.put("class", "C++");
hashMap.put("user", R.drawable.baidusousuo);
arrayList.add(hashMap);
HashMap<String, Object> hashMap1 = new HashMap<>();
hashMap1.put("class", "android");
hashMap1.put("user", R.drawable.amap);
arrayList.add(hashMap1);
HashMap<String, Object> hashMap2 = new HashMap<>();
hashMap2.put("class", "javaEE");
hashMap2.put("user", R.drawable.baiduyun);
arrayList.add(hashMap2);
/**
*
* @param context 上下文
* @param List<? extends Map<String, ?>> 要顯示的數據,外層的 List裝了所有子項。而內層的map,不同的key對應于一個子項的不同控件
* @param int resource 布局文件
* @param from map中的Key
* @param to 與from里面的key對應 的控件id,即把map里key對應的value給控件顯示。如下面的意思就是把key為class的value給tv_news_title顯示,key為user的value給控件iv_news顯示
*/
SimpleAdapter simpleAdapter = new SimpleAdapter(this, arrayList, R.layout.list_item, new String[]{"class", "user"}, new int[]{R.id.tv_news_title, R.id.iv_news});
listView.setAdapter(simpleAdapter);
}
將數據庫的數據顯示到ListView
這個例子上次寫過。當時只是將查詢到的數據簡單打印到控制臺,現(xiàn)將其顯示到手機上。
封裝數據的bean
package dao;
public class InfoBean {
public int _id;
public String name;
public String telephone;
}
對數據執(zhí)行增刪改查
查詢結果以List返回
package dao;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import com.example.databasedemo.MyDatabaseOpenHelper;
import java.util.ArrayList;
import java.util.List;
public class InfoDao {
private MyDatabaseOpenHelper myDatabaseOpenHelper;
private SQLiteDatabase db;
public InfoDao(Context context ,String dbName , int version){
//創(chuàng)建一個幫助類對象
myDatabaseOpenHelper = new MyDatabaseOpenHelper(context, dbName, null, version);
db = myDatabaseOpenHelper.getReadableDatabase();
}
public boolean add(InfoBean bean){
//執(zhí)行sql語句需要sqliteDatabase對象
ContentValues values = new ContentValues(); // 是用Map封裝的對象
values.put("name", bean.name);
values.put("telephone", bean.telephone);
// 第二個參數可以為空,返回值表示新增的行號,-1表示添加失敗
long result = db.insert("people", null, values);
return result != -1;
}
public int del(String name){
//執(zhí)行sql語句需要sqliteDatabase對象
int count = db.delete("people", "name = ?", new String[]{name});
return count;
}
public int update(InfoBean bean){
ContentValues values = new ContentValues();
values.put("telephone", bean.telephone);
int count = db.update("people", values, "name = ?", new String[]{bean.name});
return count;
}
public List<InfoBean> query(String name){
List<InfoBean> beanList = new ArrayList<>();
// 查詢people表中的name為參數指定的"_id", "name", "telephone"字段,按照id遞減
Cursor cursor = db.query("people", new String[]{"_id", "name", "telephone"}, "name = ?", new String[]{name}, null, null, "_id asc");
//解析Cursor中的數據
if(cursor != null && cursor.getCount() >0){//判斷cursor中是否存在數據
//循環(huán)遍歷結果集,獲取每一行的內容
while(cursor.moveToNext()){ //條件,游標能否定位到下一行
InfoBean bean = new InfoBean();
//獲取數據
bean._id = cursor.getInt(cursor.getColumnIndex("_id"));
bean.name = cursor.getString(cursor.getColumnIndex("name"));
bean.telephone = cursor.getString(cursor.getColumnIndex("telephone"));
beanList.add(bean);
}
cursor.close();//關閉結果集
}
return beanList;
}
}
創(chuàng)建數據庫和表
package com.example.databasedemo;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
public class MyDatabaseOpenHelper extends SQLiteOpenHelper {
public static final String CREATE_BOOK = "create table people ("
+ "_id integer primary key autoincrement, name varchar(20), telephone varchar(11))";
public static final String NEW_TABLE = "create table book ("
+ "_id integer primary key autoincrement, price real, pages integer)";
private Context mContext;
/**
*
* @param context 上下文
* @param name 數據庫的名稱
* @param factory 用來創(chuàng)建cursor對象,填入null使用默認的
* @param version version:數據庫的版本號,從1開始,如果發(fā)生改變,onUpgrade方法將會調用,4.0之后只能升不能降
*/
public MyDatabaseOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
super(context, name, factory, version);
mContext = context;
}
// 調用getReadableDatabase()或者getWritableDatabase()時會調用該方法
// 第一次創(chuàng)建數據庫時才能執(zhí)行該方法,特別適合做表結構的初始化
// 傳入的參數db可以用來執(zhí)行sql語句
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_BOOK);
db.execSQL(NEW_TABLE);
}
// version改變時,調用這個方法,version只能升不能降
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("drop table if exists book");
db.execSQL("drop table if exists people");
onCreate(db);
db.execSQL("alter table book add author varchar(20)");
db.execSQL("alter table people add age integer");
}
}
ListView的適配器
package com.example.databasedemo.adapter;
import android.content.Context;
import android.support.annotation.LayoutRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;
import com.example.databasedemo.R;
import java.util.List;
import dao.InfoBean;
public class DatabaseAdapter extends ArrayAdapter<InfoBean> {
private int resourseId;
public DatabaseAdapter(@NonNull Context context, @LayoutRes int resource, @NonNull List<InfoBean> objects) {
super(context, resource, objects);
resourseId = resource;
}
@NonNull
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
View view;
if (convertView == null) {
view = LayoutInflater.from(getContext()).inflate(resourseId,parent, false);
} else {
view = convertView;
}
TextView idText = (TextView) view.findViewById(R.id.tv_id);
TextView nameText = (TextView) view.findViewById(R.id.tv_name);
TextView phoneText = (TextView) view.findViewById(R.id.tv_phone);
InfoBean bean = getItem(position);
// 這里bean._id是int類型,而參數接收CharSequence,故轉成String
idText.setText(String.valueOf(bean._id));
nameText.setText(bean.name);
phoneText.setText(bean.telephone);
return view;
}
}
子項布局
<?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">
<TextView
android:id="@+id/tv_id"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:gravity="center"/>
<TextView
android:id="@+id/tv_name"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:gravity="center"
/>
<TextView
android:id="@+id/tv_phone"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:gravity="center"
/>
</LinearLayout>
主布局
<?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:layout_margin="16dp"
android:orientation="vertical"
tools:context="com.example.databasedemo.MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Button
android:id="@+id/bt_add"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/add" />
<Button
android:id="@+id/bt_update"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/update" />
<Button
android:id="@+id/bt_del"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/del" />
<Button
android:id="@+id/bt_query"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/query" />
</LinearLayout>
<!--invisible設置為不可見,但是占據空間
gone設置不可見,不占據空間-->
<!--這里先讓表頭不可見,當用戶點擊查詢時候才顯示-->
<LinearLayout
android:id="@+id/layout_table_title"
android:visibility="invisible"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:layout_marginTop="10dp">
<TextView
android:layout_weight="1"
android:id="@+id/tv_title1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:gravity="center"
android:text="id"
android:textColor="#000"/>
<TextView
android:layout_weight="1"
android:id="@+id/tv_title2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:gravity="center"
android:text="用戶"
android:textColor="#000"/>
<TextView
android:layout_weight="1"
android:id="@+id/tv_title3"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:gravity="center"
android:text="電話"
android:textColor="#000"/>
</LinearLayout>
<ListView
android:id="@+id/lv_result"
android:layout_width="match_parent"
android:layout_height="match_parent">
</ListView>
</LinearLayout>
MainActivity
主要看查詢
package com.example.databasedemo;
import android.content.Context;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.Toast;
import com.example.databasedemo.adapter.DatabaseAdapter;
import java.util.List;
import dao.InfoBean;
import dao.InfoDao;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private Context mContext;
private InfoDao infoDao;
private Button btAdd;
private Button btDel;
private Button btUpdate;
private Button btQuery;
private ListView listView;
private LinearLayout linearLayout;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mContext = this;
// 創(chuàng)建數據庫,有則打開,沒有則create
infoDao = new InfoDao(mContext, "test.db", 4);
btAdd = (Button) findViewById(R.id.bt_add);
btDel = (Button) findViewById(R.id.bt_del);
btQuery = (Button) findViewById(R.id.bt_query);
btUpdate = (Button) findViewById(R.id.bt_update);
listView = (ListView) findViewById(R.id.lv_result);
linearLayout = (LinearLayout) findViewById(R.id.layout_table_title);
btAdd.setOnClickListener(this);
btDel.setOnClickListener(this);
btUpdate.setOnClickListener(this);
btQuery.setOnClickListener(this);
}
@Override
public void onClick(View v) {
InfoBean bean;
int count;
switch (v.getId()) {
case R.id.bt_add:
bean = new InfoBean();
bean.name = "張三";
bean.telephone = "119";
boolean result = infoDao.add(bean);
if (result) {
Toast.makeText(mContext, "添加成功", Toast.LENGTH_SHORT).show();
}
break;
case R.id.bt_del:
count = infoDao.del("張三");
Toast.makeText(mContext, "刪除"+count+"行", Toast.LENGTH_SHORT).show();
break;
case R.id.bt_update:
bean = new InfoBean();
bean.name = "張三";
bean.telephone = "110";
count = infoDao.update(bean);
Toast.makeText(mContext, "更新"+count+"行", Toast.LENGTH_SHORT).show();
break;
case R.id.bt_query:
// 用戶查詢時候表頭才顯示
linearLayout.setVisibility(View.VISIBLE);
List<InfoBean> list = infoDao.query("張三");
DatabaseAdapter adapter = new DatabaseAdapter(mContext, R.layout.data_item, list);
listView.setAdapter(adapter);
break;
default:
break;
}
}
}
看下結果

by @sunhaiyu
2017.4.16