1、ListView分页加载
<1>分页的作用
(1)避免一次性加载过多内容时,造成内存溢出;
(2)可以增强用户体验。
<2>实现思路
(1)当滚动到最后一条的时候,加载新数据;
(2)适配器的数据源要进行累加:listDatas.addAll(list)
(3)数据发生变化时,适配器及时通知:adapter.notifyDataSetChanged()
(4)判断是否滚到了最后一行
1. if (firstVisibleItem + visibleItemCount == totalItemCount ) {
2. isBottom = true;
3. }
<3>实例步骤
(1)模拟1000条数据,以分页方式显示;
(2)使用BaseAdapter自定义适配器显示数据;
(3)在ListView下方增加“显示更多”按钮以实现加载下一页数据;
(4)ListView.setOnScrollListener()方法设置滚动事件监听器;
(5)通过滚动事件监听器判断是否滚动到最底部,若在底部则显示“显示更多“按钮;
(6)点击“显示更多”按钮,根据当前的页码与一页显示的记录数,加载数据;
(7)将加载的数据追加到适配器的数据源中
2、AdapterView.OnItemClickListener 列表项点击事件监听器:onItemClick(AdapterView<?> parent, View view, int position, long id)
3、OnScrollListener 滚动事件监听器
<1>onScrollStateChanged(AbsListView view, int scrollState):监听屏幕的滚动状态的变动情况
(1)scrollState状态说明:
SCROLL_STATE_TOUCH_SCROLL(1):
表示正在滚动。当屏幕滚动且用户使用的触碰或手指还在屏幕上时为1
SCROLL_STATE_FLING(2) :
表示手指做了抛的动作(手指离开屏幕前,用力滑了一下,屏幕产生惯性滑动)。
SCROLL_STATE_IDLE(0) :
表示屏幕已停止。屏幕停止滚动时为0。
<2>onScroll(AbsListView view, int firstVisibleItem,int visibleItemCount, int totalItemCount):监听屏幕滚动的item的数量
(1)AbsListView : 当前滚动的ListView控件
(2)firstVisibleItem:当前窗口中能看见ListView的第一个列表项ID(从0开始)
(3)visibleItemCount:当前窗口中能看见的ListView列表项的个数(小半个也算)
(4)totalItemCount:ListView列表项的总数
4、ListView的优化
<1>ListView的高度属性设置
(1)设置为"wrap_content"时,getView()方法一般会执行3次左右;
(2)建议设置为"match_parent"或固定值,避免重复计算ListView的高度。
<2>ViewHolder的使用,将findViewById()获取的控件封装起来,便于复用
<3>convertView的复用
(1)converView如何产生的?
Android中有个叫做Recycler的构件,下图是他的工作原理:
a、如果你有100个item,其中只有可见的项目存在内存中,其他的在Recycler中。
b、ListView先请求一个type1视图(getView),然后请求其他可见的item,convertView在getView中是空(null)的。
c、当item1滚出屏幕,并且一个新的item从屏幕底端上来时,ListView再请求一个type1视图,convertView此时不是空值了,它的值是item1。你只需设定新的数据,然后返回convertView,不必重新创建一个视图。
(2)优点:减少getView()时通过infalter加载布局的次数,减少内存开销,提高性能
(3)缺点:若使用异步任务下载图片时,可能会出现图片错位问题,因为convertView的位置可能是第一个
<4>View的标签tag属性
(1)同id属性一样,标识控件的唯一性
(2)View.setTag(Object)设置控件的标签
(3)View.getTag()获取控件的标签
(4)View.setTag()+ViewHolder+convertView复用,减少布局加载和findViewBy的次数,降低的内存开销,提升性能
5、ListView实现图文混排
<1>实现思路
(1)创建图文混排的Item布局文件
1. <?xml version="1.0" encoding="utf-8"?>
2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
3. android:layout_width="match_parent"
4. android:layout_height="match_parent"
5. android:orientation="vertical" >
6.
7.
8. <ImageView
9. android:id="@+id/img"
10. android:layout_width="80dp"
11. android:layout_height="80dp"
12. android:scaleType="fitXY"
13. android:src="@drawable/ic_launcher"
14. />
15.
16. <TextView
17. android:id="@+id/tv_address"
18. android:layout_width="wrap_content"
19. android:layout_height="wrap_content"
20. />
21. <TextView
22. android:id="@+id/tv_age"
23. android:layout_width="wrap_content"
24. android:layout_height="wrap_content"
25. />
26. <TextView
27. android:id="@+id/tv_name"
28. android:layout_width="wrap_content"
29. android:layout_height="wrap_content"
30. />
31. <TextView
32. android:id="@+id/tv_phone"
33. android:layout_width="wrap_content"
34. android:layout_height="wrap_content"
35. />
36. </LinearLayout>
(2)根据JSON的数据结构构建实体类
1. package com.qf.bean;
2.
3. public class Girl {
4. private String address;
5. private int age;
6. private String avatar;
7. private String name;
8. private String phone;
9.
10. public String getAddress() {
11. return address;
12. }
13. public void setAddress(String address) {
14. this.address = address;
15. }
16. public int getAge() {
17. return age;
18. }
19. public void setAge(int age) {
20. this.age = age;
21. }
22. public String getAvatar() {
23. return avatar;
24. }
25. public void setAvatar(String avatar) {
26. this.avatar = avatar;
27. }
28. public String getName() {
29. return name;
30. }
31. public void setName(String name) {
32. this.name = name;
33. }
34. public String getPhone() {
35. return phone;
36. }
37. public void setPhone(String phone) {
38. this.phone = phone;
39. }
40. @Override
41. public String toString() {
42. return "Girl [address=" + address + ", age=" + age + ", avatar="
43. + avatar + ", name=" + name + ", phone=" + phone + "]";
44. }
45.
46.
47.
48. }
(3)创建BaseAdapter的子类,并实现相关方法
1. package com.qf.adapter;
2.
3. import java.util.List;
4.
5. import com.qf.asy.ImgAsy;
6. import com.qf.bean.Girl;
7. import com.qf.day10demo02.R;
8. import com.qf.util.Constant;
9.
10. import android.content.Context;
11. import android.view.LayoutInflater;
12. import android.view.View;
13. import android.view.ViewGroup;
14. import android.widget.BaseAdapter;
15. import android.widget.ImageView;
16. import android.widget.TextView;
17.
18. public class GirlAdapter extends BaseAdapter{
19. private List<Girl> data;
20. private Context context;
21.
22. public GirlAdapter(List<Girl> data, Context context) {
23. super();
24. this.data = data;
25. this.context = context;
26. }
27.
28. @Override
29. public int getCount() {
30. return data.size();
31. }
32.
33. @Override
34. public Object getItem(int position) {
35. return data.get(position);
36. }
37.
38. @Override
39. public long getItemId(int position) {
40. return position;
41. }
42.
43. @Override
44. public View getView(int position, View convertView, ViewGroup parent) {
45. ViewHolder viewHolder = null;
46. if (convertView == null) {
47. viewHolder = new ViewHolder();
48.
49. convertView = LayoutInflater.from(context).inflate(R.layout.item, null);
50. viewHolder.img = (ImageView) convertView.findViewById(R.id.img);
51. viewHolder.tv_address = (TextView) convertView.findViewById(R.id.tv_address);
52. viewHolder.tv_age = (TextView) convertView.findViewById(R.id.tv_age);
53. viewHolder.tv_name = (TextView) convertView.findViewById(R.id.tv_name);
54. viewHolder.tv_phone = (TextView) convertView.findViewById(R.id.tv_phone);
55.
56. convertView.setTag(viewHolder);
57. }else {
58. viewHolder = (ViewHolder) convertView.getTag();
59. }
60.
61. Girl girl = data.get(position);
62. //拿到图片的后半段地址
63. String avatar = girl.getAvatar();
64. viewHolder.img.setTag(Constant.BASE_URL + avatar);
65. //下载图片地址
66. new ImgAsy(viewHolder.img).execute(Constant.BASE_URL + avatar);
67.
68.
69. viewHolder.tv_address.setText(girl.getAddress());
70. viewHolder.tv_age.setText(girl.getAge() +"");
71. viewHolder.tv_name.setText(girl.getName());
72. viewHolder.tv_phone.setText(girl.getPhone());
73.
74. return convertView;
75. }
76.
77. class ViewHolder{
78. ImageView img;
79. TextView tv_address,tv_age,tv_name,tv_phone;
80.
81. }
82.
83. }
(4)创建网络工具类
1. package com.qf.util;
2.
3. import java.io.ByteArrayOutputStream;
4. import java.io.IOException;
5. import java.io.InputStream;
6. import java.net.HttpURLConnection;
7. import java.net.MalformedURLException;
8. import java.net.URL;
9.
10.
11. public class NetworkUtil {
12. public static byte[] getBytes(String path){
13. InputStream is = null;
14. ByteArrayOutputStream baos = null;
15. try {
16. URL url = new URL(path);
17. HttpURLConnection connection = (HttpURLConnection) url.openConnection();
18. connection.setRequestMethod("GET");
19. connection.setConnectTimeout(3000);
20. if (200 == connection.getResponseCode()) {
21. is = connection.getInputStream();
22. baos = new ByteArrayOutputStream();
23. int len = 0;
24. byte[] buffer = new byte[1024];
25. while((len = is.read(buffer)) != -1){
26. baos.write(buffer,0,len);
27. }
28. return baos.toByteArray();
29. }
30.
31. } catch (MalformedURLException e) {
32. e.printStackTrace();
33. } catch (IOException e) {
34. e.printStackTrace();
35. }finally{
36. if (is != null) {
37. try {
38. is.close();
39. } catch (IOException e) {
40. e.printStackTrace();
41. }
42. }
43.
44. if(baos != null){
45. try {
46. baos.close();
47. } catch (IOException e) {
48. e.printStackTrace();
49. }
50. }
51. }
52.
53. return null;
54.
55. }
56. }
(5)创建AsyncTask的子类并实现网络请求和JSON解析的功能
1. package com.qf.asy;
2.
3. import java.util.ArrayList;
4. import java.util.List;
5.
6. import org.json.JSONArray;
7. import org.json.JSONException;
8. import org.json.JSONObject;
9.
10. import com.qf.bean.Girl;
11. import com.qf.util.NetworkUtil;
12.
13. import android.content.Context;
14. import android.os.AsyncTask;
15. import android.widget.Toast;
16.
17. public class DataAsy extends AsyncTask<String, Void, List<Girl>>{
18. private DataCallback callback;
19. private Context context;
20.
21. public DataAsy(DataCallback callback, Context context) {
22. super();
23. this.callback = callback;
24. this.context = context;
25. }
26.
27. @Override
28. protected List<Girl> doInBackground(String... params) {
29. byte[] bytes = NetworkUtil.getBytes(params[0]);
30. if (bytes != null) {
31. String content = new String(bytes);
32. //Log.i("info", "=====content===="+content);
33. try {
34. JSONObject object = new JSONObject(content);
35. JSONArray array = object.optJSONArray("girls");
36. List<Girl> girlList = new ArrayList<Girl>();
37. Girl girl = null;
38. //optxxx:当key值不存在的时候,程序不会崩,而是获取一个类型的默认值
39. for (int i = 0; i < array.length(); i++) {
40. JSONObject object2 = array.optJSONObject(i);
41. girl = new Girl();
42. girl.setAddress(object2.optString("address"));
43. girl.setAge(object2.optInt("age"));
44. girl.setAvatar(object2.optString("avatar"));
45. girl.setName(object2.optString("name"));
46. girl.setPhone(object2.optString("phone"));
47.
48. girlList.add(girl);
49.
50. }
51.
52. return girlList;
53.
54. } catch (JSONException e) {
55. e.printStackTrace();
56. }
57. }
58.
59. return null;
60. }
61.
62. @Override
63. protected void onPostExecute(List<Girl> result) {
64. super.onPostExecute(result);
65. if (result == null) {
66. Toast.makeText(context, "downLoad failed", Toast.LENGTH_LONG).show();
67. }else {
68. callback.doResult(result);
69. }
70.
71. }
72.
73.
74. //声明一个接口
75. public interface DataCallback{
76. public void doResult(List<Girl> result);
77. }
78.
79. }
(6)6、将解析的数据传入到自定义BaseAdapter适配器,并显示到ListView
1. package com.qf.day10demo02;
2.
3. import java.util.ArrayList;
4. import java.util.List;
5.
6. import com.qf.adapter.GirlAdapter;
7. import com.qf.asy.DataAsy;
8. import com.qf.asy.DataAsy.DataCallback;
9. import com.qf.bean.Girl;
10. import com.qf.util.Constant;
11.
12. import android.app.Activity;
13. import android.os.Bundle;
14. import android.view.View;
15. import android.view.View.OnClickListener;
16. import android.widget.AbsListView;
17. import android.widget.AbsListView.OnScrollListener;
18. import android.widget.Button;
19. import android.widget.ListView;
20.
21.
22. public class MainActivity extends Activity
23. implements OnScrollListener,OnClickListener{
24. private ListView listView;
25. private Button btn_more;
26. private GirlAdapter adapter;
27. private List<Girl> totaList = new ArrayList<Girl>();
28. private int page = 0;
29.
30. @Override
31. protected void onCreate(Bundle savedInstanceState) {
32. super.onCreate(savedInstanceState);
33. setContentView(R.layout.activity_main);
34. listView = (ListView) findViewById(R.id.listView);
35. btn_more = (Button) findViewById(R.id.btn_more);
36. listView.setOnScrollListener(this);
37. btn_more.setOnClickListener(this);
38.
39. getData();
40. adapter = new GirlAdapter(totaList, this);//空数据源
41. listView.setAdapter(adapter);
42.
43.
44.
45.
46. }
47.
48. private void getData(){
49. new DataAsy(new DataCallback() {
50. @Override
51. public void doResult(List<Girl> result) {
52. totaList.addAll(result);
53. adapter.notifyDataSetChanged();
54.
55. }
56. },this).execute(Constant.INDEX_PATH+page);
57. }
58.
59. @Override
60. public void onScrollStateChanged(AbsListView view, int scrollState) {
61. }
62. @Override
63. public void onScroll(AbsListView view, int firstVisibleItem,
64. int visibleItemCount, int totalItemCount) {
65. if (firstVisibleItem + visibleItemCount == totalItemCount) {
66. btn_more.setVisibility(View.VISIBLE);
67. }else {
68. btn_more.setVisibility(View.GONE);
69. }
70.
71. }
72.
73. @Override
74. public void onClick(View v) {
75. page++;
76. getData();
77.
78. }
79.
80.
81. }
<2>需要的核心类
(1)BaseAdapter
(2)AsyncTask
(4)HttpClient
(5)JSONObject/JSONArray
6、listview其他方法
<1>listView.addHeaderView(view);
1. View headView = LayoutInflater.from(this).inflate(R.layout.headlayout, null);
2. //添加一个头部布局/控件,要在绑定适配器之前。
3. listView.addHeaderView(headView);
<2>listView.addFooterView(button);
1. Button button = new Button(this);
2. button.setText("底部按钮");
3. //添加一个底部布局/控件
4. listView.addFooterView(button);
<3>listView.setEmptyView(emptyView);
1. TextView emptyView = (TextView) findViewById(R.id.emptyView);
2. //使用前提:emptyView必须是预先存在当前布局里面,隐藏的
3. listView.setEmptyView(emptyView);