0825Android基础ListView自定义Adapter优化

用convertView进行了内存的优化

  ListView中,一般除了显示的几个View外会提前缓存好两个view,之前的方法是一直findviewbyid。现在用convertView将滚动出屏幕的View重新利用到缓存中,不必每次都进行findviewbyid,减少内存的占用。
  这次的Demo添加了checkbox,如果不设置他的点击事件的话,没法得到checkbox的状态,所以要设置点击事件,然后用mManagerCheckBox数组来放checkbox的状态。

public View getView(final int position, View convertView, ViewGroup parent) {
        Fruit fruit = mFruits.get(position);
//        局部变量赋初值
        ViewHolder vh = null;
        if (convertView == null) {
//          vh必须new一个,不然会报空指针
            vh=new ViewHolder();
            convertView = mInflater.inflate(R.layout.fruit_item_list, null);
            vh.checkBox = (CheckBox) convertView.findViewById(R.id.checkbox);
            vh.img_fruit = (ImageView) convertView.findViewById(R.id.img_fruit);
            vh.tv_fruit = (TextView) convertView.findViewById(R.id.tv_fruit);
            convertView.setTag(vh);
        } else {
            vh = (ViewHolder) convertView.getTag();
        }
        vh.img_fruit.setImageResource(fruit.getImg());
        vh.tv_fruit.setText(fruit.getName());
        vh.checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
//              position是局部变量,然后这是匿名内部类,所以要传入的话只能用fianl修饰变成常量

//                在FruitAdapter中设置isCheck
                mManagerCheckBox[position]=isChecked;
                //              刷新屏幕
                notifyDataSetChanged();
            }
        });
        vh.checkBox.setChecked(mManagerCheckBox[position]);
        return convertView;
    }
//      缓存用到的数据,下次用直接调用,不需要再次find
    class ViewHolder {
        CheckBox checkBox;
        ImageView img_fruit;
        TextView tv_fruit;
    }
}

添加header和footer

  需要注意的是要将addHeaderView和addFooterView放在setAdapter的前面。另外如果添加了header以后ListView的点击事件setOnItemClickListener中的position要-1,因为这个点击事件中默认把Header设在第一个位置上
  

  • 通过LayoutInflater对布局进行动态加载
  • 获得所得View中的按键
  • 用addHeaderView和addFooterView添加到ListView中
  • 设置点击事件(此处设置了全选和反选,涉及到checkbox的状态,所以点击事件的方法写在适配器中比较方便控制checkbox的状态)

相应代码

//       获得并添加header,注意添加了header后listview的setOnItemClickListener中position要-1
//        因为setOnItemClickListener中默认把header作为第一个.其他的方法中positon不需要改变
//        见53,55
        mHeadView=mInflater.inflate(R.layout.fruit_head,null);
        mBtn_check_all= (Button) mHeadView.findViewById(R.id.btn_check_all);
        mListView.addHeaderView(mHeadView);
//       获得并添加footer
        mFootView=mInflater.inflate(R.layout.fruit_foot,null);
        mBtn_invert_check= (Button) mFootView.findViewById(R.id.btn_invert_check);
        mListView.addFooterView(mFootView);
        //       添加mHeaderView和mFootView的点击事件
        mBtn_check_all.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
            mAdapter.checkAll();
            }
        });
        mBtn_invert_check.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
            mAdapter.invertCheck();
            }
        });


//对应的点击事件存在FruitAdapter中
//    全选
    public void checkAll(){
        for (int i=0;i<mManagerCheckBox.length;i++){
            mManagerCheckBox[i]=true;
        }
        notifyDataSetChanged();
    }
//     反选
    public void invertCheck(){
        for (int i=0;i<mManagerCheckBox.length;i++){
            mManagerCheckBox[i]=!mManagerCheckBox[i];
        }
        notifyDataSetChanged();
    }

