ListView加载性能优化---ViewHolder---分页

时间:2020-12-11 11:22:30

ListView是Android中一个重要的组件,可以使用它加列表数据,用户可以自己定义列表数据,同时ListView的数据加载要借助Adapter,一般情况下要在Adapter类中重写getCount(),getItem(),getItemId(),getView()四个方法。自定义列表项,以及数据的加载在getView()中处理。当ListView加载的列表项数据过多时,会占用大量的内存,影响性能,因此要对ListView进行优化。

getView的加载方法有3种形式,如下:

1.每次创建一个view,然后加载数据,加载速度慢

        /**
* 1.每次都创建一个View
*/
@Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = LayoutInflater.from(context);
View view = inflater.inflate(R.layout.listview_item_two,null);
ImageView iv = (ImageView) view.findViewById(R.id.lv_listview_adapter_img);
TextView tv = (TextView) view.findViewById(R.id.tv_listview_adapter_name);
iv.setImageResource(icons[position]);
tv.setText(titles[position]);
return view;
}

  2.当convertView不为空的时候直接重新使用convertView,为空时新建view,这样可以减少了很多不必要的View的创建,然后加载数据

       /**
* 2.复用convertView,减少不必要的创建
* 当convertView不为空的时候直接重新使用convertView从而减少了很多不必要的View的创建,然后加载数据
* @param position
* @param convertView
* @param parent
* @return
*/
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if(convertView==null){
LayoutInflater inflater = LayoutInflater.from(context);
convertView = inflater.inflate(R.layout.listview_item_two,null);
}
ImageView iv = (ImageView) convertView.findViewById(R.id.lv_listview_adapter_img);
TextView tv = (TextView) convertView.findViewById(R.id.tv_listview_adapter_name);
iv.setImageResource(icons[position]);
tv.setText(titles[position]);
return convertView;
}

  3.定义一个ViewHolder,将convetView的tag设置为ViewHolder,不为空时重新使用即可

  ViewHolder将需要缓存的view封装好,convertView的setTag才是将这些缓存起来供下次调用。 当你的listview里布局多样化的时候 viewholder的作用体现明显,效率再一次提高。 View的findViewById()方法也是比较耗时的,因此需要考虑只调用一次,之后就用View.getTag()方法来获得ViewHolder对象。

当加载100条数据时,采用分页,每页加载20条,相当于创建了20个convertview。再加载21-40这二十条时,不需要重新创建20个convertview。第21条复用第1条的convertView,第22条复用第2条的convertView.......  

  @Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder vh;
if(convertView==null){
LayoutInflater inflater = LayoutInflater.from(context);
convertView = inflater.inflate(R.layout.listview_item_two,null);
vh = new ViewHolder();
vh.iv = (ImageView) convertView.findViewById(R.id.lv_listview_adapter_img);
vh.tv = (TextView) convertView.findViewById(R.id.tv_listview_adapter_name);
convertView.setTag(vh);
}else{
vh = (ViewHolder)convertView.getTag();
}
vh.iv.setImageResource(icons[position]);
vh.tv.setText(titles[position]);
return convertView;
}
static class ViewHolder{
ImageView iv;
TextView tv;
}

  以下是一个亲测的Demo案例,代码及运行结果如下:

1.ListViewActivity.class

public class ListViewActivity extends AppCompatActivity {

    private ListView listView_five;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_list_view_adapter); listView_five = (ListView) findViewById(R.id.lv_listview_adater); listView_five.setAdapter(new MyAdapter(this));
listView_five.setOnItemClickListener(new AdapterView.OnItemClickListener(){
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Toast.makeText(ListViewActivity.this,"position="+position,Toast.LENGTH_LONG).show();
}
});
} static class MyAdapter extends BaseAdapter{
private String[] titles = {"title-1","title-2","title-3","title-4"};
private int[] icons = {android.R.drawable.ic_lock_idle_alarm,android.R.drawable.ic_lock_idle_alarm,
android.R.drawable.ic_lock_idle_alarm,android.R.drawable.ic_lock_idle_alarm};
private Context context;
public MyAdapter(Context context){
this.context = context;
} @Override
public int getCount() {
return titles.length;
} @Override
public Object getItem(int position) {
return titles[position];
} @Override
public long getItemId(int position) {
return position;
} /**
* 1.每次都创建一个View
*/
/*@Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = LayoutInflater.from(context);
View view = inflater.inflate(R.layout.listview_item_two,null);
ImageView iv = (ImageView) view.findViewById(R.id.lv_listview_adapter_img);
TextView tv = (TextView) view.findViewById(R.id.tv_listview_adapter_name);
iv.setImageResource(icons[position]);
tv.setText(titles[position]);
return view;
}*/ /**
* 2.复用convertView,减少不必要的创建
* 当convertView不为空的时候直接重新使用convertView从而减少了很多不必要的View的创建,然后加载数据
* @param position
* @param convertView
* @param parent
* @return
*/
/* @Override
public View getView(int position, View convertView, ViewGroup parent) {
if(convertView==null){
LayoutInflater inflater = LayoutInflater.from(context);
convertView = inflater.inflate(R.layout.listview_item_two,null);
}
ImageView iv = (ImageView) convertView.findViewById(R.id.lv_listview_adapter_img);
TextView tv = (TextView) convertView.findViewById(R.id.tv_listview_adapter_name);
iv.setImageResource(icons[position]);
tv.setText(titles[position]);
return convertView;
}*/ /**
* 3.定义一个ViewHolder,将convetView的tag设置为ViewHolder,不为空时重新使用即可
* @param position
* @param convertView
* @param parent
* @return
*/
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder vh;
if(convertView==null){
LayoutInflater inflater = LayoutInflater.from(context);
convertView = inflater.inflate(R.layout.listview_item_two,null);
vh = new ViewHolder();
vh.iv = (ImageView) convertView.findViewById(R.id.lv_listview_adapter_img);
vh.tv = (TextView) convertView.findViewById(R.id.tv_listview_adapter_name);
convertView.setTag(vh);
}else{
vh = (ViewHolder)convertView.getTag();
}
vh.iv.setImageResource(icons[position]);
vh.tv.setText(titles[position]);
return convertView;
}
static class ViewHolder{
ImageView iv;
TextView tv;
}
}
}

  

