今天就来说说ListView这个组件吧,这个组件相信每个App都会涉及到~当然或许很多人已经知道 ListView 的优化。在这里我在详细的说一下listview的优化,顺便让ListView分类显示~~让效果更好~
先来看效果图:
从这个图中可以看出,可以看到数据有100个(0-99),奇数和偶数分类显示,并给每类数据加了一个头~显示是奇数还是偶数的。
步骤:
1.在activity_main布局中编写listview组件:
<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" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="com.example.listview.MainActivity" > <ListView android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/listview"></ListView> </RelativeLayout>2.在MainActivity里为listview设置适配器
private ListView listview; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); listview=(ListView) findViewById(R.id.listview); listview.setAdapter(new MyAdapter()); }3.ListView设置数据,这里就使用模拟数据(0-99),并把数据分成两类(奇数和偶数)
private List<String>jiData;//奇数 private List<String>ouData;//偶数 jiData=new ArrayList<String>(); ouData=new ArrayList<String>(); for(int i=1;i<=100;i++){ if(i%2==0) ouData.add(i+""); else jiData.add(i+""); }4.然后要为MyAdapter继承BaseAdapter,实现4个方法~
(1)public int getCount() {} 代表的是返回listView有多少个条目
(2)public Object getItem(int position) {} 指的是返回位置为position的条目
(3)public long getItemId(int position) {} 指的是返回位置为position的条目的Id
(4)public View getView(int position, View convertView, ViewGroup parent) {} 指的是返回每个条目的视图
我们最重要的是第4个方法-getView方法,前面几个这样写:
@Override public int getCount() { // TODO Auto-generated method stub return 1+jiData.size()+1+ouData.size(); } @Override public Object getItem(int position) { // TODO Auto-generated method stub //俩者效果一样 return null; //return data.get(position); } @Override public long getItemId(int position) { // TODO Auto-generated method stub return position; }现在来看看getView方法,在这个方法里我们要对listview优化和分类显示,所以代码量有点多~
先来分析一下一点都不优化的方式-逗比式:
@Override public View getView(int position, View convertView, ViewGroup parent) { // TODO Auto-generated method stub /*LayoutInflater layoutInflater=LayoutInflater.from(MainActivity.this); View view=layoutInflater.inflate(R.layout.listview, null);*/ //不优化,逗比式 System.out.println(convertView); View view=View.inflate(MainActivity.this, R.layout.listview, null); TextView tv=(TextView)view.findViewById(R.id.tv); text=data.get(position); tv.setText(text); return view; }
我们知道getView的调用次数,是有多少个条目就调用多少次吧,那么上面这么写的加载布局和findViewById就会调用100次吧,而每个条目的布局都一样,为什么要重复加载呢,所以要想办法减少加载布局的次数,这样更优化,那该怎么做呢。
我们可以看到getView方法有三个参数,第二个参数convertView还没有使用到,对,有人应该知道就是缓存。
convertView工作原理:
这里屏幕只显示8个条目,你的可能不是,因为每个手机的屏幕大小不一样嘛,不过原理都是一样的。
看到这里我们知道了convertView,所以接下来可以使用convertView来减少加载布局的次数:
那就是所谓的普通式:
@Override public View getView(int position, View convertView, ViewGroup parent) { // TODO Auto-generated method stub /*LayoutInflater layoutInflater=LayoutInflater.from(MainActivity.this); View view=layoutInflater.inflate(R.layout.listview, null);*/ //普通式,虽然对视图进行了优化,不用每次都重新加载,但是每次都要findViewById if(convertView==null){ convertView=View.inflate(MainActivity.this, R.layout.listview, null); } TextView tv=(TextView)convertView.findViewById(R.id.tv); text=data.get(position); tv.setText(text); return convertView; }
这样加载视图就只会调用它第一屏的条目的数量。比如:第一屏显示8个条目,加载视图会调用8次,以后滑动就不会调用了。
到这里看起来是可以了,但是我们可以注意到findViewById也到调用了100次呀,也挺多的,能不能减少呢,答案是肯定的。
那这就是所谓的 高档式:定义一个ViewHolder,将convertView的tag设置为ViewHolder,不为空时重新使用
ViewHolder只是将需要缓存的那些view封装好,convertView的setTag才是将这些缓存起来供下次调用
当你的listview里布局多样化的时候 viewholder的作用就有比较明显的体现了。 当然了,单一模式的布局一样有性能优化的作用 只是不直观。
viewholder就是个静态类 与缓存无关的
@Override public View getView(int position, View convertView, ViewGroup parent) { // TODO Auto-generated method stub /*LayoutInflater layoutInflater=LayoutInflater.from(MainActivity.this); View view=layoutInflater.inflate(R.layout.listview, null);*/ //高档式 ViewHolder holder; String data ; if(convertView!=null&&convertView instanceof ViewGroup) { holder=(ViewHolder) convertView.getTag(); } else{ convertView=View.inflate(MainActivity.this, R.layout.listview, null); holder=new ViewHolder(); holder.tv=(TextView) convertView.findViewById(R.id.tv); convertView.setTag(holder);//setTag用于携带数据 } return convertView; }
//static--只执行一次,而且先执行 static class ViewHolder{ TextView tv; }
为什么要这样写呢
if (convertView != null && convertView instanceof ViewGroup) {}
因为头的布局和item的布局不一样,不能够全部复用,所以要判断下。
这样就又减少了findViewById的次数了,又再一次优化了。
优化完成了,接下来实现分类显示,首先分析:
数据0-99,分成奇数和偶数,所以各有50个,头的位置分别加在 position = 0 的时候和 position = 51 的时候。
所以:
@Override public View getView(int position, View convertView, ViewGroup parent) { // TODO Auto-generated method stub /*LayoutInflater layoutInflater=LayoutInflater.from(MainActivity.this); View view=layoutInflater.inflate(R.layout.listview, null);*/ //高档式 ViewHolder holder; String data ; if(position==0){ TextView tv=new TextView(getApplicationContext()); tv.setText("奇数:"+jiData.size()); return tv; }else if(position<=jiData.size()){ data=jiData.get(position-1); }else if(position==jiData.size()+1){ TextView tv2=new TextView(getApplicationContext()); tv2.setText("偶数:"+ouData.size()); return tv2; }else{ data=ouData.get(position-1-jiData.size()-1); } return convertView; } }到这里就已经实现了上面的效果。
MainActivity:
public class MainActivity extends Activity { private ListView listview; private List<String>data; private List<String>jiData;//奇数 private List<String>ouData;//偶数 private String text; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); listview=(ListView) findViewById(R.id.listview); jiData=new ArrayList<String>(); ouData=new ArrayList<String>(); for(int i=1;i<=100;i++){ if(i%2==0) ouData.add(i+""); else jiData.add(i+""); } listview.setAdapter(new MyAdapter()); } class MyAdapter extends BaseAdapter{ @Override public int getCount() { // TODO Auto-generated method stub return 1+jiData.size()+1+ouData.size(); } @Override public Object getItem(int position) { // TODO Auto-generated method stub //俩者效果一样 return null; //return data.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 /*LayoutInflater layoutInflater=LayoutInflater.from(MainActivity.this); View view=layoutInflater.inflate(R.layout.listview, null);*/ //高档式 ViewHolder holder; String data ; if(position==0){ TextView tv=new TextView(getApplicationContext()); tv.setText("奇数:"+jiData.size()); return tv; }else if(position<=jiData.size()){ data=jiData.get(position-1); }else if(position==jiData.size()+1){ TextView tv2=new TextView(getApplicationContext()); tv2.setText("偶数:"+ouData.size()); return tv2; }else{ data=ouData.get(position-1-jiData.size()-1); } /*if(position<jiData.size()) data=jiData.get(position); else data=ouData.get(position-jiData.size());*/ if(convertView!=null&&convertView instanceof ViewGroup) { holder=(ViewHolder) convertView.getTag(); } else{ convertView=View.inflate(MainActivity.this, R.layout.listview, null); holder=new ViewHolder(); holder.tv=(TextView) convertView.findViewById(R.id.tv); convertView.setTag(holder);//setTag用于携带数据 } //text=data.get(position); //分偶数和奇数 holder.tv.setText(data); return convertView; } } //static--只执行一次,而且先执行 static class ViewHolder{ TextView tv; } }源码: 下载