ListView进阶之使用技巧和优化策略

1      ListView进阶

1.1    ListView的效率优化

1、通过ContentView实现优化,减少创建View的次数

2、通过ViewHolder实现优化,减少遍历查找控件的次数

 

getView方法优化(ListView的优化,convertView的重用)

 

getView方法的运行特点:

1.      在列表初始显示时一次性运行n次,n的大小取决于初始显示时能够显示多少个item条目

注意:此处的显示指的是只要item的一点点边缘显示出来就算做1个

2.       每当由于上下滑动造成某条目移动出屏幕再重新回来的时候,该position位置的条目也会再次运行getView方法

 

优化处理的原因

由于上方getView方法的运行特点,决定了同一位置的View对象会被多次重复创建,一旦

View对象里面存储的数据内容比较大,又多次重复创建,易引发OutOfMemorryError,内存溢出错误。未解决内存上的优化提出了getView优化的思想

 

 

优化处理的核心思想

getView方法的参数二convertView,该参数的特点:

1.      系统自动传递过来的值

2.      当ListView初始显示是为null

3.       一旦ListView开始滑动,那么converView使用的就是被滑动出屏幕的View对象

 

ListView的效率优化

         1、通过ContentView实现优化,减少创建View的次数

         2、通过ViewHolder实现优化,减少遍历查找控件的次数

 

使用优化后最直观的效果:

假设getView方法来回滑动运行100次,如果没有使用优化,那么将在内存中创建100个View对象进行存储,但是一旦使用了优化,那么内存中可能只会创建10个或者5,6个View对象,达到了内存优化的效果

 

 

优化的实现步骤:

1.       判断convertView对象是否为null,如果为null,那么通过LayoutInflater对象进行初始化并且将convertView对象作为返回值返回值

 此步骤实现了减少被返回的View对象的初始化次数

2.       创建一个类(通常此类名为ViewHolder),在该类中定义n条属性,

定义的属性取决于item的布局文件中有多少控件

3.      在getView方法中声明一个ViewHolder对象,在convertView为null的时候,进行初始化,并初始化holder中所有的控件属性,

4.       初始化完成之后通过convertView的setTag方法将holder对象整体的存储起来

第2,3,4步骤的目的是减少item中控件的初始化次数

5.      当convertView不为null时,通过getTag方法取出之前存储的ViewHolder对象

6.       通过holder调用属性设置控件的显示内容即可

 

1.1.1  ListView优化代码

public class Item {

 

         privateint imageId;

         privateString name;

         privateString text;

         publicint getImageId() {

                   returnimageId;

         }

}

public class MainActivity extends Activity{

 

ListView lv;

        

         ArrayListlist = new ArrayList();

         int[]imgs = {R.drawable.pic0,R.drawable.pic1,R.drawable.pic2,R.drawable.pic3,R.drawable.pic4};

 

         privateLayoutInflater inflater;

         @Override

         protectedvoid onCreate(Bundle savedInstanceState) {

                   super.onCreate(savedInstanceState);

                   setContentView(R.layout.activity_main);

                  

                   lv= (ListView)findViewById(R.id.lv);

                  

                   for(int i=0;i<30;i++) {

                           

                            list.add(newItem(imgs[i%imgs.length], "name"+i, "texxt"+i));

                   }

                   inflater= LayoutInflater.from(this);

                   //设置适配器

                   lv.setAdapter(newMyAdapter());

         }

        

