Android ListView和Adapter优化

今天来说一下Android 中ListView控件 和使用Adapter 绑定数据。
现在分别列出两个对象的说明:

ListView

  • 通常我们如果加载比较多相同类型的数据,会使用ListView,使用ListView有说明好处呢。ListView是一个可以滚动的视图控件,加载一条一条的Item数据是很好的选择。为什么说它好。原因就是ListView 只有在可见的范围内才加载数据,一但数据被划出屏幕之外,数据就会进入缓存或者销毁,可以节省很多内存。如果传统做法的话,就需要一次性把所有数据加载到内存,系统负荷比较重。

Adapter

  • Adapter又是怎么东西呢?
  • 在我们需要使用ListView这样展示相同结构 Item 的时候,就需要用到Adapter来进行数据匹配,说通俗点就是:把数据绑定到一个列表类的视图中去。

    下面我先个简单Adapter

SimpleAdapter adapter; 

adapter=newSimpleAdapter(context,data,resource,from,to);`

这个Adapter传进来5个参数,下面分别对这些参数进行讲解:

  • context ——上下文对象(这个在这里我不详细讲解,网上很多资料)
  • data——这个参数要求的是一个List<Map<String,Object>>集合,里面装的数据是Map<String,Object>集合,通常我们需要把数据装到Map集合里面,再把Map集合装到List集合里面去即可。
  • resource ——指的是自定义的布局文件,里面需要设好对应控件的id
  • form ——是一个String数组,里面放的是你定义Map集合时用到的key值,数据顺序和后面一个参数要一一匹配
  • to ——resource参数对对应的布局文件中的控件 id ,和前一个参数一一对应。

可能有人还不不明白上面所说,下面给一个例子。

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

        Map<String,Object> map = new HashMap<String, Object>();
        map.put("name", "haha");
        data.add(map);


        SimpleAdapter adapter = new SimpleAdapter(this, data, R.layout.test, new String[]{"name"},new int[]{R.id.name});

上面一段代码的意思是将你放进map里面的key为name的值放到R.layout.test 布局中的 id 为 name 的控件中,如果有多条数据,就用for循环创建多个Map集合,把它添加到List集合即可,ListView会用布局文件作为一个Item的布局显示出来一个列表。

其实在开发中,用的最多的还是自定义Adapter ,自定义Adapter需要自定义一个类继承BaseAdapter,BaseAdapter有几个方法需要你实现:

假设我要放在listview数据已经存在一个List集合里面,名为list

  • public int getCount() ——返回你需要显示的数据条目的数量(list.size())
  • public Object getItem(int position )——返回要显示的数据中的第position个数据(list.get(position))
  • public long getItemId(int position)——-返回条目ID (position)
  • public View getView(int position, View convertView, ViewGroup parent) 该方法在需要显示数据的时候调用,即在滑动ListView的时候调用,传进来几个参数需要说明一下,position指的数第几个条目,converView指的是缓存view对象(一般条目被划出屏幕都会被缓存),ViewGroup指,父节点,一般不用理会。

说完几个需要实现的方法了,那么我们应该怎么做呢,其实很简单,getView方法就是要我们返回每一个Item执行的方法,在listview 每在屏幕中出现一个新的Item都会调用一次该方法。而且每一次都会把Item是第几个通过position参数传进来。我们在里面就可以通过
View v = View.inflate(MainActivity.this, R.layout.item, null); 方法来加载布局文件,同时通过该View对象找到里面的子控件。最后我们把这个View对象返回就是需要显示的Item了。

说那么多没什么用,用一个实例说明吧。

加入我有布局文件item.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="match_parent"
    android:orientation="horizontal" >

    <TextView
        android:id="@+id/name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    <TextView
        android:id="@+id/salary"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    <TextView
        android:id="@+id/phone"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</LinearLayout>

我需要以该布局作为一个item的布局,然后把数据显示出去。

 class MyAdapter extends BaseAdapter{
        private List<Person> list = new ArrayList<Persom>();
        public void MyAdapter(){
        for(int i =0 ;i< 10;i++){//初始化数据,姓名,薪水,电话号码
            list.add(new Person("哈哈"+110000+i,"1387894"+i))        
        }
        }
        @Override
        public int getCount() {
            return list.size();
        }

        @Override
        public Object getItem(int position) {
            return position;
        }

        @Override
        public long getItemId(int position) {

            return position;
        }

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



View v = View.inflate(MainActivity.this, R.layout.item, null);
            TextView name = (TextView) v.findViewById(R.id.name);
        TextView salary = (TextView) v.findViewById(R.id.salary);
          TextView phone = (TextView) v.findViewById(R.id.phone);
            Person p = list.get(position);
            name.setText(p.getName());
            salary.setText(p.getSalary());
            phone.setText(p.getPhone());
            return v;
        }
    }
}

这样简单的代码就能把数据绑定到ListView了。

其实这样的代码有2点不好的地方:

  • gerView 方法存进来的是一个convertView对象,该对象是缓存视图,在listview第一次加载的时候,该对象为空,但是一旦listview滑动,就会有对象保存到convertView中去了,所以我们不必要每次调用getView都需要创建加载一个布局文件,我们可以先判断convertView是否为空,不为空的话,可以直接使用。
  • 每次调用都会执行几次findViewById方法,这样也会耗费不必要的资源,我们可以定义一个ViewHolder,每次找到了子空间就存放在ViewHolder中,然后通过setTag 方法把ViewHolder绑定到view对象中,当缓存中获取该view对象中,里面就包含了对应的ViewHolder对象,节省了不必要的操作。

最终代码如下:


    class MyAdapter extends BaseAdapter{
        private List<Person> list = new ArrayList<Persom>();
        public void MyAdapter(){
        for(int i =0 ;i< 10;i++){//初始化数据,姓名,薪水,电话号码
            list.add(new Person("哈哈"+110000+i,"1387894"+i))        
        }
        }
        @Override
        public int getCount() {
            return list.size();
        }

        @Override
        public Object getItem(int position) {
            return position;
        }

        @Override
        public long getItemId(int position) {

            return position;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            //从布局文件填充称为view对象
            /**
             * 第二个参数convertView 是缓存视图
             * 优化过程是,getView会传进来一个convertView对象,该对象是缓存的视图,
             * 为了使程序优化,避免出现OOM,所以我们需要判断converView是否为空,
             * 不为空就返回convertView  ,问题来了:当listview滑动几次之后,convertView
             * 就不一定是我们所需要的那个View对象,所以最后要把view对象的数据重新绑定,这样就
             * 不会出问题了。
             */
            View v = null;
            //当缓存不为空的时候,就填充视图,该代码优化了内存的使用
            ViewHolder mHolder = null;
            if(convertView == null){
                v = View.inflate(MainActivity.this, R.layout.item, null);
                mHolder = new ViewHolder();
                mHolder.name = (TextView) v.findViewById(R.id.name);
                mHolder.salary = (TextView) v.findViewById(R.id.salary);
                mHolder.phone = (TextView) v.findViewById(R.id.phone);
                v.setTag(mHolder); //将mholder绑定到view对象中,通过getTag方法就可以回去对应的view 避免每次重复查找
            }else{
                //当缓存不为空的时候,就不必要执行findViewById操作了
                v = convertView;
                mHolder = (ViewHolder) v.getTag();
            }
            //因为convertView对象是指缓存中的内容,不一定是我们需要的那个view对象,所以要重新设值
            System.out.println("getView调用:"+position);

            Person p = list.get(position);
            mHolder.name.setText(p.getName());
            mHolder.salary.setText(p.getSalary());
            mHolder.phone.setText(p.getPhone());
            return v;
        }
    }
    /**
     * ViewHolder 的作用是为了避免每次调用getView的时候都要再执行findviewById()这样会耗费资源
     * @author Jam
     *
     */
    class ViewHolder{
        TextView name;
        TextView salary;
        TextView phone; 
    }

}

以上就是我对ListView的一些总结了。

发布了105 篇原创文章 · 获赞 58 · 访问量 80万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 编程工作室 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览