上一篇说这个星期要搞RN,但是今天老师和我说,目前正在做的一个商业项目出了点问题,让我给改改,于是乎,趁这个机会,也写写开发中我是怎么用ListView的吧。首先看下今天的效果图(ps:并不会给出代码,毕竟是商业的,但是我会给出大致代码。)

androidstudio创建listview对应的item布局_listview

,看起来还不错是吧。那么说下功能吧。

1. listview多Item布局

2. 下拉刷新,上拉加载

3. 搜索

4. 点击进入企业详情

ListView开发中常用的无非就是下面几点

1. headerview,footerview(搜索框)

2. 多item布局

3. 优化

3.1. 复用convertView

3.2. ViewHolder

3.3. 快速滑动不开启异步任务(如加载图片)

3.4. 分页加载

4. 数据更新(搜索)

5. 下拉刷新,上拉加载

  • headerView,footerView
    简单,只需要通过addHeaderView(addfooterView)来添加就好了。搜索框就是这样弄的。
  • 多Item布局以及优化
    在Adapter中,除了平常需要重写的函数,我们还需要重写2个函数,getViewTypeCount(布局类别数目)和getItemViewType(position处对应的布局)。
    从效果图中我们能直接看出是2种布局,其实还有一种,就是无数据时候的布局。简单来说就是一个空白页。
//这里返回三种
@Override
    public int getViewTypeCount() {
        // TODO Auto-generated method stub
        return 3;
    }
/**
     * 获取每个Item项的Type
     */
    @Override
    public int getItemViewType(int position) {
        // TODO Auto-generated method stub
        int top_count = dataModel.company_top_list.size();
        if (dataModel.company_notop_list.size() == 0 && dataModel.company_top_list.size() == 0) {
            return 2; // 无数据
        }
        if (position < top_count) {
            return 0; // 置顶
        } else {
            return 1; // 非置顶
        }
    }

我这里维护着一个Model,这个Model里面维护这2个List(一个置顶公司,一个非置顶公司),其实维护一个列表的话在这里更合适, 其实维护一个列表的话在这里更合适, 其实维护一个列表的话在这里更合适, 为什么这么说呢,后面会说到,这里先以2个列表来讲。

这样下来,我们就得根据我们的数据来在getView里面搞一些事情了。先大致看下getView的代码,我们对着代码将。