将checkboxd的点击范围扩大到整个View

  点击checkbox以外的地方也要能选中checkbox,即设置ListView的点击事件,使ListView的点击事件关联到checkbox的点击事件。也是通过控制改变checkbox的状态来控制。方法还是放在适配器中。
  需要注意的是,要将checkbox做以下设置: android:focusable=”false”,如果不这样设置的话ListView的点击事件就会被checkbox抢占,也就是无法触发ListView点击事件。这里这样设置的效果是checkbox将本界面除他以外的点击事件传递给LiseView,也就是可以触发ListView的点击事件。
  

代码如下

 mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
//                调用适配器中扩展checkbox点击范围的方法
                mAdapter.expandClickArea(position-1);
            }
        });

//方法
 public void expandClickArea(int position){
//        点击后只能变为true,无法由true变为false
//        mManagerCheckBox[position]=true;
//        点击则true/false交替变化
        mManagerCheckBox[position]=!mManagerCheckBox[position];
//        刷新界面,设置下面85行相应的checkbox为true或者false.他是BaseAdapter的一个方法
        notifyDataSetChanged();
    }

一个小知识点:ListView中android:divider=”@color/grey”; android:dividerHeight=”2dp”分别设置插入线,以及插入线的宽度和颜色。

全文代码如下

//自定义水果适配器
public class FruitAdapter extends BaseAdapter {
    private LayoutInflater mInflater;
    private List<Fruit> mFruits;
    private boolean[] mManagerCheckBox;
    public FruitAdapter(LayoutInflater mInflater, List<Fruit> mFruits) {
        this.mInflater = mInflater;
        this.mFruits = mFruits;
        mManagerCheckBox=new boolean[mFruits.size()];
    }
//    全选
    public void checkAll(){
        for (int i=0;i<mManagerCheckBox.length;i++){
            mManagerCheckBox[i]=true;
        }
        notifyDataSetChanged();
    }
//     反选
    public void invertCheck(){
        for (int i=0;i<mManagerCheckBox.length;i++){
            mManagerCheckBox[i]=!mManagerCheckBox[i];
        }
        notifyDataSetChanged();
    }
//    扩大checkbox点击范围
    public void expandClickArea(int position){
//        点击后只能变为true,无法由true变为false
//        mManagerCheckBox[position]=true;
//        点击则true/false交替变化
        mManagerCheckBox[position]=!mManagerCheckBox[position];
//        刷新界面,设置下面85行相应的checkbox为true或者false.他是BaseAdapter的一个方法
        notifyDataSetChanged();
    }
    @Override
    public int getCount() {
        return mFruits.size();
    }

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

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(final int position, View convertView, ViewGroup parent) {
        Fruit fruit = mFruits.get(position);
//        局部变量赋初值
        ViewHolder vh = null;
        if (convertView == null) {
//          vh必须new一个,不然会报空指针
            vh=new ViewHolder();
            convertView = mInflater.inflate(R.layout.fruit_item_list, null);
            vh.checkBox = (CheckBox) convertView.findViewById(R.id.checkbox);
            vh.img_fruit = (ImageView) convertView.findViewById(R.id.img_fruit);
            vh.tv_fruit = (TextView) convertView.findViewById(R.id.tv_fruit);
            convertView.setTag(vh);
        } else {
            vh = (ViewHolder) convertView.getTag();
        }
        vh.img_fruit.setImageResource(fruit.getImg());
        vh.tv_fruit.setText(fruit.getName());
        vh.checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
//              position是局部变量,然后这是匿名内部类,所以要传入的话只能用fianl修饰变成常量

//                在FruitAdapter中设置isCheck
                mManagerCheckBox[position]=isChecked;
//              刷新屏幕
                notifyDataSetChanged();
            }
        });
//        vh.checkBox.setChecked(fruit.getIsCheck());
        vh.checkBox.setChecked(mManagerCheckBox[position]);
        return convertView;
    }
