自定义Apater 复杂显示ListView

前言

Adapter用于存放数据。

Android是完全遵循MVC模式设计的框架,Activity是Controller,layout是View 

因为layout五花八门,很多数据都不能直接绑定上去,所以Android引入了Adapter这个机制作为复杂数据的展示的转换载体,所以各种Adapter只不过是转换的方式和能力不一样而已。 Adapter是连接后端数据和前端显示的适配器接口,是数据和UI(View)之间一个重要的纽带。在常见的View(List View,Grid View)等地方都需要用到Adapter。

之前我提过各种Adapter,但是有时特殊的需求没法满足我们。

我有过这样一个需求,需要在ListView中动态显示每一行的状态,状态是连接IP地址的状态,起始的时候每一行的有一个状态图是等待的那种。如果连接上了就显示对号,如果没有就显示错号。而且每一行对IP连接都是异步的。如何实现呢。

最初的思路是获取数据数组,将他们组装成Adapter放入ListView。然后开启多线程,去连接每一个Item的IP地址,当一个线程连接成功后,通过Handler机制,改变原始放入Adapter的数组的对应属性,并通过notifyDataSetChanged()方法更新adapter,从而动态更新整个ListView。

1、自定义Adapter,添加数据判断

public class MyAdapter extends BaseAdapter {
    private Context context;
    private List<ipInfo> data;
    public MyAdapter(Context context,List<ipInfo> data){
        this.context=context;
        this.data=data;
    }
    @Override//返回数据集的长度
    public int getCount() {
// TODO Auto-generated method stub
        return data.size();
    }


    @Override
    public Object getItem(int position) {
// TODO Auto-generated method stub
        return data.get(position);
    }


    @Override
    public long getItemId(int position) {
// TODO Auto-generated method stub
        return position;
    }


    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder = null;
        if(convertView==null){//不是一次更新,则填充一个新视图  
            convertView= LayoutInflater.from(context).inflate(R.layout.info_item_layout, null);
            holder = new ViewHolder();
            holder.imgTv = (ImageView) convertView.findViewById(R.id.img);
            holder.name = (TextView) convertView.findViewById(R.id.name);
            holder.id = (TextView) convertView.findViewById(R.id.ipid);
            holder.ip = (TextView) convertView.findViewById(R.id.ip);
            holder.state = (TextView) convertView.findViewById(R.id.state);
            holder.probar = (ProgressBar) convertView.findViewById(R.id.proBar);
            holder.imgSuc = (ImageView) convertView.findViewById(R.id.success);
            holder.imgErr = (ImageView) convertView.findViewById(R.id.error);
            convertView.setTag(holder);
        }else { //else里面说明,convertView已经被复用了,说明convertView中已经设置过tag了,即holder
            holder = (ViewHolder) convertView.getTag();
        }
        ipInfo bean = data.get(position);
        holder.name.setText(bean.getIpname());
        holder.ip.setText(bean.getIp());
        holder.id.setText(bean.getIpid());
        holder.state.setText(bean.getIpstate());
        if(bean.getIpstate().equals("1")){//加载时的图标
            holder.probar.setVisibility(View.VISIBLE);
            holder.imgSuc.setVisibility(View.GONE);
            holder.imgErr.setVisibility(View.GONE);
        }
        else if(bean.getIpstate().equals("2")){//成功时的图标
            holder.probar.setVisibility(View.GONE);
            holder.imgSuc.setVisibility(View.VISIBLE);
            holder.imgErr.setVisibility(View.GONE);
        }
        else if(bean.getIpstate().equals("3")){//错误时的图标
            holder.probar.setVisibility(View.GONE);
            holder.imgSuc.setVisibility(View.GONE);
            holder.imgErr.setVisibility(View.VISIBLE);
        }
        return convertView;
    }

    //这个ViewHolder只能服务于当前这个特定的adapter,因为ViewHolder里会指定item的控件,不同的ListView,item可能不同,所以ViewHolder写成一个私有的类
    private class ViewHolder {
        ImageView imgTv;
        TextView name;
        TextView id;
        TextView ip;
        TextView state;
        ProgressBar probar;
        ImageView imgSuc;
        ImageView imgErr;
    }
}

1、要自定义Adapter时,首先要继承BaseAdapter。然后根据需要重写getView()方法,该方法主要是找到item的布局文件,然后根据你传到自定义Adapter中的数据信息进行对应设置,以此来进行适配

convertView= LayoutInflater.from(context).inflate(R.layout.info_item_layout, null);//绑定对应的Itmebuj

