ListView作为Android的一个高级控件,使用的频率是非常高的,它不像一些简单的控件直接在xml文件中设置各项参数就能达到预期的结果,而需要一个中间件的支持,这个中间件就是adapter。
一个简单的关系图说明ListView的使用层次关系:
上图中,向下是set和use的关系,向上是控制关系,即ListView使用adapter,adapter使用数据源data,data控制adapter的数据源,adapter控制ListView的逻辑功能。
这里对于新手来说还是比较难理解的,显示点东西比较容易,但是一但嵌套其它可操作的控件就产生疑惑了:如何判定哪个控件被点击,哪个控件状态改变了都成了问题。这里我们一并说明!
首先是我们的MainActivity.java
public class MainActivity extends Activity {
ListView main_list;
MyAdapter adapter;
List<Info> datalist;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
main_list=(ListView)findViewById(R.id.main_list);
datalist=new ArrayList<Info>();
datalist.add(new Info("我是第一个"));
datalist.add(new Info("我是第二个"));
datalist.add(new Info("我是第三个"));
datalist.add(new Info("我是第四个"));
datalist.add(new Info("我是第五个"));
adapter=new MyAdapter(this,datalist);
main_list.setAdapter(adapter);
}
}
很简单的一个activity,其中的Info类是我们的数据承接bean,代码是:
public class Info {
String text;
public Info(String text) {
this.text = text;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
}
最后是我们的adapter:
public class MyAdapter extends BaseAdapter{
private Context c;
private List<Info> datalist;
private LayoutInflater inflater;
//构造,传入我们Activity的this和数据源datalist
public MyAdapter(Context c,List<Info> datalist)
{
this.c=c;
this.datalist=datalist;
this.inflater=LayoutInflater.from(c);
}
//以下三个方法是adapter自动调用的,但是需要我们对它进行简单修改,照猫画虎就行
@Override
public int getCount() {
return datalist.size();
}
@Override
public Object getItem(int position) {
return datalist.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
//adapter的核心方法,需要仔细研究
@Override
public View getView(int position, View convertView, ViewGroup parent) {
final int pos=position;
//这里的if-else是基于ListView的缓存机制编写的,使用一个ViewHolder来承接我们ListView中每一个小项中的控件。注意findViewById是比较耗时的操作,这样做可以增加重用,避免多次调用耗时操作
ViewHolder holder=null;
if(convertView==null)
{
convertView=inflater.inflate(R.layout.item, null);
holder=new ViewHolder();
holder.cb_check=(CheckBox) convertView.findViewById(R.id.cb_check);
holder.tv_text=(TextView) convertView.findViewById(R.id.tv_text);
convertView.setTag(holder);
}else{
holder=(ViewHolder) convertView.getTag();
}
holder.tv_text.setText(datalist.get(position).getText()+"--"+position);
//间隔勾选CheckBox而已,没什么技术含量
holder.cb_check.setChecked(position%2==0?true:false);
//这里解决我们在一个ListView小项中嵌套可操作控件的问题,实际每次点击都是可以获得当前你点击的位置position的,
holder.cb_check.setOnCheckedChangeListener(new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if(isChecked)
{
Toast.makeText(c, datalist.get(pos).getText()+" 勾上了", Toast.LENGTH_SHORT).show();
}else{
Toast.makeText(c, datalist.get(pos).getText()+" 取消了", Toast.LENGTH_SHORT).show();
}
}
});
//为ListView的item设置了点击事件的监听
convertView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(c, datalist.get(pos).getText(), Toast.LENGTH_SHORT).show();
}
});
return convertView;
}
class ViewHolder{
TextView tv_text;
CheckBox cb_check;
}
}
!!跪求注意的部分:
我们的adapter继承BaseAdapter,这里我们需要实现这个抽象类的几个方法,注释中有详细的说明。再次强调一下:在我们手指滑动屏幕上的ListView时,getView方法会不断执行,如果在里面加一个log输出,一滑动产生n多输出。
而且这个方法是线性执行的,前一个getView执行完后才会执行下一个getView,这也就解释了当我们数据量比较多时,滑动出现卡顿的问题,因为getView的执行出现了高耗时,绘制一个item需要过多的时间。
另外,我们完全可以在这个adapter中维护一个列表,这个列表存储每个checkbox的状态,拿到了每个checkbox 的状态就可以对ListView中的数据进行批量修改(文件的批量删除就是这样实现的!)。
最后贴上xml的布局代码:
activity_main.xml
<RelativeLayout 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"
tools:context="${relativePackage}.${activityClass}" >
<ListView
android:id="@+id/main_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</RelativeLayout>
item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="20dp"
android:orientation="horizontal" >
<TextView
android:id="@+id/tv_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="TextView" />
<CheckBox
android:id="@+id/cb_check"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="CheckBox" />
</LinearLayout>
ok,就到这里~