ListView的优化一直是Android中比较常见的问题,那么一些比较基本的ListView的优化还是应该要掌握的,其中就涉及到ListView的分页加载问题,主要是将数据一页一页的加载出来,提高流畅度,一般是一次加载10项,下面看看具体的代码。
xml的代码比较简单,就是在activity_main中显示一个listview
<?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/android:list"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
显示ListView中item的内容list_item
<?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" >
<TextView
android:id="@+id/list_item_text"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:paddingBottom="10dp"
android:paddingTop="10dp"
android:textSize="20sp" />
</LinearLayout>
定义一个分页加载的Button load_more
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<Button
android:id="@+id/loadMoreButton"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:onClick="loadMore"
android:text="加载更多" />
</LinearLayout>
定义一个ListViewAdpater适配器继承BaseAdapter,需要重写里面的getCount(),getItem(),getItem(),getView()方法。此适配器需要一个Context对象来获取LayoutInflater实例和一个集合对象来充当适配器的数据集。
package com.example.mikasa_list;
import java.util.List;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
public class ListViewAdapter extends BaseAdapter{
private List<String> items;
private LayoutInflater inflater;
public ListViewAdapter(Context context, List<String> list){
items = list;
inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
@Override
public int getCount() {
// TODO Auto-generated method stub
return items.size();
}
@Override
public Object getItem(int position) {
// TODO Auto-generated method stub
return items.get(position);
}
@Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
ViewHolder viewholder = null;
if(convertView == null){
convertView = inflater.inflate(R.layout.list_item, null);
viewholder = new ViewHolder();
viewholder.text = (TextView)convertView.findViewById(R.id.list_item_text);
convertView.setTag(viewholder);
}else{
viewholder = (ViewHolder)convertView.getTag();
}
viewholder.text.setText(items.get(position));
return convertView;
}
class ViewHolder{
private TextView text;
}
public void additem(String item){
items.add(item);
}
}
可以看到在getView()方法中使用了对ListView的基本优化,如果convertView为空,则使用LayoutInflater的inflate方法去加载布局,如果不为空,则直接重用。同时增加了一个ViewHolder的内部类来对空间进行缓存,这样就不用每次都重新加载空间的实例,提高了ListView的运行效率。
主MainActivity也比较简单
package com.example.mikasa_list;
import java.util.ArrayList;
import java.util.List;
import android.app.ListActivity;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends ListActivity implements OnScrollListener {
private ListView lv;
private Button btn;
private View loadMoreView;
private int visibleLastIndex = 0;
private int visibleItemCount;
private ListViewAdapter adapter;
private Handler mhandler = new Handler();
private Boolean islast = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
loadMoreView = getLayoutInflater().inflate(R.layout.load_more, null);
btn = (Button)loadMoreView.findViewById(R.id.loadMoreButton);
lv = getListView();
lv.addFooterView(loadMoreView);
intiadater();
setListAdapter(adapter);
lv.setOnScrollListener(this);
}
private void intiadater() {
ArrayList<String> list = new ArrayList<String>();
for(int i = 0; i < 10; i++){
list.add(String.valueOf(i + 1));
}
adapter = new ListViewAdapter(this, list);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount){
//public void onScroll(AbsListView arg0, int arg1, int arg2, int arg3) {
// TODO Auto-generated method stub
visibleLastIndex = firstVisibleItem;
if(firstVisibleItem + visibleItemCount == totalItemCount){
islast = true;
}
}
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
//public void onScrollStateChanged(AbsListView arg0, int arg1) {
// TODO Auto-generated method stub
int itemsLastIndex = adapter.getCount();
int lastIndex = itemsLastIndex + 1 ;
if(scrollState == OnScrollListener.SCROLL_STATE_IDLE && islast){
Log.i("TAG", "loading...");
loadData();
Toast.makeText(this, "正在加载中...", Toast.LENGTH_SHORT).show();
islast = false;
}
}
public void loadMore(View view) {
btn.setText("加载中...");
loadData();
}
private void loadData() {
mhandler.postDelayed(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
int count = adapter.getCount();
for(int i = count; i < count + 10; i++){
adapter.additem(String.valueOf(i + 1));
}
adapter.notifyDataSetChanged();
lv.setSelection(visibleLastIndex);
btn.setText("加载更多...");
}
}, 2000);
}
}
主要是设置ListView的监听事件ListView.setOnScrollListener(this);并重写onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount)和onScrollStateChanged(AbsListView view, int scrollState)方法,注意到这里的几个参数。
firstVisibleItem表示当前能看到的第一个列表项id(从0开始,不完整的也算)
visibleItemCount表示当前页面能看到的列表总数(不完整的也算),即整个页面能显示多少个item
totalItemCount表示ListView的列表总数
scrollState表示正在滚动的回调次数,为0的时候表示没有滑动,为1的时候表示正在滑动,为2的时候表示手指离开屏幕
islast是判断是否在最后一行,初始值为false,注意加载的数据一般在异步完成。
还有几个问题是在滑动过程中,向上滑动有时也会自动加载,应该是要加判断是向上还是向下,还有一个是会连续加载两次,有时会因为firstVisibleItem和visibleItemCount的数值问题导致不能自动加载,猜想应该是因为在底部加了一个Button的原因,有待考证。希望能一起讨论