将converView的布局传给ViewHolder,data是Activity传过来的内容数据,利用ViewHolder设置数据。根据出过来的代码编号不同来显示不同的图片。

其中holder = (ViewHolder)converView.getTag();是为了防止多次findViewById来拖慢程序运行效率

Android中有个叫做Recycler的构件,下图是他的工作原理:

  1. 如果你有10亿个项目(item),其中只有可见的项目存在内存中,其他的在Recycler中。
  2. ListView先请求一个type1视图(getView)然后请求其他可见的项目。convertView在getView中是空(null)的。
  3. 当item1滚出屏幕,并且一个新的项目从屏幕低端上来时,ListView再请求一个type1视图。convertView此时不是空值了,它的值是item1。你只需设定新的数据然后返回convertView,不必重新创建一个视图。
使用ConvertView和ViewHolder的优化是针对ListView的Adapter(BaseAdapter)的。这种优化的 优点 如下:
1) 重用了ConveertView,在很大程度上减少了内存的消耗。通过判断ConvertView是否为NULL,如果是NULL那么就需要生成一个新的View出来(通过LayoutInflater生成),绑定数据后显示给用户;如果ConvertView不是NULL,则我们需要做的就只有绑定数据并呈现给用户。
2) 由于ListView中的Item往往都是只有一个模板,即整个ListView的所有Item用的都是一套ID,所以我们可以把findViewById()方法提取出来,即全程之找一遍ID,这样可以避免让程序不停的做同样的事情。这样做的话通常需要用到另一个内部类(通常写成ViewHolder,在这个类中定义Item中需要用到的控件的名称),这样做也可以方便我们调用控件的onClick()事件等等。
3) 综上,这样做不仅减少了项目性能的消耗,也减少了内存的消耗。

2、开启线程,利用Handler来更新ListView

首先,数据传递给MyAdapter,每个状态都是显示“加载时的图标”。然后线程进行连接检查,将能连上的和不能连上的进行更新。

 //将数组中的数据放入到自定义的adpater
        adapter = new MyAdapter(this,datas);
        lv_data = (ListView)findViewById(R.id.list_view) ;
        //写入listview
        lv_data.setAdapter(adapter);
        helper.close();
        //加载完列表以后进行验证ip是否在线的操作
        //加载每一个ip去ping

        for(int i =0 ; i< datas.size();i++){
            String ip = datas.get(i).getIp();
            pingInNewThread(ip,""+""+i);//有一个ip就开一个线程。。将ip地址和listview的地址放入到线程中
        }
        //开启线程验证每一个IP是否可用
 private void pingInNewThread(final String ip ,final String ps) {
        Thread th = new Thread(new Runnable() {

            @Override
            public void run() {
                // TODO Auto-generated method stub
                //进行耗时连接操作,连接成功与否都向Handler sendMessage
            }
        });
        th.start();
    }

对应的Handler

Handler mHandler = new Handler() {//等待Message的穿过来,用于动态更新ping的状态图标。这个是
        @Override
        public void handleMessage(Message msg) {
            // TODO Auto-generated method stub
            String arr[] = msg.obj.toString().split(",");
            String ip = arr[0];
            String ps = arr[1];
            int pos = Integer.parseInt(ps);
            switch (msg.what) {
                case 0:
//                    Toast.makeText(MainActivity.this, ip + "拼不通 位置是=" + ps,Toast.LENGTH_SHORT).show();
                    datas.get(pos).setIpstate("3");
                    adapter.notifyDataSetChanged();
                    break;
                case 1:
                    if (FLAG_FIRST == 0) {
//                        Toast.makeText(MainActivity.this,ip + "获得第一名位,置是=" + ps, Toast.LENGTH_SHORT).show();
                    } else {
//                        Toast.makeText(MainActivity.this,ip + "获得" + (FLAG_FIRST + 1) + "名,位置是=" + ps,Toast.LENGTH_SHORT).show();
                    }
                    datas.get(pos).setIpstate("2");
                    adapter.notifyDataSetChanged();
                    FLAG_FIRST++;
                    break;
                case 2:
//                    Toast.makeText(MainActivity.this, "发生本地异常", Toast.LENGTH_SHORT) .show();
                    datas.get(pos).setIpstate("2");
                    adapter.notifyDataSetChanged();
                    break;
                default:
                    break;
            }
        }
    };
data是MyAdapter的那个数据源,msg获取的信息是哪个位置上的测试连接完成。这样再Handler接收到后就能对对应的数据更改值,之后调用 notifyDataSetChanged();就能刷新adapter,调用getView重新加载数据,更新ListView
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值