         classMyAdapter extends BaseAdapter {

 

                  

 

                   @Override

                   publicint getCount() {

                            //TODO Auto-generated method stub

                            returnlist.size();

                   }

 

                   @Override

                   publicObject getItem(int position) {

                            //TODO Auto-generated method stub

                            returnnull;

                   }

 

                   @Override

                   publiclong getItemId(int position) {

                            //TODO Auto-generated method stub

                            return0;

                   }

 

                   @Override

                   publicView getView(int position, View convertView, ViewGroup parent) {

                            //TODO Auto-generated method stub

                            ViewHolderhodler;

                            //1.判断convertView对象是否为null

                            if (convertView == null) {

                                     Log.i("====","======   getview convertView isnull "+position );

                                     //3.当convertView为null时初始化convertView对象,holder对象,以及holder中的所有属性

                                     convertView= inflater.inflate(R.layout.item, null);

                                    

                                     hodler = newViewHolder();

                                     hodler.iv= (ImageView)convertView.findViewById(R.id.imageView1);

                                     hodler.tv= (TextView)convertView.findViewById(R.id.textView1);

                                     hodler.tv2= (TextView)convertView.findViewById(R.id.textView2);

                                     //4.通过setTag方法将holder对象存入convertView对象的tag属性中

                                     convertView.setTag(hodler);

                                      

                            }else {

                                     Log.i("====","======   getview convertView"+position + convertView.hashCode());

                                     //5.取出convertView对象的tag属性中之前存储好的holder对象进行重用

                                     hodler = (ViewHolder) convertView.getTag();

                            }

                           

                            Itemitem = list.get(position);

                            //6.通过holder对象取出对应的控件,设置显示即可

                            hodler.iv.setImageResource(item.getImageId());

                            hodler.tv.setText(item.getName());

                            hodler.tv2.setText(item.getText());

//                         Log.i("===","=====  return view "+v.hashCode());

                            returnconvertView;

                   }

                   //2.自定义ViewHodler对象存储item布局中的所有控件

                   classViewHolder {

                            ImageViewiv;

                            TextViewtv;

                            TextViewtv2;

                   }

         }

        

}

1.1.2  ListView_ay优化及刷新练习

public class Student {

 

         private String name;

         private int age;

         public Student(String name, int age) {

                   super();

                   this.name = name;

                   this.age = age;

         }

}

/**

 * 实现练习需求如下:

 * 先在ListView中显示10条学生数据,后期可以通过在输入框中输入内容后,点击添加按钮,实现数据的

 * 添加,同时可以通过listview的item的点击事件实现学生数据的删除

 */

public class MainActivity extendsActivity {

 

         ListView lv;

         EditText et1;

         EditText et2;

        

         ArrayList list = new ArrayList();

         private StudentAdapter adapter;

         @Override

         protected void onCreate(Bundle savedInstanceState) {

                   super.onCreate(savedInstanceState);

                   setContentView(R.layout.activity_main);

                  

                   initView();

                  

                   for (int i=0;i<10;i++) {

                            list.add(new Student("name"+i, i+10));

                   }

                   adapter = new StudentAdapter();

                   lv.setAdapter(adapter);

                  

                   lv.setOnItemClickListener(new OnItemClickListener() {

 

                            @Override

                            public void onItemClick(AdapterView parent, View view,

                                               int position, long id) {

                                     // TODO Auto-generated method stub

                                     list.remove(position);

                                     adapter.notifyDataSetChanged();

                            }

                   });

         }

 

 

         private void initView() {

                   lv = (ListView)findViewById(R.id.listView1);

                   et1 = (EditText)findViewById(R.id.editText1);

                   et2 = (EditText)findViewById(R.id.editText2);

         }

        

        

         public void click (View v) {      

                   //将数据添加到列表中进行显示

//               list.add(new Student(n, a));

                   //如果想要让新添加的数据显示在指定位置

                   if( TextUtils.isEmpty(et1.getText().toString())&& TextUtils.isEmpty(et2.getText().toString())){

                            String n = et1.getText().toString();

                            int a =Integer.parseInt(et2.getText().toString());

                            list.add(0, new Student(n, a));

                            adapter.notifyDataSetChanged();

                   }else{

                            return;

                   }

                  

                  

         }

 

         class StudentAdapter extendsBaseAdapter {

 

                   @Override

                   public int getCount() {

                            // TODO Auto-generated method stub

                            return list.size();

                   }

 

                   @Override

                   public Object getItem(intposition) {

                            // TODO Auto-generated method stub

                            return null;

                   }

 

                   @Override

                   public long getItemId(intposition) {

                            // TODO Auto-generated method stub

                            return 0;

                   }

 

                   @Override