2.activity_list_view_adapter.xml

<?xml version="1.0" encoding="utf-8"?>
<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"> <ListView
android:id="@+id/lv_listview_adater"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:dividerHeight="10dp"
android:listSelector="#00ff00"
android:scrollbars="vertical|horizontal"
/>
</RelativeLayout>

  

3.listview_item_two.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:orientation="vertical"> <ImageView
android:id="@+id/lv_listview_adapter_img"
android:src="@mipmap/gradview_dog_one"
android:layout_width="wrap_content"
android:layout_gravity="center"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/tv_listview_adapter_name"
android:text="拉布拉多"
android:layout_gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content" /> </LinearLayout>

三种方法的运行结果均相同,如下所示: 

 ListView加载性能优化---ViewHolder---分页

参考网址:http://blog.csdn.net/jacman/article/details/7087995

     http://blog.csdn.net/pkxiuluo01/article/details/7380874

ListView分页的实现:

1.PageActivity.class

/**
* ListView分页
* 实现OnScrollListener监听
*/
public class PageActivity extends AppCompatActivity implements AbsListView.OnScrollListener{
private ListView listView_six;
private int index = 1;
private MyAdapter myAdapter;
private Vector<News> news = new Vector<>();//线程安全的容器
private static final int DATA_UPDATE = 0x1;//数据更新完成后的标记
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_page); listView_six = (ListView) findViewById(R.id.lv_listview_page);
listView_six.setOnScrollListener(this);
//引入加载缓冲布局,并添加到ListView的底部
View footView = getLayoutInflater().inflate(R.layout.load_item,null);
listView_six.addFooterView(footView);
initData();
myAdapter = new MyAdapter(this);
listView_six.setAdapter(myAdapter);
} //加载数据,每次加载10条
private void initData() {
for(int i = 0;i < 10;i++){
News n = new News();
n.setTitle("title"+index);
n.setContent("content"+index);
index++;
news.add(n);
}
} private int visibleLastIndex;//用来可显示的最后一条数据的索引
/**
*
* @param view
* @param scrollState 滚动状态
* SCROLL_STATE_IDLE:不再滚动时(停止)
*/
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
if(myAdapter.getCount() == visibleLastIndex && scrollState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE){
new loadDataThread().start();
} } /**
*
* @param view
* @param firstVisibleItem 第一次显示的
* @param visibleItemCount 可显示的总量
* @param totalItemCount
*/
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
visibleLastIndex = firstVisibleItem + visibleItemCount -1;
} //线程之间通讯机制
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what){
case DATA_UPDATE:
//告知Adapter刷新数据
myAdapter.notifyDataSetChanged();
break;
default:
break;
}
}
}; //子线程通知UI线程
class loadDataThread extends Thread{
@Override
public void run() {
super.run();
//前10条数据加载完成时,再加载另外10条
initData();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//通过Handler向主线程发送一个标记
handler.sendEmptyMessage(DATA_UPDATE);
}
}
//自定义Adapter
class MyAdapter extends BaseAdapter{
private Context context;
public MyAdapter(Context context){
this.context = context;
}
@Override
public int getCount() {
return news!=null?news.size():0;
} @Override
public Object getItem(int position) {
return news.get(position);
} @Override
public long getItemId(int position) {
return position;
} @Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder vh;
if(convertView==null){
LayoutInflater inflater = LayoutInflater.from(context);
convertView = inflater.inflate(R.layout.listview_item_three,null);
vh = new ViewHolder();
vh.tv_one = (TextView) convertView.findViewById(R.id.tv_page_title);
vh.tv_two = (TextView) convertView.findViewById(R.id.tv_page_content);
convertView.setTag(vh);
}else{
vh = (ViewHolder)convertView.getTag();
} News n = news.get(position);
vh.tv_one.setText(n.getTitle()+"---");
vh.tv_two.setText(n.getContent());
return convertView;
}
class ViewHolder{
TextView tv_one;
TextView tv_two;
}
}
}

  2.activity_page.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
tools:context="com.langdon.taiyang.androidtest.listview.PageActivity"> <ListView
android:id="@+id/lv_listview_page"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:dividerHeight="10dp"
android:listSelector="#00ff00"
android:scrollbars="vertical|horizontal"
/>
</LinearLayout>

  3.load_item.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
tools:context="com.langdon.taiyang.androidtest.listview.PageActivity"> <ListView
android:id="@+id/lv_listview_page"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:dividerHeight="10dp"
android:listSelector="#00ff00"
android:scrollbars="vertical|horizontal"
/>
</LinearLayout>

  4.listview_item_three.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:orientation="horizontal"> <TextView
android:id="@+id/tv_page_title"
android:text="标题"
android:layout_gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/tv_page_content"
android:text="内容"
android:layout_gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content" /> </LinearLayout>

  运行效果如下:

ListView加载性能优化---ViewHolder---分页