【正文】
一、具体步骤:
(1)在activiy_main.xml中加一个ListView控件;再添加一个item的模板activity_main_item.xml,加一个底部加载的视图activity_main_load.xml;
(2)初始化item中的数据;
(3)自定义适配器BaseAdapter;
(4)ListiView绑定监听器OnScrollListener,并实现该监听器的两个方法:
- public void onScrollStateChanged(AbsListView view, int scrollState)
- public void onScroll(AbsListView view, int firstVisibleItem,int visibleItemCount, int totalItemCount)
注:如果屏幕滑到最下面了,并且scrollState的状态为:滚动完毕之后ListView处于停止状态(手离开屏幕),此时可以加载新数据了
(5)通过额外的线程,来模拟加载新数据;
(6)新数据加载完成后,通过handle通知主线程,将这个新数据显示在UI界面上(因为涉及到线程安全问题),此时要调用notifyDataSetChanged()方法来刷新。
注:handler为线程之间通信的桥梁
二、代码实现:
完整版代码如下:
activiy_main.xml代码如下:
1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 xmlns:tools="http://schemas.android.com/tools" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" 5 tools:context=".MainActivity" > 6 <ListView 7 android:id="@+id/listView1" 8 android:layout_width="match_parent" 9 android:layout_height="match_parent" > 10 </ListView> 11 </LinearLayout>
注:为优化起见,第9行的代码一定要写成"match_parent",而不是“wrap_content”(解释略)
activity_main_item.xml代码如下:(作为一个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 <TextView 7 android:id="@+id/textView1_title" 8 android:layout_width="match_parent" 9 android:layout_height="wrap_content" 10 android:text="Large Text" 11 android:textAppearance="?android:attr/textAppearanceLarge" /> 12 <TextView 13 android:id="@+id/textView2_content" 14 android:layout_width="match_parent" 15 android:layout_height="wrap_content" 16 android:text="TextView" /> 17 </LinearLayout>
activity_main_load.xml代码如下:(作为加载时底部的显示)
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="wrap_content" 5 android:orientation="horizontal" 6 android:gravity="center" > 7 8 <ProgressBar 9 android:id="@+id/progressBar1" 10 android:layout_width="25dp" 11 android:layout_height="25dp" /> 12 13 <TextView 14 android:id="@+id/textView1" 15 android:layout_width="wrap_content" 16 android:layout_height="wrap_content" 17 android:text="正在加载···" /> 18 19 </LinearLayout>
MainActivity.java代码如下:
1 package com.smyhvae.smyh005listview5; 2 3 import java.util.Vector; 4 5 import android.app.Activity; 6 import android.os.Bundle; 7 import android.os.Handler; 8 import android.os.Message; 9 import android.view.Menu; 10 import android.view.View; 11 import android.view.ViewGroup; 12 import android.widget.AbsListView; 13 import android.widget.AbsListView.OnScrollListener; 14 import android.widget.BaseAdapter; 15 import android.widget.ListView; 16 import android.widget.TextView; 17 18 public class MainActivity extends Activity { 19 20 private ListView listView; 21 Vector<News> news = new Vector<News>(); 22 //private ArrayList<News> news = new ArrayList<News>();上面一行与这一行,二选一 23 MyAdapter myAdapter; 24 25 @Override 26 protected void onCreate(Bundle savedInstanceState) { 27 super.onCreate(savedInstanceState); 28 setContentView(R.layout.activity_main); 29 listView = (ListView)findViewById(R.id.listView1); 30 31 listView.setOnScrollListener(new ListViewListener()); //绑定监听器 32 33 //设置底部视图 34 View footer = getLayoutInflater().inflate(R.layout.activity_main_load, null); 35 listView.addFooterView(footer); 36 37 // initData(); 38 new LoadDataThread().start();//加载数据的工作线程 39 40 myAdapter = new MyAdapter(); 41 listView.setAdapter(myAdapter); 42 } 43 44 //定义一个数据的类 45 class News { 46 String title; 47 String content; 48 } 49 50 int index = 1;//数据的记数器(索引) 51 //初始化数据 52 void initData(){ 53 System.out.println("initData"); 54 for (int i = 0; i < 15; i++) { 55 News n = new News(); //这句话一定要放在循环的里面,否则每个item显示的内容都是一样的 56 n.title = "title-"+index; 57 n.content = "content"+index; 58 index++; 59 news.add(n); //这一步很关键,我就是少了这一步,导致运行时,界面为空 60 } 61 } 62 63 64 65 //自定义适配器 66 class MyAdapter extends BaseAdapter{ 67 68 @Override 69 public int getCount() { 70 // TODO Auto-generated method stub 71 return news.size(); 72 } 73 public Object getItem(int position) { 74 return news.get(position); 75 } 76 77 @Override 78 public long getItemId(int position) { 79 return position; 80 } 81 82 @Override 83 public View getView(int position, View convertView, ViewGroup parent) { 84 ViewHolder viewHolder = new ViewHolder(); 85 //通过下面的条件判断语句,来循环利用。如果convertView = null ,表示屏幕上没有可以被重复利用的对象。 86 if(convertView==null){ 87 //创建View 88 convertView = getLayoutInflater().inflate(R.layout.activity_main_item, null); 89 viewHolder.tvTitle = (TextView) convertView.findViewById(R.id.textView1_title); 90 viewHolder.tvContent = (TextView) convertView.findViewById(R.id.textView2_content); 91 convertView.setTag(viewHolder); 92 }else{ 93 viewHolder = (ViewHolder)convertView.getTag(); 94 } 95 //从Vector中取出数据填充到ListView列表项中 96 News n = news.get(position); 97 viewHolder.tvTitle.setText(n.title); 98 viewHolder.tvContent.setText(n.content); 99 return convertView; 100 } 101 102 } 103 104 static class ViewHolder{ 105 TextView tvTitle; 106 TextView tvContent; 107 } 108 109 110 111 112 //实现ListView的监听器的接口 113 int visibleLastIndex = 0; //最后一个显示的索引 114 public class ListViewListener implements OnScrollListener{ 115 @Override 116 public void onScrollStateChanged(AbsListView view, int scrollState) { 117 //如果屏幕滑到最下面了,并且scrollState的状态为:滚动完毕之后ListView处于停止状态(手离开屏幕), 118 if(visibleLastIndex==myAdapter.getCount() && scrollState==OnScrollListener.SCROLL_STATE_IDLE){ 119 new LoadDataThread().start();//如果满足上面的条件,此时,可以加载新数据了 120 } 121 } 122 123 @Override 124 public void onScroll(AbsListView view, int firstVisibleItem, 125 int visibleItemCount, int totalItemCount) { 126 visibleLastIndex = firstVisibleItem+visibleItemCount-1; 127 128 } 129 130 } 131 132 //额外开启一个线程,模拟加载数据 133 class LoadDataThread extends Thread{ 134 @Override 135 public void run() { 136 initData(); 137 try { 138 Thread.sleep(2000);//休眠两秒 139 } catch (InterruptedException e) { 140 // TODO Auto-generated catch block 141 e.printStackTrace(); 142 } 143 144 //新数据加载完成后,通过handle通知主线程,将这个新数据显示在UI界面上(因为涉及到线程安全问题) 145 handler.sendEmptyMessage(1); 146 } 147 } 148 149 //handler为线程之间通信的桥梁 150 private Handler handler = new Handler(){ 151 public void handleMessage(Message msg) { 152 switch(msg.what){ 153 case 1: //根据上面的提示,当Message为1,表示数据处理完了,可以通知主线程了 154 myAdapter.notifyDataSetChanged(); //这个方法一旦调用,UI界面就刷新了 155 break; 156 157 default : 158 break; 159 } 160 } 161 162 }; 163 164 165 @Override 166 public boolean onCreateOptionsMenu(Menu menu) { 167 // Inflate the menu; this adds items to the action bar if it is present. 168 getMenuInflater().inflate(R.menu.main, menu); 169 return true; 170 } 171 172 173 174 }
运行后,显示结果如下:
下图依次为:刚运行时、滑动到底部时、刷新之后的效果
三、总结:
- 监听事件处理的接口:OnScrollListener
- Adapter的刷新方法:notifyDataSetChanged()