                   public View getView(intposition, View convertView, ViewGroup parent) {

                            // TODO Auto-generated method stub

                            ViewHolder holder;

                            if(convertView == null) {

                                     convertView = View.inflate(MainActivity.this, R.layout.item, null);

                                    

                                     holder = new ViewHolder();

                                     holder.name =(TextView)convertView.findViewById(R.id.textView1);

                                     holder.age =(TextView)convertView.findViewById(R.id.textView2);

                                    

                                     convertView.setTag(holder);

                            } else {

                                     holder = (ViewHolder)convertView.getTag();

                            }

                           

                            Student stu = list.get(position);

                            holder.name.setText(stu.getName());

                            holder.age.setText(stu.getAge()+"");

                            returnconvertView;

                   }

                  

                   class ViewHolder {

                            TextView name;

                            TextView age;

                   }

                                    

         }

        

}

 

 

1.2    ListView问题处理

ListView中常见bug

 

1.      当item中包含checkbox或者button这些具备默认点击效果的控件时,易引发的bug:

造成ListView无法接收ItemClick事件,

原因:checkbox或者button这些具备默认点击效果的控件默认抢占页面焦点

解决方式:使item具备焦点的能力

                   解决方式一:找到对应抢占焦点的控件,给该控件的标签添加一条androidfocusable=false,让当前控件失去焦点

                  

                   解决方式二:在item布局的根标签中添加属性:android:descendantFocusability="blocksDescendants"  ,屏蔽根标签内所有子控件 的焦点

 

2.       当item中包含CheckBox控件时,如果想要实现CheckBox本身单独的点击事件,那么

只需在getView方法中通过holder对象直接调用CheckBox对象进行设置即可

3.      当item中包含CheckBox控件时,如果想要实现即使点击是CheckBox控件区域也算做listveiw的itemclick事件的话,

实现方式:在CheckBox标签中添加属性:android:clickable = false

 

4.       当item中包含CheckBox控件并且列表比较长,多次上下滚动后,CheckBox的选中效果会乱串

产生原因: 由于convertView的复用带来,新滑动入屏幕的item的View对象复用了之前CheckBox选中那一行的View对象

解决方式:

在处理checkbox的选中事件时,同时将选中内容通过一个集合对象存储起来,在getView中,通过当前行的显示内容与选中集合进行判断,符合条件的,让当前行的checkbox选中,反之,设置checkbox非选中

public class MainActivity extends Activity{

 

         ListViewlv;

        

         ArrayListlist = new ArrayList();

 

         privateMyCheckAdapter adapter;

        

         //用于存储所有选中数据

         ArrayListselect = new ArrayList();

        

         @Override

         protectedvoid onCreate(Bundle savedInstanceState) {

                   super.onCreate(savedInstanceState);

                   setContentView(R.layout.activity_main);

                  

                   lv= (ListView)findViewById(R.id.lv);

                  

                   for(int i=0;i<30;i++) {

                            list.add("item"+i);

                   }

                   adapter= new MyCheckAdapter(list,this,select);

                   lv.setAdapter(adapter);

                  

                   lv.setOnItemClickListener(newOnItemClickListener() {

 

                            @Override

                            publicvoid onItemClick(AdapterView parent, View view,

                                               intposition, long id) {

                                     //TODO Auto-generated method stub

                                     Toast.makeText(MainActivity.this,"click   !!",Toast.LENGTH_SHORT).show();

                                     //手动处理CheckBox的选中效果

                                     //取出被点击的那一行中的CheckBox对象

                                     CheckBoxcb = (CheckBox) view.findViewById(R.id.checkBox1);

                                     //取出点击时cb的当前效果,并根据当前效果将cb设置为相反效果

                                     if(cb.isChecked()) {

                                               cb.setChecked(false);

                                               //根据checkbox的选中效果,针对select集合进行相应的操作

                                               select.remove(list.get(position));

                                     }else {

                                               cb.setChecked(true);

                                               //根据checkbox的选中效果,针对select集合进行相应的操作

                                               select.add(list.get(position));

                                     }

                                    

                            }

                   });

         }

        

         //实现点击button,同时删除多条选中内容的效果

         publicvoid click (View v) {

                   list.removeAll(select);

                   adapter.notifyDataSetChanged();

                   select.clear();

         }

}

 

1.3    接口回调

实现的作用:

想要在不同类中针对必然会调用的同一个方法,实现不同的方法体处理代码

 

