标签:
听到这,大家觉得应该是再熟悉不过了吧,当然或许很多人已经知道 ListView 的优化。我在这里就再详细的讲一遍。顺便让ListView分类显示。
之前咋网上仿照别人写过一个,但是不知道原理, 今天看到了一篇写的不错,收藏了。
可以看到数据有100个(0-99),奇数和偶数分类显示,并给每类数据加了一个头
<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" tools:context="com.example.listview.MainActivity" > <ListView android:id="@+id/listView" android:layout_width="wrap_content" android:layout_height="wrap_content" </ListView> </RelativeLayout>
②在主activity中找到该listView,并为它设置适配器
private ListView listView; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); listView = (ListView) findViewById(R.id.listView); //初始化adapter MyAdapter adapter = new MyAdapter(); //为listView设置适配器 listView.setAdapter(adapter); }
jiDatas = new ArrayList<String>(); ouDatas = new ArrayList<String>(); //将data进行奇数和偶数分类(模拟数据) for (int i = 0; i < 100; i++) { if (i%2 == 0) { ouDatas.add(i+""); //偶数 }else { jiDatas.add(i+""); //奇数 } }
接下来重要的来了,定义适配器继承BaseAdapter,并实现四个方法
public int getCount() //返回listView有多少个条目
public Object getItem(int position) //返回位置为position的条目
public long getItemId(int position) //返回位置为position的条目的Id
public View getView(int position, View convertView, ViewGroup parent) //返回每个条目的视图
中间两个方法目前还用不到。主要方法是getView()方法。
前面三个方法比较简单我就直接写:
class MyAdapter extends BaseAdapter { @Override public int getCount() { //注意为什么要加2呢,因为把数据分成了两类,加了两个头,就增加了两个条目 return 1+jiDatas.size()+1+ouDatas.size(); } @Override public Object getItem(int position) { return data.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { return null; }
现在看看最重要的一个方法getView,也就是在这里面对ListView的优化和分类显示。
首先不优化的时候的写法是:
public View getView(int position, View convertView, ViewGroup parent) { View view = view = View.inflate(MainActivity.this, R.layout.myitem, null); TextView tv = (TextView) view.findViewById(R.id.tv); tv.setText(data.get(position)); return view; }
我们知道getView的调用次数,是有多少个条目就调用多少次吧,那么上面这么写的加载布局和findViewById就会调用100次吧,而每个条目的布局都一样,为什么要重复加载呢,所以要想办法减少加载布局的次数,怎么做呢。
我们可以看到getView方法有三个参数,第二个参数convertView还没有使用到,对,有人应该知道就是缓存。
convertView工作原理:
注:
这里屏幕只显示8个条目,你的可能不是,因为每个手机的屏幕大小不一样嘛,不过原理都是一样的。
看到这里我们知道了convertView,所以接下来使用convertView来减少加载布局的次数:
public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { convertView = View.inflate(MainActivity.this, R.layout.myitem, null); } TextView tv = (TextView) convertView.findViewById(R.id.tv); tv.setText(data.get(position)); return convertView ; }
注:
这里屏幕只显示8个条目,你的可能不是,因为每个手机的屏幕大小不一样嘛,不过原理都是一样的。
看到这里我们知道了convertView,所以接下来使用convertView来减少加载布局的次数:
这样加载视图就只会调用它第一屏的条目的数量。比如:第一屏显示8个条目,加载视图会调用8次,以后滑动就不会调用了。
到这里看起来是可以了,但是我们可以注意到findViewById也到调用了100次呀,也挺多的,能不能减少呢,答案是肯定的。
解决办法:
定义一个ViewHolder,将convertView的tag设置为ViewHolder,不为空时重新使用
ViewHolder只是将需要缓存的那些view封装好,convertView的setTag才是将这些缓存起来供下次调用
当你的listview里布局多样化的时候 viewholder的作用就有比较明显的体现了。 当然了,单一模式的布局一样有性能优化的作用 只是不直观。
VH就是个静态类 与缓存无关的
public View getView(int position, View convertView, ViewGroup parent) { ViewHolder viewHolder = null; //缓存:convertView调用的次数为第一屏item的数目 //findViewById调用的次数为第一屏item的数目 //滑动的时候convertView的布局不一样,所以需要加个判断 //如果是ViewGroup(即TextView的父容器)的实例就从Tag中取 if (convertView != null && convertView instanceof ViewGroup) { viewHolder = (ViewHolder) convertView.getTag(); }else { convertView = View.inflate(MainActivity.this, R.layout.myitem, null); viewHolder = new ViewHolder(); viewHolder.tv = (TextView) convertView.findViewById(R.id.tv); convertView.setTag(viewHolder); } viewHolder.tv.setText(data); return convertView; } static class ViewHolder { public TextView tv; }
为什么要这样写呢
if (convertView != null && convertView instanceof ViewGroup) {}
因为头的布局和item的布局不一样,不能够全部复用,所以要判断下。
这样就又减少了findViewById的次数了,又再一次优化了。
优化完成了,接下来实现分类显示,首先分析:
数据0-99,分成奇数和偶数,所以各有50个,头的位置分别加在 position = 0 的时候和 position = 51 的时候。
所以:
public View getView(int position, View convertView, ViewGroup parent) { String data; if (position == 0) { //position为0时加载头,结束本次方法调用 TextView textView = new TextView(getApplicationContext()); textView.setText("奇数" + jiDatas.size()); return textView; }else if (position <= jiDatas.size()) { //position为[1-50]时加载奇数数据 data = jiDatas.get(position - 1); }else if (position == jiDatas.size() + 1) { //position为51时加载头,结束本次方法调用(因为奇数为50条) TextView textView = new TextView(getApplicationContext()); textView.setText("偶数" + ouDatas.size()); return textView; }else { //position为[52-101]时加载偶数数据 data = ouDatas.get(position-jiDatas.size()-2); } return convertView ; }
到这里就已经实现了上面的效果。
核心代码:
activity_main.xml
<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" tools:context="com.example.listview.MainActivity" > <ListView android:id="@+id/listView" android:layout_width="wrap_content" android:layout_height="wrap_content"> </ListView> </RelativeLayout>
public class MainActivity extends Activity { private ListView listView; //总的数据的集合 private List<String> data; //存放奇数的集合 private List<String> jiDatas; //存放偶数的集合 private List<String> ouDatas; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); listView = (ListView) findViewById(R.id.listView); data = new ArrayList<String>(); jiDatas = new ArrayList<String>(); ouDatas = new ArrayList<String>(); //将data进行奇数和偶数分类(模拟数据) for (int i = 0; i < 100; i++) { if (i%2 == 0) { ouDatas.add(i+""); //偶数 }else { jiDatas.add(i+""); //奇数 } } //初始化adapter MyAdapter adapter = new MyAdapter(); //为listView设置适配器 listView.setAdapter(adapter); } class MyAdapter extends BaseAdapter { @Override public int getCount() { return 1+jiDatas.size()+1+ouDatas.size(); } @Override public Object getItem(int position) { return data.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { // //普通式 // if (convertView == null) { // convertView = View.inflate(MainActivity.this, R.layout.myitem, null); // } // TextView tv = (TextView) convertView.findViewById(R.id.tv); // tv.setText(data.get(position)); String data; if (position == 0) { //position为0时加载头,结束本次方法调用 TextView textView = new TextView(getApplicationContext()); textView.setText("奇数" + jiDatas.size()); return textView; }else if (position <= jiDatas.size()) { //position为[1-50]时加载奇数数据 data = jiDatas.get(position - 1); }else if (position == jiDatas.size() + 1) { //position为51时加载头,结束本次方法调用(因为奇数为50条) TextView textView = new TextView(getApplicationContext()); textView.setText("偶数" + ouDatas.size()); return textView; }else { //position为[52-101]时加载偶数数据 data = ouDatas.get(position-jiDatas.size()-2); } //文艺式 ViewHolder viewHolder = null; //缓存:convertView调用的次数为第一屏item的数目 //findViewById调用的次数为第一屏item的数目 //滑动的时候convertView的布局不一样,所以需要加个判断 //如果是ViewGroup(即TextView的父容器)的实例就从Tag中取 if (convertView != null && convertView instanceof ViewGroup) { viewHolder = (ViewHolder) convertView.getTag(); }else { convertView = View.inflate(MainActivity.this, R.layout.myitem, null); viewHolder = new ViewHolder(); viewHolder.tv = (TextView) convertView.findViewById(R.id.tv); convertView.setTag(viewHolder); } viewHolder.tv.setText(data); // //逗比式 // View view = view = View.inflate(MainActivity.this, R.layout.myitem, null); // TextView tv = (TextView) view.findViewById(R.id.tv); // tv.setText(data.get(position)); return convertView; } } static class ViewHolder { public TextView tv; } }
<?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/tv" android:layout_width="match_parent" android:layout_height="100dp" android:textSize="20sp" android:gravity="center"/> </LinearLayout>
标签:
原文地址:http://blog.csdn.net/yuanlongquan753/article/details/51473776