android 学习笔记4-ListView使用

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;
            
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值