实现步骤:

1.       创建一个接口和一个抽象方法,方法的参数列表定义与onPostExecute方法的参数列表一致即可

2.       在异步任务类中,定义接口类型的变量,并且通过构造方法给该变量赋值

3.       onPostExecute方法中通过接口对象调用抽象方法

4.       在对应Activity页面中实现上方接口重写相关方法,启动异步任务的时候通过构造方法传递该接口的实现子类

 

通过以上步骤即可实现,当在不同的页面中启动页面任务获取下载结果后,针对下载结果进行不同的操作

 

1.3.1  List获取网络数回调

public class TextTask extendsAsyncTaskArrayList>{

 

 

        

         public interfaceLoadFinishListener {

                   void onLoadFinish(ArrayListresult);

         }

 

         LoadFinishListenerani;

         publicTextTask(LoadFinishListener ani) {

                   //TODO Auto-generated constructor stub

                   this.ani = ani;

         }

         @Override

         protectedArrayList doInBackground(String... params) {

                   //TODO Auto-generated method stub

                   Log.i("===","====  donin");

                   Stringjson = new HttpUtils().getJson(params[0]);

                   if(json != null) {

                            //连网成功,可以继续进行解析

                            returnnew JsonUtils().getList(json);

                   }

                   returnnull;

         }

        

         @Override

         protected void onPostExecute(ArrayList result) {

                   //TODO Auto-generated method stub

                   super.onPostExecute(result);

                   Log.i("===","====  result");         

                   ani.onLoadFinish(result);      //通过接口回调把解析后的数据对外提供

                  

         }

}

1.4    自定义适配器的完整实现步骤总结

自定义适配器:

1、定义类继承自BaseAdapter

2、改写getCount()方法:返回Item的数量

3、改写getItem()方法:返回当前Item对应的数据对象

4、改写getItemId()方法:返回当前Item的id

5、改写getView()方法:返回当前Item对应的View对象

         1)通过布局填充器将指定的布局文件填转换成View对象

         2)在View对象中查找相应的控件

         3)设置各个控件的显示内容

         4)效率优化

                   a、使用缓存View对象,减少创建View对象的次数

                   b、使用持有者模式,减少遍历查找控件的次数

         5)返回View对象

        

1.5    案例:ListView实现多选功能

去除标题栏和通知栏

         通过设置主题实现

无标题主题:@android:style/Theme.Light.NoTitleBar

全屏主题:@android:style/Theme.Light.NoTitleBar.Fullscreen

         通过代码实现

去除标题栏:Activity.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);

去除通知栏:Activity.requestWindowFeature(Window.FEATURE_NO_TITLE);

PS:以上两行代码必须在setContentView(intresId)方式之前执行,不然会报错

 

1.6    ListView分页显示

设置滑动监听:setOnScrollListener(OnScrollListener)

OnScrollListener接口

         onScrollStateChanged(AbsListViewview, int scrollState)

         当滑动状态发生改变的时候回调的方法

         参数1:view,被监听的对象

         参数2:scrollState,当前手指在屏幕上滑动的状态

                   SCROLL_STATE_IDLE:停止滑动

                   SCROLL_STATE_TOUCH_SCROLL:手指正在屏幕上滑动

                   SCROLL_STATE_:惯性滑动

onScroll(AbsListView view, intfirstVisibleItem, int visibleItemCount, int totalItemCount)

         当正在滑动的时候回调的方法

         参数1:view,被监听的对象

         参数2:firstVisibleItem,当前界面中第一个可见的item的下标

         参数3:visibleItemCount,当前界面中可见的item的数量

         参数4:totalItemCount,当前ListView中item的总数

当(firstVisibleItem + visibleItemCount =totalItemCount)时,表示ListView已滑到最底部

1.7    Android下的单元测试(AndroidJUnit Test)

1、定义测试类继承自AndroidTestCase

2、在清单文件中添加

        

                   android:name="android.test.InstrumentationTestRunner"

                   android:targetPackage="包名" >

        

3、在清单文件的application节点下添加

        

4、在测试类中定义测试方法,并使用AndroidJunit Test 运行指定的测试方法

你可能感兴趣的:(Android移动开发)