前几篇博客,我们从缓存的角度优化了ListView,如果你对此还不太熟悉的话可以到android-----带你一步一步优化ListView(一),android-----带你一步一步优化ListView(二),android-----带你一步一步优化ListView(三),查看,其实,对于ListView的优化来说,还有一种方式我们没有涉及,那就是分页加载了,你在微信加载朋友圈的时候会发现每次显示到最下面会出现加载更多的提示信息,这就是分页加载的使用啦,我们没有必要每次加载ListView的数据时把所有数据全部都查找到,因为ListView本身能够显示在一个界面的条目数是有限的,我们完全可以只去查找一屏幕的数据并且显示他,当用户滑动到最下面的时候再去加载下一屏幕的数据,这对于ListView上面有图片显示的应用来说优化效果特别明显,那么这篇博客,我们将会实现类似于朋友圈上拉加载更多的功能;
先来看看效果图:
好了,接下来我们从代码层面来讲解该怎么实现:
首先定义一个主界面布局listview.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" android:orientation="vertical" > <ListView android:id="@+id/listView" android:layout_width="match_parent" android:layout_height="match_parent"> </ListView> </LinearLayout>定义每个item的显示布局item.xml
<?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="match_parent" android:orientation="vertical" > <ImageView android:id="@+id/imageView" android:layout_width="50dp" android:layout_height="50dp" /> <TextView android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="50dp" android:layout_toRightOf="@id/imageView" android:layout_marginTop="20dp" android:layout_marginRight="70dp" /> </RelativeLayout>很简单,就是一个ImageView和一个TextView
接下来就是一个用于显示加载更多的布局了load_more.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/loadmore" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:orientation="horizontal" > <ProgressBar android:id="@+id/progress" style="?android:attr/progressBarStyleSmall" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="30dp" android:layout_marginTop="30dp" /> <TextView android:id="@+id/tv" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="20dp" android:text="正在加载..." android:textColor="#FF0000" /> </LinearLayout>这个界面中会显示一个进度条和正在加载的TextView
之后便是为我们的ListView设置适配器ListViewAdapter了
public class ListViewAdapter extends BaseAdapter{ public List<String> list; public LayoutInflater inflater; public ListViewAdapter() { } public ListViewAdapter(Context context,List<String> list) { this.list = list; this.inflater = LayoutInflater.from(context); } @Override public int getCount() { return list.size(); } @Override public String getItem(int position) { return list.get(position); } @Override public long getItemId(int position) { return 0; } public void updateView(List<String> nowList) { this.list = nowList; this.notifyDataSetChanged();//强制动态刷新数据进而调用getView方法 } @Override public View getView(int position, View convertView, ViewGroup parent) { View view = null; ViewHolder holder = null; if(convertView == null) { view = inflater.inflate(R.layout.item, null); holder = new ViewHolder(); holder.imageView = (ImageView)view.findViewById(R.id.imageView); holder.textView = (TextView)view.findViewById(R.id.textView); view.setTag(holder);//为了复用holder }else { view = convertView; holder = (ViewHolder) view.getTag(); } holder.imageView.setImageResource(R.drawable.image); holder.textView.setText(list.get(position)); return view; } static class ViewHolder { ImageView imageView; TextView textView; } }
这段代码中的getView方法就是我们平常使用ListView为其设置适配器所要重写的方法,重点在于第30行的updateView方法了,这个方法用于修改需要显示在ListView上面的数据信息,并且随后调用notifyDataSetChanged方法来通知适配器告诉他ListView上面需要显示的内容已经发生了改变,这时候就会调用getView方法来重新加载数据了;
接下来就是我们的主Activity
public class MainActivity extends Activity implements OnScrollListener{ public View loadmoreView; public LayoutInflater inflater; public ListView listView; public int last_index; public int total_index; public List<String> firstList = new ArrayList<String>();//表示首次加载的list public List<String> nextList = new ArrayList<String>();//表示出现刷新之后需要显示的list public boolean isLoading = false;//表示是否正处于加载状态 public ListViewAdapter adapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.listview); inflater = LayoutInflater.from(this); loadmoreView = inflater.inflate(R.layout.load_more, null);//获得刷新视图 loadmoreView.setVisibility(View.VISIBLE);//设置刷新视图默认情况下是不可见的 listView = (ListView) findViewById(R.id.listView); initList(10, 10); adapter = new ListViewAdapter(this, firstList); listView.setOnScrollListener(this); listView.addFooterView(loadmoreView,null,false); listView.setAdapter(adapter); } /** * 初始化我们需要加载的数据 * @param firstCount * @param nextCount */ public void initList(int firstCount,int nextCount) { for(int i = 0;i < firstCount;i++) { firstList.add("第"+(i+1)+"个开始加载"); } for(int i = 0;i < firstCount;i++) { nextList.add("第"+(i+1)+"个开始加载"); } for(int i = 0;i < nextCount;i++) { nextList.add("刷新之后第"+(i+1)+"个开始加载"); } } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { last_index = firstVisibleItem+visibleItemCount; total_index = totalItemCount; System.out.println("last: "+last_index); System.out.println("total: "+total_index); } @Override public void onScrollStateChanged(AbsListView view, int scrollState) { if(last_index == total_index && (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE)) { //表示此时需要显示刷新视图界面进行新数据的加载(要等滑动停止) if(!isLoading) { //不处于加载状态的话对其进行加载 isLoading = true; //设置刷新界面可见 loadmoreView.setVisibility(View.VISIBLE); onLoad(); } } } /** * 刷新加载 */ public void onLoad() { try { //模拟耗时操作 Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } if(adapter == null) { adapter = new ListViewAdapter(this, firstList); listView.setAdapter(adapter); }else { adapter.updateView(nextList); } loadComplete();//刷新结束 } /** * 加载完成 */ public void loadComplete() { loadmoreView.setVisibility(View.GONE);//设置刷新界面不可见 isLoading = false;//设置正在刷新标志位false MainActivity.this.invalidateOptionsMenu(); listView.removeFooterView(loadmoreView);//如果是最后一页的话,则将其从ListView中移出 } }
在第19行获得了加载更多的刷新视图并且第20行设置该视图是可见的,因为我们模拟的是要加载两页的数据,这样的话第一页加载结束之后需要显示加载更多的视图,在第24行将该视图添加到了ListView的最下面,随后为Listview添加滑动事件,这里面最重要的两个方法就是第48行的onScroll和第56行的onScrollStateChanged,通过onScroll中的last_index用于获得当前页面表示在现时屏幕可以见到的最大Item的位置,total_index用于表示ListView可以加载的ListItem总数;第57行判断当前页面表示在现时屏幕可以见到的最大Item的位置如果等于ListView可以加载的ListItem总数并且滑动停止的话,则判断isLoading是否处于加载状态,如果不处于加载状态的话,则调用onLoad方法去加载,这个方法里面的Thread.sleep(2000)用于模拟耗时操作,随后利用ListAdapter的updateView方法更新ListView需要显示的数据,最后调用loadComplete来结束刷新操作,第98行设置刷新视图不可见,修改isLoading加载标志,最后将加载更多视图从当前ListView中移除;
至此,上拉加载更多讲解结束,下一篇我们将讲解一下下拉刷新的实现,谢谢大家!