BaseAdapter – convertView回收机制与动态控件响应
一,ConvertView回收机制
工作原理:
- ListView针对List中每个item,要求adapter“给我一个试图”(getView)
- 一个新试图被返回并显示
Android为此缓存了视图,Android有一个叫做Recycler的构件,下图是它的工作原理。
ListView先请求一个item1视图(getView)并且请求其他屏幕可见的视图,此时convertView是空(null)。
当item1滚出屏幕,并且一个新的item从屏幕底端上来时,ListView再次请求一个视图,convertView此时不是空值了,它的值是item1.你只需设定新的数据然后返回convertView,不必重新创建一个视图。
二,实例
public class MainActivity extends AppCompatActivity {
private List<String> infos = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initData();
ListView listView = findViewById(R.id.liseView);
MyAdapter adapter = new MyAdapter(this,infos);
listView.setAdapter(adapter);
}
public void initData(){
for (int i = 0 ;i<50;i++){
infos.add("第"+(i+1)+"信息");
}
}
}
public class MyAdapter extends BaseAdapter {
private LayoutInflater inflater;
private List<String> infos;
public MyAdapter(Context context,List<String> infos) {
inflater = LayoutInflater.from(context);
this.infos = infos;
}
@Override
public int getCount() {
return infos.size();
}
@Override
public Object getItem(int position) {
return infos.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
holder = new ViewHolder();
convertView = inflater.inflate(R.layout.layout_item, parent, false);
holder.info = convertView.findViewById(R.id.info);
holder.attention = convertView.findViewById(R.id.attention);
holder.info.setText(infos.get(position));
final TextView attention = holder.attention;
holder.attention.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
attention.setTextColor(Color.RED);
}
});
return convertView;
}
static class ViewHolder{
TextView info;
TextView attention;
}
}
手机初始化是这样子的:
根据上面的理论,在初始化时,整个屏幕可以放下三个item,所以会创建三个全新的convertView。当手指向上滑,出现第四个item时,就会回收第一个item的convertView给第四个。
可以看到,前四个convertView为null,当地五个item出现时,此时由于第一个item肯定已经滚出屏幕,所以将其重新传给即将出现的item5使用。回到上面的代码,我们为“关注”写了一个点击事件,当点击"关注"的时候,字体会变红,如下:
点击关注
手指向上滑再滑回来
问题出现,当滑回来的时候,”关注“不再红了
问题出现在代码上:
holder = new ViewHolder();
convertView = inflater.inflate(R.layout.layout_item, parent, false);
holder.info = convertView.findViewById(R.id.info);
holder.attention = convertView.findViewById(R.id.attention);
每次运行getView 获取当前item时,都会重新new一个ViewHolder与R.layout.item绑定,也就是说,每次都会产生一个新布局赋值给convertView让其显示。上面讲过,Android会将回收过来的convertView返回给即将显示的getView使用,以节约资源。而这里我们每次都新建一个布局赋值给convertView,由于每次都是新建布局,所以item1
被重新拉回来显示的时候,由于是重新创建的布局,当然是初始状态。
改进:
public class MainActivity extends AppCompatActivity {
private List<String> infos = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initData();
ListView listView = findViewById(R.id.liseView);
MyAdapter adapter = new MyAdapter(this,infos);
listView.setAdapter(adapter);
}
public void initData(){
for (int i = 0 ;i<50;i++){
infos.add("第"+(i+1)+"信息");
}
}
}
public class MyAdapter extends BaseAdapter {
private LayoutInflater inflater;
private List<String> infos;
public MyAdapter(Context context,List<String> infos) {
inflater = LayoutInflater.from(context);
this.infos = infos;
}
@Override
public int getCount() {
return infos.size();
}
@Override
public Object getItem(int position) {
return infos.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@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 = inflater.inflate(R.layout.layout_item, parent, false);
holder.info = convertView.findViewById(R.id.info);
holder.attention = convertView.findViewById(R.id.attention);
convertView.setTag(holder);
}else{
holder = (ViewHolder) convertView.getTag();
}
holder.info.setText(infos.get(position));
final TextView attention = holder.attention;
holder.attention.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
attention.setTextColor(Color.RED);
}
});
return convertView;
}
static class ViewHolder{
TextView info;
TextView attention;
}
}
点击
滑上去再滑回来
再往下拉
问题出现在第三张图,明明没有点击item5,但是关注是红色的
这是因为item5用的是item1回收来的convertView,而item1中的关注是红色的,所以只要回收机制在,我们就没有办法改变从item1回收来的convertView的图片布局。
理解了这个问题,解决办法如下:
首先申请一个ArrayList attentionArr变量,保存用户点击“关注”的item的position,然后在绘制当前item时,根据这个position是否在attentionArr里来判断是不是将关注重新变红。
代码如下:
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 = inflater.inflate(R.layout.layout_item, parent, false);
holder.info = convertView.findViewById(R.id.info);
holder.attention = convertView.findViewById(R.id.attention);
convertView.setTag(holder);
}else{
holder = (ViewHolder) convertView.getTag();
}
holder.info.setText(infos.get(position));
final TextView attention = holder.attention;
if (attentionArr.contains(position)){
attention.setTextColor(Color.RED);
}else{
attention.setTextColor(Color.BLACK);
}
holder.attention.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
attention.setTextColor(Color.RED);
attentionArr.add(position);
}
});
return convertView;
}