上一篇说这个星期要搞RN,但是今天老师和我说,目前正在做的一个商业项目出了点问题,让我给改改,于是乎,趁这个机会,也写写开发中我是怎么用ListView的吧。首先看下今天的效果图(ps:并不会给出代码,毕竟是商业的,但是我会给出大致代码。)
,看起来还不错是吧。那么说下功能吧。
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)的一个方法。再来说下非置顶的计算。
,
图丑了点,将就着看吧。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去了。