1、为什么使用ListView:
    我们使用new TextView对象显示数据的时候,会一次性查询大量的数据,创建大量的类对象保存数据,创建大量的TextView显示数据
    这样我们手机的内存会接受不了。
    
    为了节省内存消耗,我们在界面上显示多少条就创建多少个TextView,不显示的就销毁掉,自己实现比较复杂,google给我们提供了ListView 这个组件
    
    
    
2、ListView使用方法:
    a、首先添加布局文件
     

<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=".MainActivity"  

             android:orientation="vertical" 

             > 

             <ListView  

                 android:id="@+id/lv" 

                 android:layout_width="match_parent" 

                 android:layout_height="match_parent" 

                 ></ListView> 

         </LinearLayout>


    
    b、找到listview
ListView lv = (ListView) findViewById(R.id.lv);
    c、设置显示内容
lv.setAdapter(new MyAdapter());//这里设置一个适配器,是ListAdpater(接口)。
        我们一般不直接new一个接口,因为要实现里面所有方法,没必要。系统肯定提供了实现类来让我们继承,一般都是Base开头或Default开头的类(ctrl+T查看)。
        所以创建MyAdapter类继承自BaseAdapter接口,实现里面几个方法。也可以使用匿名内部类,为了清晰重新创建一个类
        
 

//主要自己实现2个方法 

         class MyAdapter extends BaseAdapter{ 



             //给系统调用的,用来获知有多少条数据 

             @Override 

             public int getCount() { 

                 return personList.size(); 

             } 

              



             //系统调用,返回的View对象就会作为ListView的一个条目显示在屏幕上 

             //position:该次getView调用返回的View对象在ListView中是第几个条目,position的值就是几 

             @Override 

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

                 System.out.println("getView方法调用" + position); 

                 Person p = personList.get(position);//从集合中取数据 

                  

     
TextView tv = new TextView(MainActivity.this); 

     
tv.setText(p.toString()); 

     
tv.setTextSize(12); 



                 return tv;//返回的是一个View对象,就是说所有布局文件都可以作为一个条目,可以实现复杂的界面。 

             } 

              

             @Override 

             public Object getItem(int position) {//暂时不管,使用默认返回就好了 

                 return null; 

             } 



             @Override 

             public long getItemId(int position) {//暂时不管,使用默认返回就好了 

                 return 0; 

             } 

              

         }


        
    d、这样就完成了,界面显示多少条数据,就调用多少次getView方法创建,没有显示的部分就被销毁了,这样就可以达到节省内存的目的。不过界面比较丑。
    
    
    
3、ListView使用方法-使用自定义布局,让界面更美观


    a、我们需要给每个条目设置下布局,重新创建一个item_layout.xml的布局文件

<?xml version="1.0" encoding="utf-8"?> 

         <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 

             android:layout_width="match_parent" 

             android:layout_height="match_parent" > 

              

             <TextView  

                 android:id="@+id/tv_name" 

                 android:layout_width="wrap_content" 

                 android:layout_height="wrap_content" 

                 android:text="name" 

                 android:textSize="20sp" 

                 /> 

             <LinearLayout  

                 android:layout_width="wrap_content" 

                 android:layout_height="wrap_content" 

                 android:orientation="vertical"//竖直排列 

                 android:layout_alignParentRight="true"//靠右对齐, 

                 > 

                 <TextView  

                     android:id="@+id/tv_phone" 

                     android:layout_width="wrap_content" 

                     android:layout_height="wrap_content" 

                     android:text="phone" 

                     /> 

                 <TextView  

                     android:id="@+id/tv_salary" 

                     android:layout_width="wrap_content" 

                     android:layout_height="wrap_content" 

                     android:text="salary" 

                     /> 

             </LinearLayout> 

         </RelativeLayout>


        
    b、我们重新改写下MyAdapter类里面的代码:将返回的条目设置为我们自己的布局
 

