BaseAdapter——convertView回收机制与动态控件响应

本文探讨了在Android ListView中实现关注按钮颜色变化时遇到的问题,并提出了解决方案。通过维护一个已关注项的位置列表,确保了即使在列表滚动过程中也能正确地显示关注状态。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

关于ListView的工作原理,我已经在 ListView中getView的原理+如何在ListView中放置多个item这篇文章中介绍过了,这里就不再介绍了。

我们先看一段代码:

public class MainActivity extends ListActivity {

    private ArrayList<HashMap<String, Object>> listItem = new ArrayList<HashMap<String, Object>>();
    private PullToRefreshListView mPullRefreshListView;
    MyAdapter adapter=null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mPullRefreshListView = (PullToRefreshListView) findViewById(R.id.pull_refresh_list);
        //设定下拉监听函数
        mPullRefreshListView.setOnRefreshListener(new OnRefreshListener<ListView>() {
            @Override
            public void onRefresh(PullToRefreshBase<ListView> refreshView) {
                String label = DateUtils.formatDateTime(getApplicationContext(), System.currentTimeMillis(),
                        DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_ABBREV_ALL);

                // Update the LastUpdatedLabel
                refreshView.getLoadingLayoutProxy().setLastUpdatedLabel(label);

                // Do work to refresh the list here.
            }
        });
        mPullRefreshListView.setMode(Mode.PULL_FROM_END);//设置底部下拉刷新模式

        listItem=getData();//获取LIST数据
        adapter = new MyAdapter(this);
        //设置适配器
        ListView actualListView = mPullRefreshListView.getRefreshableView();
        actualListView.setAdapter(adapter); 
    }
    private ArrayList<HashMap<String, Object>> getData() {
        ArrayList<HashMap<String, Object>> list = new ArrayList<HashMap<String, Object>>();
        HashMap<String, Object> map = new HashMap<String, Object>();
        InputStream inputStream;
        try {
            inputStream=this.getAssets().open("my_home_friends.txt");
            String json=readTextFile(inputStream);
            JSONArray array = new JSONArray(json);
            for (int i = 0; i < array.length(); i++) {
                map = new HashMap<String, Object>();
                map.put("name", array.getJSONObject(i).getString("name"));
                map.put("info", array.getJSONObject(i).getString("info"));
                map.put("img",array.getJSONObject(i).getString("photo"));
                list.add(map);
            }
            return list;    

        } catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
        }
        return list;    
    }
    public final class ViewHolder{
        public TextView name;
        public TextView info;

        public TextView attentntion;
    }       
    public class MyAdapter extends BaseAdapter{

        private LayoutInflater mInflater;

        public MyAdapter(Context context){
            this.mInflater = LayoutInflater.from(context);
        }
        @Override
        public int getCount() {
            return listItem.size();
        }

        @Override
        public Object getItem(int arg0) {
            return null;
        }
        @Override
        public long getItemId(int arg0) {
            return 0;
        }
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        System.out.println("position:"+position+"   convertView:"+convertView);
        ViewHolder holder = null;
        holder=new ViewHolder();        
        convertView = mInflater.inflate(R.layout.item, null);
        holder.name = (TextView)convertView.findViewById(R.id.name);
        holder.info = (TextView)convertView.findViewById(R.id.info);
        holder.attentntion=(TextView)convertView.findViewById(R.id.attention);

        holder.name.setText((String)listItem.get(position).get("name"));
        holder.info.setText((String)listItem.get(position).get("info"));
        final TextView attention=holder.attentntion;
        holder.attentntion.setOnClickListener(new View.OnClickListener() {              
            @Override
            public void onClick(View v) {
                attention.setTextColor(Color.RED);
            }
        });
        convertView.setTag(holder);     
        return convertView;
    }   
    }
    ////工具类
    /**
     * 
     * @param inputStream
     * @return
     */
    public String readTextFile(InputStream inputStream) {
        String readedStr = "";
        BufferedReader br;
        try {
            br = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
            String tmp;
            while ((tmp = br.readLine()) != null) {
                readedStr += tmp;
            }
            br.close();
            inputStream.close();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return readedStr;
    }
}

这样写会有什么问题呢?先来看一个效果图