@Override
    public View getView(int position, View cellView, ViewGroup parent) {
        // TODO Auto-generated method stub

        BeeCellHolder holder = null;
        ViewHolder viewholder;
        if (TYPE_TOP == getItemViewType(position)) { // 置顶
            viewholder = new ViewHolder();
            if (null == cellView || cellView.getClass() != TopCompany.class) {
                cellView = (TopCompany) LayoutInflater.from(mContext).inflate(R.layout.f0_company_top, null);
                Log.e("tag", "TYPE---->" + getItemViewType(position));
                viewholder.top= (TopCompany) cellView;
                cellView.setTag(viewholder);
            } else {
                viewholder=(ViewHolder) cellView.getTag();
                cellView=viewholder.top;
            }
            /**
             * 这里可以稍作修改,将list数组直接改成Company_TOP,应为每个布局之有一个指定公司
             */
            List<Company_TOP> itemlist = null;
            // 置顶项,是第几个就获得第几个

            // itemlist.add(dataModel.company_top_list.get(position));

            itemlist = dataModel.company_top_list.subList(position, position + 1);

            Log.e("tag", "getView里面----》" + itemlist.get(0).getCompany_bg_3g());
            ((TopCompany) viewholder.top).bindData(itemlist);
        } else if (TYPE_NO_TOP == getItemViewType(position)) { // 非置顶
            viewholder = new ViewHolder();
            if (null == cellView || cellView.getClass() != NoTopCompany.class) {
                cellView = LayoutInflater.from(mContext).inflate(R.layout.f0_company_notop, null);
                viewholder.notop= (NoTopCompany) cellView;
                cellView.setTag(viewholder);
            } else {
                viewholder=(ViewHolder) cellView.getTag();
                cellView = viewholder.notop;
            }
            List<Company_NOTOP> itemList = null;
//          Log.e("tag", "TYPE---->" + getItemViewType(position));
//          Log.e("tag", "getview中的数据数目---->"+dataModel.company_notop_list.size());
            // 确定开始位置和终止的位置
            int start = (position - dataModel.company_top_list.size()) * 2;
            // 非置顶公司的数目
            int notop_count = dataModel.company_notop_list.size();
            Log.e("tag1","非置顶公司数目--->"+notop_count);
            int end = 0;
            // 加一个值就超过数目的索引值,说明当前就是最后一个
            if (start == notop_count - 1) {
                end = start + 1;
            } else {
                end = start + 2;
            }
            itemList = dataModel.company_notop_list.subList(start, end);
            try {
                Log.e("tag","这是网址---》"+dataModel.company_notop_list.get(36).toJson().toString()+
                        dataModel.company_notop_list.get(36).toJson().toString());
            } catch (JSONException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            Log.e("tag", "com.insthub.ecmobile.adapter.F0_CompanyList_Adapter这是" + position + "列的开始位置和结束位置" + start
                    + ";" + end);
            ((NoTopCompany) cellView).bindData(itemList);
        } else { // 无数据

            cellView = LayoutInflater.from(mContext).inflate(R.layout.null_pager, null);

        }
        return cellView;
    }

上面,我们在getView中,先通过getItemViewType(position)来知道position对应要加载的布局。然后在判断布局是否为空或者布局是不是我们想要的布局。为什么呢,只有在布局为空或这当前布局不是我们想要的布局的时候,我们采取inflate,这样是不是就节省了时间了呢。前面这说了,我们这里需要三种布局,置顶公司(占一行),非置顶公司(一行2个),空白页,我们现在先不考虑空白页。我们可以根据我们的逻辑知道position位置是应该加载置顶公司还是非置顶公司的对吧。接下来怎么做,看代码

if (null == cellView || cellView.getClass() != TopCompany.class) {
                cellView = (TopCompany) LayoutInflater.from(mContext).inflate(R.layout.f0_company_top, null);
                Log.e("tag", "TYPE---->" + getItemViewType(position));
                viewholder.top= (TopCompany) cellView;
                cellView.setTag(viewholder);
            } else {
                viewholder=(ViewHolder) cellView.getTag();
                cellView=viewholder.top;
            }

到这里,我们还不知道出现的ViewHolder和TopCompany是个什么玩意。来看下。

class ViewHolder{
        private TopCompany top;
        private NoTopCompany notop;
    }

这是ViewHolder,并不是向普通的ViewHolder一样 ,维护这一堆控件,我这里维护这2个LinearLayout,那么,在这个例子中,我们就不能通过ViewHolder来节省findViewById的时间了。
在来看下NoTopCompany吧。因为TopCompany是一列一个,没啥看的。

private void initView() {
        // TODO Auto-generated method stub
        if (null == imageView_1) {
            imageView_1 = (ImageView) findViewById(R.id.company_notop_listitem_img_1);
            imageView_1.setOnClickListener(new OnClickListener() {

                @Override
                public void onClick(View v) {
                    // TODO Auto-generated method stub
                    Intent intent = new Intent(mContext,
                            f0_Company_Detail_Activity.class);
                    intent.putExtra("company_id", cellData.get(0)
                            .getCompany_notop().getCompany_id());
                    mContext.startActivity(intent);
                    ((EcmobileMainActivity) mContext)
                            .overridePendingTransition(R.anim.push_right_in,
                                    R.anim.push_right_out);
                }
            });
        }

        if (null == imageView_2) {
            imageView_2 = (ImageView) findViewById(R.id.company_notop_listitem_img_2);
            imageView_2.setOnClickListener(new OnClickListener() {

                @Override
                public void onClick(View v) {
                    // TODO Auto-generated method stub
                    Intent intent = new Intent(mContext,
                            f0_Company_Detail_Activity.class);
                    intent.putExtra("company_id", cellData.get(1)
                            .getCompany_notop().getCompany_id());
                    mContext.startActivity(intent);
                    ((EcmobileMainActivity) mContext)
                            .overridePendingTransition(R.anim.push_right_in,
                                    R.anim.push_right_out);
                }
            });
        }

    }

就是里面又2个ImageView,分别绑定监听事件。那么数据是怎么穿过来的呢。

public void bindData(List<Company_NOTOP> listData) {
        cellData.clear();
        cellData.addAll(listData);
        mHandler.removeMessages(0);
        mHandler.sendEmptyMessageDelayed(0, 30);
    }

我们这里可以控制,比如说传过来的listData里面就一条数据,那么我们就将第二个imageview隐藏,简单吧。
好了,这个就说到这里。继续我们的getView,虽然ViewHolder在这里并不能起到优化作用,但是他却能帮我们解决另一个问题,郭霖–ListView乱序,因为ListView的复用机制,不设置tag会造成乱序问题。关于这个问题的解决以及原理,还是去看郭霖大神的吧。代码如下

if (null == cellView || cellView.getClass() != NoTopCompany.class) {
                cellView = LayoutInflater.from(mContext).inflate(R.layout.f0_company_notop, null);
                viewholder.notop= (NoTopCompany) cellView;
                cellView.setTag(viewholder);
            } else {
                viewholder=(ViewHolder) cellView.getTag();
                cellView = viewholder.notop;
            }

在加载布局之后设置tag,在不需要加载的时候通过getTag来获取。这样,我们就解决乱序问题了。
最后,我们通过

((TopCompany) viewholder.top).bindData(itemlist);

就将数据绑定到布局上了。这个bindData是TopCompany(NoTopCompany)的一个方法。再来说下非置顶的计算。

androidstudio创建listview对应的item布局_置顶_02


图丑了点,将就着看吧。position对应的NoTopCompanyList的索引不就start=(position-置顶公司数目)*2么,结束位置的索引就看非置顶公司的size了,如果总数-1等于起始的话,就是end=start+1,否则,就是start+2,我们这里需要用sublist(左闭右开)来截取list。看代码。

// 确定开始位置和终止的位置
            int start = (position - dataModel.company_top_list.size()) * 2;
            // 非置顶公司的数目
            int notop_count = dataModel.company_notop_list.size();
            Log.e("tag1","非置顶公司数目--->"+notop_count);
            int end = 0;
            // 加一个值就超过数目的索引值,说明当前就是最后一个
            if (start == notop_count - 1) {
                end = start + 1;
            } else {
                end = start + 2;
            }
            itemList = dataModel.company_notop_list.subList(start, end);

关于空白页就不说了。

  • 数据更新
listAdapter.notifyDataSetChanged()

注意:
1. 要保证数据变化了
2. 要保证是同一个数据源,并没有指向新的引用。


  • 下拉刷新、上拉加载以及分页加载
    下拉刷新和上拉加载网上游很多开源控件,谷歌官方也有一个SwipeRefreshLayout,但是没有上拉加载更多,需要自己封装。
    分页加载功能和上拉加载更多是紧密相连的,只需要在家在更多的时候,从服务器拿到数据之后加到原来的数据源里面,然后notify…

恩,ListView的用法 大致就这些了,虽然我这里用的是ListView,但是我还是建议大家用RecyclerView。好了,学RN去了。