ListView总结(一)
listview是比较常用的组件,可以方便的把内容以列表的形式展现.
步骤
三要素:listview(展示列表的view),数据(字符串,图片等),适配器(将数据映射到listview上)
步骤:
1.在布局文件上添加listview控件
2.通过id找到listview控件
3.准备数据
4.使用数据适配器填充数据
代码
<RelativeLayout 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" >
<!-- 1.在布局文件上添加listview控件 -->
<ListView
android:id="@+id/lv"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
布局文件view
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="100dip"
android:padding="10dip" >
<ImageView
android:id="@+id/iv"
android:layout_width="80dip"
android:layout_height="80dip"
android:src="@drawable/ic_launcher" />
<TextView
android:id="@+id/tv_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/iv"
<!-- 设置单行显示 -->
android:singleLine="true"
android:text="新闻标题"
android:textSize="16sp" />
<TextView
android:id="@+id/tv_desc"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/tv_title"
android:layout_toRightOf="@id/iv"
<!-- 设置最多显示的行数 -->
android:maxLines="3"
android:text="新闻描述"
android:textColor="#66000000"
android:textSize="13sp" />
<TextView
android:id="@+id/tv_type"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:text="专题"
android:textSize="10sp" />
</RelativeLayout>
activity文件
import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.xmlpull.v1.XmlPullParser;
import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Environment;
import android.util.Xml;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
public class MainActivity extends Activity {
private ListView lv;
private List<Map<String, String>> list = new ArrayList<Map<String, String>>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 2.通过id找到listview控件
lv = (ListView) findViewById(R.id.lv);
// 3.准备数据:解析sd卡中的xml文件,把数据放到list集合中
parsernews();
// 4.使用数据适配器填充数据:自定义数据适配器填充数据
lv.setAdapter(new MyAdapter());
}
private void parsernews() {
try {
// 1.创建解析器
XmlPullParser parser = Xml.newPullParser();
// 2.初始化解析器
FileInputStream fis = new FileInputStream(
Environment.getExternalStorageDirectory() + "/news.xml");
parser.setInput(fis, "UTF-8");
// 3.得到当前解析的事件类型
int type = parser.getEventType();
Map<String, String> map = null;
// 4.如果没有解析到文档结尾,就循环解析每个子标签
while (type != XmlPullParser.END_DOCUMENT) {
switch (type) {
case XmlPullParser.START_TAG: // 解析到开始标签
// 得到当前解析到的开始标签
String name = parser.getName();
if ("item".equals(name)) {
map = new HashMap<String, String>();
} else if ("title".equals(name)) {
// 得到标签体
String title = parser.nextText();
map.put("title", title);
} else if ("description".equals(name)) {
// 得到标签体
String description = parser.nextText();
map.put("description", description);
} else if ("image".equals(name)) {
// 得到标签体
String image = parser.nextText();
map.put("image", image);
} else if ("type".equals(name)) {
// 得到标签体
String newsType = parser.nextText();
map.put("type", newsType);
} else if ("comment".equals(name)) {
// 得到标签体
String comment = parser.nextText();
map.put("comment", comment);
}
break;
case XmlPullParser.END_TAG:
// 得到当前解析到的开始标签
String endName = parser.getName();
if ("item".equals(endName)) {
list.add(map);
map = null;
}
break;
}
// 解析到下一个事件类型
type = parser.next();
}
fis.close();
} catch (Exception e) {
e.printStackTrace();
}
}
private class MyAdapter extends BaseAdapter {
@Override
public int getCount() {
return list.size();
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// 1.加载布局文件为view
View view = View.inflate(MainActivity.this, R.layout.item, null);
// 2.得到布局文件中的控件
ImageView iv = (ImageView) view.findViewById(R.id.iv);
TextView tv_title = (TextView) view.findViewById(R.id.tv_title);
TextView tv_desc = (TextView) view.findViewById(R.id.tv_desc);
TextView tv_type = (TextView) view.findViewById(R.id.tv_type);
// 3.给控件填充数据
Map<String, String> map = list.get(position);
tv_title.setText(map.get("title"));
tv_desc.setText(map.get("description"));
String type = map.get("type");
if ("1".equals(type)) {
tv_type.setText("专题");
tv_type.setTextColor(Color.RED);
}
if ("2".equals(type)) {
tv_type.setText("评论");
tv_type.setTextColor(Color.BLACK);
}
if ("3".equals(type)) {
tv_type.setTextColor(Color.BLUE);
tv_type.setText("视频");
}
// 4.返回view
return view;
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return 0;
}
}
}
程序运行得到的结果如图1,
上面将xml文件解析后的数据存放在map集合中,但是这种单纯键值对的形式有局限性,如果用Javabean类保存解析得到的数据,扩展性会更好.
下面的几段代码用Javabean类保存解析得到的数据,并用tomcat模拟服务器存放xml文件(需要加权限).
<uses-permission android:name="android.permission.INTERNET"/>
javabean类
package so.easy.newsclient.domain;
public class NewsInfo {
private String title;
private String description;
private String image;
private String type;
private String comment;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getImage() {
return image;
}
public void setImage(String image) {
this.image = image;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getComment() {
return comment;
}
public void setComment(String comment) {
this.comment = comment;
}
@Override
public String toString() {
return "NewsInfo [title=" + title + ", description=" + description
+ ", image=" + image + ", type=" + type + ", comment="
+ comment + "]";
}
}
解析xml文件的util类
package so.easy.newsclient.service;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import org.xmlpull.v1.XmlPullParser;
import android.util.Xml;
import so.easy.newsclient.domain.NewsInfo;
public class ParseNewsService {
public static List<NewsInfo> parserNews(InputStream is) {
List<NewsInfo> list = new ArrayList<NewsInfo>();
try {
XmlPullParser parser = Xml.newPullParser();
parser.setInput(is, "UTF-8");
int type = parser.getEventType();
NewsInfo info = null;
while (type != XmlPullParser.END_DOCUMENT) {
switch (type) {
case XmlPullParser.START_TAG:
String name = parser.getName();
if ("item".equals(name)) {
info = new NewsInfo();
} else if ("title".equals(name)) {
String title = parser.nextText();
info.setTitle(title);
} else if ("description".equals(name)) {
String description = parser.nextText();
info.setDescription(description);
} else if ("image".equals(name)) {
String image = parser.nextText();
info.setImage(image);
} else if ("type".equals(name)) {
String newsType = parser.nextText();
info.setType(newsType);
} else if ("comment".equals(name)) {
String comment = parser.nextText();
info.setComment(comment);
}
break;
case XmlPullParser.END_TAG:
String endName = parser.getName();
if ("item".equals(endName)) {
list.add(info);
info = null;
}
break;
}
type = parser.next();
}
} catch (Exception e) {
e.printStackTrace();
}
return list;
}
}
activity
package so.easy.newsclient;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import com.jit.lib.SmartImageView;
import so.easy.newsclient.domain.NewsInfo;
import so.easy.newsclient.service.ParseNewsService;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.app.Activity;
import android.graphics.Color;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.TextView;
public class MainActivity extends Activity {
private ListView lv;
public static final String path = "http://192.168.20.87:8080/news.xml";
private List<NewsInfo> list = new ArrayList<NewsInfo>();
private Handler handler = new Handler() {
public void handleMessage(Message msg) {
// List<NewsInfo> list = (List<NewsInfo>) msg.obj;
// 新闻数据填充在listview上;
lv.setAdapter(new MyAdapter());
};
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
lv = (ListView) findViewById(R.id.lv);
// 访问网络,获取服务器返回的数据,放到list集合中
// 不能在主线程中访问网络
getData();
}
private void getData() {
// 访问网络
new Thread() {
public void run() {
try {
// 创建url对象,打开http类型的连接
URL url = new URL(path);
HttpURLConnection connection = (HttpURLConnection) url
.openConnection();
// 设置连接的请求参数
connection.setRequestMethod("GET");
connection.setConnectTimeout(3000);
// 得到服务器返回的响应码,判断是否是200
int code = connection.getResponseCode();
if (code == 200) {
// 得到服务器返回的二进制输入流
InputStream is = connection.getInputStream();
// 获取服务器返回的新闻数据,解析xml数据,放到list集合中
list = ParseNewsService.parserNews(is);
// 关流
is.close();
// 子线程不能修改ui界面,所以使用handle完成修改工作
// 新闻数据填充到listview上
Message msg = Message.obtain();
msg.obj = list;
handler.sendMessage(msg);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}.start();
}
private class MyAdapter extends BaseAdapter {
@Override
public int getCount() {
return list.size();
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// 1.把布局文件加载为一个view对象
View view = View.inflate(MainActivity.this, R.layout.item, null);
// 2.得到布局文件中的控件
SmartImageView siv = (SmartImageView) view.findViewById(R.id.siv);
TextView tv_title = (TextView) view.findViewById(R.id.tv_title);
TextView tv_desc = (TextView) view.findViewById(R.id.tv_desc);
TextView tv_type = (TextView) view.findViewById(R.id.tv_type);
// 3.给控件填充数据
NewsInfo info = list.get(position);
// 使用SmartImageview加载网络上的图片
siv.setImageUrl(info.getImage());
tv_title.setText(info.getTitle());
tv_desc.setText(info.getDescription());
if ("1".equals(info.getType())) {
// 评论
tv_type.setText("评论:" + info.getComment());
tv_type.setTextColor(Color.RED);
}
if ("2".equals(info.getType())) {
// 视频
tv_type.setText("视频");
tv_type.setTextColor(Color.BLUE);
}
if ("3".equals(info.getType())) {
// 专题
tv_type.setText("专题");
tv_type.setTextColor(Color.BLACK);
}
// 4.返回view显示在界面上
return view;
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return 0;
}
}
}
关于adapter
上面用到的适配器都是自定义的适配器,Android也提供了很多adapter,ArrayAdapter,simpleAdapter,SimpleCursorAdapter,BaseAdapter等.
#
使用arrayAdapter适配数据
MainActivity.this 上下文
android.R.layout.simple_list_item_1
系统定义好的条目的布局文件,这里也可以使用自定义的布局view
names 需要填充的数据
lv.setAdapter(new ArrayAdapter<String>(MainActivity.this, android.R.layout.simple_list_item_1, names));
使用系统提供的布局还可以实现带选择框的ListView;
android.R.layout.simple_list_item_checked
android.R.layout.simple_list_item_multiple_choice
android.R.layout.simple_list_item_single_choice
上面三种布局只是提供了选择框的风格,单选多选的实现还需添加以下代码;
lv.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); //多选
lv.setChoiceMode(ListView.CHOICE_MODE_SINGLE); //单选
还可以为listview添加点击监听事件;
lv.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position,
long id) {
int x = position%3;
if (x==0) {
view.setBackgroundColor(Color.BLUE);
} else if(x==1){
view.setBackgroundColor(Color.GREEN);
}else if (x==2) {
view.setBackgroundColor(Color.RED);
}
}
});
#
使用SimpleAdapter适配数据
除了可以在listview列表中添加文字,还能添加图片,Button,CheckBox等
MainActivity.this 上下文
data 要显示的数据,必须是list,里面的元素必须是map类型的
new String[]{"name"} map中要显示的字段名称的数组
new int[]{R.id.tv_name} 字段显示的控件的id的数组
lv.setAdapter(new SimpleAdapter(MainActivity.this, data, R.layout.item, new String[]{"name","sex"}, new int[]{R.id.tv_name,R.id.tv_sex}));
#
如果在listview列表中加入Button,Button会抢夺ListView的焦点,只需在button的xml文件中将focusable参数(默认为true)设置为false,
android:focusable=“false”
lv.setAdapter(new SimpleAdapter(MainActivity.this, data, R.layout.item,
new String[] { "name", "sex", "btn" }, new int[] {
R.id.tv_name, R.id.tv_sex, R.id.btn }));
lv.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> arg0, View arg1, int arg2,
long arg3) {
Toast.makeText(getApplicationContext(), "Listview条目", 0).show();
System.out.println("ListView 条目");
Button btn = (Button) arg1.findViewById(R.id.btn);
btn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(getApplicationContext(), "btn", 0)
.show();
System.out.println("Button");
}
});
}
});
点击后的Logcat打印日志:
#
使用ListView时,可以继承listActivity(和Activity区别不大),它对ListView做了优化,设置适配器时调用setListAdapter()方法.
ListView 优化
优化的两种方式:
conventView
ViewHolder
下面是三段Google实例代码;
the slow way
public View getView(int position, View convertView, ViewGroup parent) {
View item = mInflater.inflat(R.layout.list_item_icon_text, null);
((TextView) item.findViewById(R.id.text)).setText(DATA[position]);
((ImageView) item.findViewById(R.id.icon)).setImageBitmap((position & 1) == 1 ? mIcon1 : mIcon2);
return item;
}
the right way
conventView的特点:
– Supplied by ListView
– Matches item types
– Reuse it
可以看出conventView的重用省去了findviewbyid()的时间,提高了效率;
#
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
// 缓存为空的时候 创建一个新的view
convertView = mInflater.inflate(R.layout.item, parent, false);
}
((TextView) convertView.findViewById(R.id.text)).setText(DATA[position]);
((ImageView) convertView.findViewById(R.id.icon)).setImageBitmap(
(position & 1) == 1 ? mIcon1 : mIcon2);
return convertView;
}
the fast way
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
// 1.判断缓存view是否为空
if (convertView == null) {
convertView = mInflater.inflate(R.layout.list_item_icon_text,parent, false);
// 2创建新的对象 并创建viewHolder对象
holder = new ViewHolder();
// 3.取出控件放到viewHolder里
holder.text = (TextView) convertView.findViewById(R.id.text);
holder.icon = (ImageView) convertView.findViewById(R.id.icon);
// 4.把viewHolder作为一个标记 放到convertView上
convertView.setTag(holder);
} else {
// 5.取出缓存view里的viewHolder对象 因为viewHolder里面有控件 减少了findViewById的过程
holder = (ViewHolder) convertView.getTag();
}
// 6.给控件赋值
holder.text.setText(DATA[position]);
holder.icon.setImageBitmap((position & 1) == 1 ? mIcon1 : mIcon2);
return convertView;
}