这里写图片描述

当点击关注的时候我们让字体变红,但是当我们将当前的item移除屏幕然后在拉回来的时候,发现样式已经恢复默认了,为什么会出现这种情况呢,问题出在这里:

    ViewHolder holder = null;
        holder=new ViewHolder();        
        convertView = mInflater.inflate(R.layout.item, null);
        holder.name = (TextView)convertView.findViewById(R.id.name);
        holder.info = (TextView)convertView.findViewById(R.id.info);
        holder.attentntion=(TextView)convertView.findViewById(R.id.attention);

每次运行getView获取当前ITEM时,都会重新new 一个viewHolder与R.layout.item绑定,也就是说,每次都会产生一个新布局赋值给convertView让其显示。而我们上面讲了,android会将回收过来的convertView返回给即将显示的getView使用,以节约资源。而我们这里却没有领情,每次都重新创建一个布局赋给convertView,由于每次都创建一个新布局,所以当ITEM1被重新拉回来显示的时候,由于是重新创建的布局,当然是初始状态。“关注”当然也就是黑色的了

下面我们来改进一下代码:

    if (convertView == null) {
                holder = new ViewHolder();
                convertView = mInflater.inflate(R.layout.item, null);
                holder.name = (TextView) convertView.findViewById(R.id.name);
                holder.info = (TextView) convertView.findViewById(R.id.info);
                holder.attentntion = (TextView) convertView
                        .findViewById(R.id.attention);

                holder.name
                        .setText((String) listItem.get(position).get("name"));
                holder.info
                        .setText((String) listItem.get(position).get("info"));
                convertView.setTag(holder);
            } else {
                holder = (ViewHolder) convertView.getTag();
            }

当convertView为空,即初始化创建时,我们就将生成的布局利用setTag()保存在convertView中,当convertView利用回收机制回收过来让我们再次使用时,我们通过getTag()将保存的布局取出来,重新将布局里的各个控件重新赋值就可以了。这里就利用了android-listView的回收机制,这时候再来看看效果图:

这里写图片描述

此时已经不会出现刚才那种问题了。但是同时又会引发另外一个问题,请看效果图

这里写图片描述

我们明明没有点P-6,为什么”关注”会变红呢?这事因为P-6使用的是P-1回收来的convertview,P-1里面的字是红色色,那么P-6里面的字当然也是红色的了。所以只要回收机制在,我们就没有办法改变从P-1回收来的convertView里的图片布局,除非人为的将其重置!

理解了这个问题以后,我们想想解决办法。
首先申请一个arrayList attentionArr变量,保存用户点击“关注”的item的position,然后在绘制当前item时,根据这个position是否在attentionArr里来判断是不是将“关注”重新变红。主要代码如下:

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

        System.out.println("position:"+position+"   convertView:"+convertView);
        ViewHolder holder = null;

        if (convertView == null) {

        holder=new ViewHolder();    
        convertView = mInflater.inflate(R.layout.item, null);
        holder.name = (TextView)convertView.findViewById(R.id.name);
        holder.info = (TextView)convertView.findViewById(R.id.info);
        holder.attentntion=(TextView)convertView.findViewById(R.id.attention);
        convertView.setTag(holder);     
        }else {

        holder = (ViewHolder)convertView.getTag();
        }

        holder.name.setText((String)listItem.get(position).get("name"));
        holder.info.setText((String)listItem.get(position).get("info"));
        final TextView attention=holder.attentntion;

        //根据当前position判断,重新制做样式
        if (attentionArr.contains(position)) {
            attention.setTextColor(Color.RED);
        }else {
            attention.setTextColor(Color.BLACK);
        }

        holder.attentntion.setOnClickListener(new View.OnClickListener() {              
            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                attention.setTextColor(Color.RED);
                attentionArr.add(position);//在点击时将position加入其中
            }
        });
        return convertView;
    }   
    }

再看效果:

这里写图片描述

理解代码难度不大,首先在OnClickListener时,将position加入到attentionArr数组中,然后在getView里,判断当前position是不是用户点击过的,即是否包含在attentionArr数组中,如果是,则将“关注”置为红色,否则置为初始色,黑色。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值