关于ListView的工作原理,我已经在 ListView中getView的原理+如何在ListView中放置多个item这篇文章中介绍过了,这里就不再介绍了。
我们先看一段代码:
public class MainActivity extends ListActivity {
private ArrayList<HashMap<String, Object>> listItem = new ArrayList<HashMap<String, Object>>();
private PullToRefreshListView mPullRefreshListView;
MyAdapter adapter=null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mPullRefreshListView = (PullToRefreshListView) findViewById(R.id.pull_refresh_list);
//设定下拉监听函数
mPullRefreshListView.setOnRefreshListener(new OnRefreshListener<ListView>() {
@Override
public void onRefresh(PullToRefreshBase<ListView> refreshView) {
String label = DateUtils.formatDateTime(getApplicationContext(), System.currentTimeMillis(),
DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_ABBREV_ALL);
// Update the LastUpdatedLabel
refreshView.getLoadingLayoutProxy().setLastUpdatedLabel(label);
// Do work to refresh the list here.
}
});
mPullRefreshListView.setMode(Mode.PULL_FROM_END);//设置底部下拉刷新模式
listItem=getData();//获取LIST数据
adapter = new MyAdapter(this);
//设置适配器
ListView actualListView = mPullRefreshListView.getRefreshableView();
actualListView.setAdapter(adapter);
}
private ArrayList<HashMap<String, Object>> getData() {
ArrayList<HashMap<String, Object>> list = new ArrayList<HashMap<String, Object>>();
HashMap<String, Object> map = new HashMap<String, Object>();
InputStream inputStream;
try {
inputStream=this.getAssets().open("my_home_friends.txt");
String json=readTextFile(inputStream);
JSONArray array = new JSONArray(json);
for (int i = 0; i < array.length(); i++) {
map = new HashMap<String, Object>();
map.put("name", array.getJSONObject(i).getString("name"));
map.put("info", array.getJSONObject(i).getString("info"));
map.put("img",array.getJSONObject(i).getString("photo"));
list.add(map);
}
return list;
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
return list;
}
public final class ViewHolder{
public TextView name;
public TextView info;
public TextView attentntion;
}
public class MyAdapter extends BaseAdapter{
private LayoutInflater mInflater;
public MyAdapter(Context context){
this.mInflater = LayoutInflater.from(context);
}
@Override
public int getCount() {
return listItem.size();
}
@Override
public Object getItem(int arg0) {
return null;
}
@Override
public long getItemId(int arg0) {
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
System.out.println("position:"+position+" convertView:"+convertView);
ViewHolder holder = null;
holder=new ViewHolder();
convertView = mInflater.inflate(R.layout.item, null);
holder.name = (TextView)convertView.findViewById(R.id.name);
holder.info = (TextView)convertView.findViewById(R.id.info);
holder.attentntion=(TextView)convertView.findViewById(R.id.attention);
holder.name.setText((String)listItem.get(position).get("name"));
holder.info.setText((String)listItem.get(position).get("info"));
final TextView attention=holder.attentntion;
holder.attentntion.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
attention.setTextColor(Color.RED);
}
});
convertView.setTag(holder);
return convertView;
}
}
////工具类
/**
*
* @param inputStream
* @return
*/
public String readTextFile(InputStream inputStream) {
String readedStr = "";
BufferedReader br;
try {
br = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
String tmp;
while ((tmp = br.readLine()) != null) {
readedStr += tmp;
}
br.close();
inputStream.close();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return readedStr;
}
}
这样写会有什么问题呢?先来看一个效果图
当点击关注的时候我们让字体变红,但是当我们将当前的item移除屏幕然后在拉回来的时候,发现样式已经恢复默认了,为什么会出现这种情况呢,问题出在这里:
ViewHolder holder = null;
holder=new ViewHolder();
convertView = mInflater.inflate(R.layout.item, null);
holder.name = (TextView)convertView.findViewById(R.id.name);
holder.info = (TextView)convertView.findViewById(R.id.info);
holder.attentntion=(TextView)convertView.findViewById(R.id.attention);
每次运行getView获取当前ITEM时,都会重新new 一个viewHolder与R.layout.item绑定,也就是说,每次都会产生一个新布局赋值给convertView让其显示。而我们上面讲了,android会将回收过来的convertView返回给即将显示的getView使用,以节约资源。而我们这里却没有领情,每次都重新创建一个布局赋给convertView,由于每次都创建一个新布局,所以当ITEM1被重新拉回来显示的时候,由于是重新创建的布局,当然是初始状态。“关注”当然也就是黑色的了
下面我们来改进一下代码:
if (convertView == null) {
holder = new ViewHolder();
convertView = mInflater.inflate(R.layout.item, null);
holder.name = (TextView) convertView.findViewById(R.id.name);
holder.info = (TextView) convertView.findViewById(R.id.info);
holder.attentntion = (TextView) convertView
.findViewById(R.id.attention);
holder.name
.setText((String) listItem.get(position).get("name"));
holder.info
.setText((String) listItem.get(position).get("info"));
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
当convertView为空,即初始化创建时,我们就将生成的布局利用setTag()保存在convertView中,当convertView利用回收机制回收过来让我们再次使用时,我们通过getTag()将保存的布局取出来,重新将布局里的各个控件重新赋值就可以了。这里就利用了android-listView的回收机制,这时候再来看看效果图:
此时已经不会出现刚才那种问题了。但是同时又会引发另外一个问题,请看效果图
我们明明没有点P-6,为什么”关注”会变红呢?这事因为P-6使用的是P-1回收来的convertview,P-1里面的字是红色色,那么P-6里面的字当然也是红色的了。所以只要回收机制在,我们就没有办法改变从P-1回收来的convertView里的图片布局,除非人为的将其重置!
理解了这个问题以后,我们想想解决办法。
首先申请一个arrayList attentionArr变量,保存用户点击“关注”的item的position,然后在绘制当前item时,根据这个position是否在attentionArr里来判断是不是将“关注”重新变红。主要代码如下:
@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 = mInflater.inflate(R.layout.item, null);
holder.name = (TextView)convertView.findViewById(R.id.name);
holder.info = (TextView)convertView.findViewById(R.id.info);
holder.attentntion=(TextView)convertView.findViewById(R.id.attention);
convertView.setTag(holder);
}else {
holder = (ViewHolder)convertView.getTag();
}
holder.name.setText((String)listItem.get(position).get("name"));
holder.info.setText((String)listItem.get(position).get("info"));
final TextView attention=holder.attentntion;
//根据当前position判断,重新制做样式
if (attentionArr.contains(position)) {
attention.setTextColor(Color.RED);
}else {
attention.setTextColor(Color.BLACK);
}
holder.attentntion.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
attention.setTextColor(Color.RED);
attentionArr.add(position);//在点击时将position加入其中
}
});
return convertView;
}
}
再看效果:
理解代码难度不大,首先在OnClickListener时,将position加入到attentionArr数组中,然后在getView里,判断当前position是不是用户点击过的,即是否包含在attentionArr数组中,如果是,则将“关注”置为红色,否则置为初始色,黑色。