//主要自己实现2个方法 

         class MyAdapter extends BaseAdapter{ 



             //给系统调用的,用来获知有多少条数据 

             @Override 

             public int getCount() { 

                 return personList.size(); 

             } 

              



             @Override 

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

                 System.out.println("getView方法调用" + position); 

                 Person p = personList.get(position);//从集合中取数据 

                  

                 View view = View.inflate(MainActivity.this, R.layout.item_layout, null);//填充自定义的xml布局 

                  

                  

                 //这样会报错的,空指针异常,找不到tv_name,因为系统会去在主的xml中去找,主的xml没有定义这个id 

                 //TextView tv_name = (TextView) findViewById(R.id.tv_name); 

                  

                 //所以必须使用view.findViewById 

                 TextView tv_name = (TextView) view.findViewById(R.id.tv_name); 

                 tv_name.setText(p.getName()); 

                  

                 TextView tv_phone = (TextView) view.findViewById(R.id.tv_phone); 

                 tv_phone.setText(p.getPhone()); 

                  

                 TextView tv_salary = (TextView) view.findViewById(R.id.tv_salary); 

                 tv_salary.setText(p.getSalary() + "");//不能用整型,否则会被做为id使用 

              

                 return view; 

             } 

              

             @Override 

             public Object getItem(int position) { 

                 return null; 

             } 



             @Override 

             public long getItemId(int position) { 

                 return 0; 

             } 

              

         }


    
    c、这样就改造完成了,运行也没有问题。
    
    备注:获取View对象有3中写法,只是写法不一样,其实都是获取系统服务搞定的
        1、View view = View.inflate(MainActivity.this, R.layout.item_layout, null);
        2、使用布局管理器
            LayoutInflater inflater = LayoutInflater.from(MainActivity.this);//获取布局填充器
            View view = inflater.inflate(R.layout.item_listview, null);
        3、获取系统服务方式:
            LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
            View view = inflater.inflate(R.layout.item_listview, null);
    程序不断快速创建View对象,都来不及销毁,会出现程序崩溃,OutOfMemory错误
        
4、ListView缓存-很重要
    上面虽然可以实现这个功能,但是在用户非常快速的滑动时候,程序不断快速创建View对象,都来不及销毁,会出现程序崩溃,OutOfMemory错误

//convertView:系统之前缓存的条目,这个就是缓存 

     public View getView(int position, View convertView, ViewGroup parent)  

      

     我们在实现getView方法时候判断是否有缓存 

     例如: 

         View view = null; 

         if(convertView == null){ 

             //把布局文件填充成view对象 

             view = View.inflate(MainActivity.this, R.layout.item_listview, null); 

             //填充布局的数据千万别写在这里,因为缓存里面全是第一次创建时候填充的数据,后面条目显示的就是之前的数据了 

         }else{ 

             view = convertView; 

         } 

         TextView tv_name = (TextView) view.findViewById(R.id.tv_name); 

         tv_name.setText(p.getName()); 

          

         TextView tv_phone = (TextView) view.findViewById(R.id.tv_phone); 

         tv_phone.setText(p.getPhone()); 

          

         TextView tv_salary = (TextView) view.findViewById(R.id.tv_salary); 

         tv_salary.setText(p.getSalary() + "");//不能用整型,否则会被做为id使用


    

注意: 屏幕能显示多少个条目系统就只会缓存多少个,不然把所有的条目缓存起来就失去了节省内存的意义了。   

      

      

 5、ArrayAdapter-只能处理文本 

     是BaseAdapter的子类,功能就没那么的强大了 

     我们看到BaseAdapter是封装度最差的,因此他的自由度更高,里面的方法都是我们自己实现的,可以实现更复杂的布局 

      

     例如: 

         String[] arr = new String[]{"name1","name2"}; 

 

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

lv.setAdapter(new ArrayAdapter<String>(this, R.layout.item_listview, //指定作为条目的布局文件 

R.id.tv, //指定文本显示在哪个文本框 

arr)); 

      

     备注:这种只能显示同样的图片,不同的文本 

      

      

 6、SimpleAdapter-比ArrayAdapter强大一点 

     把每个条目需要处理的所有数据封装至map中,在把map封装至list中 

     这样就保证了list每个元素都包含了一个条目需要的所有数据 

      

     例如: 

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

         //把每个map集合存放到ArrayList集合中,每一个map就是一个条目数据 

         //String用来存放key只,Object用来存放对象,因为不知道要存字符或是图片,所以用Object 