//      缓存用到的数据,下次用直接调用,不需要再次find
    class ViewHolder {
        CheckBox checkBox;
        ImageView img_fruit;
        TextView tv_fruit;
    }
}

//水果类
public class Fruit {
    private String name;
    private int img;

    public Fruit(String name, int img) {
        this.name = name;
        this.img = img;
    }

    public int getImg() {
        return img;
    }

    public void setImg(int img) {
        this.img = img;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

//ListView布局
<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"
    android:orientation="vertical"
    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/listView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:divider="@color/grey"
            android:dividerHeight="2dp"></ListView>
</LinearLayout>


//自定义的View
<?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:gravity="center_vertical"
    android:orientation="horizontal">

    <CheckBox
        android:id="@+id/checkbox"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:focusable="false"
        android:text="选择水果" />

    <ImageView
        android:id="@+id/img_fruit"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@mipmap/apple" />

    <TextView
        android:id="@+id/tv_fruit"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="5dp"
        android:text="水果名称" />
</LinearLayout>

这里写图片描述

//添加的header和footer
<?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="vertical">

    <Button
        android:id="@+id/btn_invert_check"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@drawable/button_orange"
        android:text="反选"
        android:textColor="#ffffff"
        android:textAppearance="@android:style/TextAppearance.Large"/>
</LinearLayout>

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">
    <Button
        android:id="@+id/btn_check_all"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@drawable/button_orange"
        android:text="全选"
        android:textColor="#ffffff"
        android:textAppearance="@android:style/TextAppearance.Large"/>
</LinearLayout>

这里写图片描述
这里写图片描述

//button_orange.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@mipmap/buttom_click" android:state_pressed="true"/>
<item android:drawable="@mipmap/buttom"/>
</selector>

//活动
public class MainActivity extends Activity {
    private List<Fruit> mFruits;
    private ListView mListView;
    private FruitAdapter mAdapter;
    private LayoutInflater mInflater;
    private View mHeadView;
    private View mFootView;
    private Button mBtn_check_all;
    private Button mBtn_invert_check;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mListView = (ListView) findViewById(R.id.listView);
//      LayoutInflater的方法只能在活动中调用,适配器是个类没有这个方法
        mInflater=getLayoutInflater();
        initmFruit();
        mAdapter=new FruitAdapter(mInflater,mFruits);
//       获得并添加header,注意添加了header后listview的setOnItemClickListener中position要-1
//        因为setOnItemClickListener中默认把header作为第一个.其他的方法中positon不需要改变
//        见53,55
        mHeadView=mInflater.inflate(R.layout.fruit_head,null);
        mBtn_check_all= (Button) mHeadView.findViewById(R.id.btn_check_all);
        mListView.addHeaderView(mHeadView);
//       获得并添加footer
        mFootView=mInflater.inflate(R.layout.fruit_foot,null);
        mBtn_invert_check= (Button) mFootView.findViewById(R.id.btn_invert_check);
        mListView.addFooterView(mFootView);
//        addfooterview和addheaderview必须要在setadapter之前
        mListView.setAdapter(mAdapter);
        mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
//                调用适配器中扩展checkbox点击范围的方法
                mAdapter.expandClickArea(position-1);
            }
        });
//       添加mHeaderView和mFootView的点击事件
        mBtn_check_all.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
            mAdapter.checkAll();
            }
        });
        mBtn_invert_check.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
            mAdapter.invertCheck();
            }
        });
    }

    private void initmFruit() {
        mFruits = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
//          这三个要放在for里面,放在for外面的话每次循环的都只有三个一样的对象
            Fruit apple = new Fruit("apple", R.mipmap.apple);
            Fruit lemon = new Fruit("lemon", R.mipmap.lemon);
            Fruit orange = new Fruit("orange", R.mipmap.orange);
            mFruits.add(apple);
            mFruits.add(lemon);
            mFruits.add(orange);
        }
    }
}

这里写图片描述

这里写图片描述

这里写图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值