List<Map<String, Object>> data = new ArrayList<Map<String,Object>>(); 

 

Map<String, Object> map1 = new HashMap<String, Object>(); 

map1.put("name", "张三"); 

map1.put("photo", R.drawable.photo1); 

data.add(map1); 

 

Map<String, Object> map2 = new HashMap<String, Object>(); 

map2.put("name", "李四"); 

map2.put("photo", R.drawable.photo2); 

data.add(map2); 

 

Map<String, Object> map3 = new HashMap<String, Object>(); 

map3.put("name", "王五"); 

map3.put("photo", R.drawable.photo3); 

data.add(map3); 

 

lv.setAdapter(new SimpleAdapter(this, data, R.layout.item_listview,  

new String[]{"name", "photo"}, //存放键的数组 

new int[]{R.id.tv, R.id.iv}));//存放资源id的数组,两个数组用下标来对应 

}



    
    主xml:

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" 

             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=".MainActivity" > 



             <ListView 

                 android:id="@+id/lv" 

                 android:layout_width="wrap_content" 

                 android:layout_height="wrap_content" 

                 /> 



         </RelativeLayout> 



     xml文件:item_listview.xml 

         <?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="wrap_content" 

             android:orientation="horizontal" > 

             <ImageView  

                 android:id="@+id/iv" 

                 android:layout_width="60dp" 

                 android:layout_height="60dp" 

                 android:src="@drawable/photo2" 

                 /> 

             <TextView  

                 android:id="@+id/tv" 

                 android:layout_width="wrap_content" 

                 android:layout_height="wrap_content" 

                 android:text="name" 

                 android:textSize="30sp" 

                 android:layout_gravity="center_vertical" 

                 /> 

         </LinearLayout>


    
7、ListView缓存优化:
    当我们显示的每一条条目比较复杂的时候,使用findViewById也是一个比较耗费cpu资源的操作,我们也可以把查找id也缓存起来
    
    原始的代码:
 

View view = null; 

         if(convertView == null){ 

             //把布局文件填充成view对象 

             view = View.inflate(MainActivity.this, R.layout.item_listview, null); 

             //填充布局的数据千万别写在这里,因为缓存里面全是第一次创建时候填充的数据,后面条目显示的就是之前的数据了 

         }else{ 

             view = convertView; 

         } 

         TextView tv_name = (TextView) view.findViewById(R.id.tv_name); 

         tv_name.setText(p.getName()); 

          

         TextView tv_phone = (TextView) view.findViewById(R.id.tv_phone); 

         tv_phone.setText(p.getPhone()); 

          

         TextView tv_salary = (TextView) view.findViewById(R.id.tv_salary); 

         tv_salary.setText(p.getSalary() + "");//不能用整型,否则会被做为id使用


    优化后的代码:
        我们需要创建一个缓存id的类,把所有组件封装在这个类中

class ViewHolder{ 

TextView tv_name; 

TextView tv_phone; 

TextView tv_salary; 

} 

          

         把找第一次找id后,将id使用view.setTag关联到view中 

         View view = null; 

ViewHolder mHolder = null; 

if(convertView == null){ 

//如何填充的 

view = View.inflate(MainActivity.this, R.layout.item_listview, null); 

 

//创建viewHoler封装所有条目使用的组件 

mHolder = new ViewHolder(); 

mHolder.tv_name = (TextView) v.findViewById(R.id.tv_name); 

mHolder.tv_phone = (TextView) v.findViewById(R.id.tv_phone); 

mHolder.tv_salary = (TextView) v.findViewById(R.id.tv_salary); 

 

//把viewHolder封装至view对象中,这样view被缓存时,viewHolder也就被缓存了 

view.setTag(mHolder); 

} 

else{ 

view = convertView; 

//从view中取出保存的viewHolder,viewHolder中就有所有的组件对象,不需要再去findViewById 

mHolder = (ViewHolder) view.getTag(); 

} 

//给条目中的每个组件设置要显示的内容 

mHolder.tv_name.setText(p.getName()); 

mHolder.tv_phone.setText(p.getPhone()); 

mHolder.tv_salary.setText(p.getSalary() + ""); 

 

return view;