转自: http://blog.csdn.net/xujk2008/article/details/10049845
最近需要给ListView写一个监听器,在按住ListView中Item不松手的时候,改变Item的样式,使得被按住的Item背景色为灰,并显示跑马灯效果。因为需要具体到每个Item,所以首先想到了OnItemClickListener。但是稍微想想就知道这样是不行的,OnItemClickListener监听的是点击某个Item的动作,点击包括按下和松开两个部分,所以不用到达想要的效果。
于是想到用OnTouchListener监听按下的动作。但是,你知道我是会说但是的。OnTouchListener针对的是整个ListView,并没有提供被点击Item的位置参数,所以这样并不能达到改变单个Item显示效果的目的。顿时有种想掀桌的冲动,但是实验室的桌子我真的掀不动。于是去StackOverflow搜索,开始搜索不得法,没有找到想要的东西。难道老外们都不屑于问这种小白问题吗?不可能,我明明见过有人问Hello world为啥跑不出来之类的问题来着。几经变换关键词之后找到了一个和我问题很相似的提问。大概意思就是说 :“哥们儿,我原来遇到的问题和你现在遇到的这个问题贼拉想,唉呀妈呀,我告诉你咋整呗。你就自个儿写一个BaseAdapter子类,然后再getView()方法里加一个OnTouchListener就欧了,缸缸地好使。”之前要写跑马灯效果,所以自定义的适配器已经写过了,按照提问里说的办法加了OnTouchListener。
- package org.cdpsn.client.widget.wheel;
- import java.util.List;
- import java.util.Map;
- import org.cdpsn.client.R;
- import android.content.Context;
- import android.graphics.Color;
- import android.view.LayoutInflater;
- import android.view.MotionEvent;
- import android.view.View;
- import android.view.ViewGroup;
- import android.widget.BaseAdapter;
- import android.widget.TextView;
- public class ListItemAdapter extends BaseAdapter
- {
- private LayoutInflater inflater;
- private List<Map<String, Object>> items;
- private static int index = 0;
- public ListItemAdapter(Context context, List<Map<String, Object>> items) {
- super();
- this.inflater = LayoutInflater.from(context);
- this.items = items;
- }
- public void setIndex(int selected)
- {
- index = selected;
- }
- @Override
- public int getCount() {
- // TODO Auto-generated method stub
- return items.size();
- }
- @Override
- public Object getItem(int position) {
- // TODO Auto-generated method stub
- return items.get(position);
- }
- @Override
- public long getItemId(int position) {
- // TODO Auto-generated method stub
- return position;
- }
- @Override
- public View getView(final int position, View convertView, ViewGroup parent) {
- // TODO Auto-generated method stub
- System.out.println("Get in function getView.");
- ViewHolder holder;
- if(convertView == null)
- {
- convertView = inflater.inflate(R.layout.list_job, null);
- holder = new ViewHolder();
- holder.jobTitle = (TextView)convertView.findViewById(R.id.jobTitle);
- holder.content = (TextView)convertView.findViewById(R.id.content);
- holder.publishTimeText = (TextView)convertView.findViewById(R.id.publishTimeText);
- convertView.setOnTouchListener(new View.OnTouchListener() {
- @Override
- public boolean onTouch(View view, MotionEvent motionEvent) {
- if(motionEvent.getAction() == MotionEvent.ACTION_DOWN)
- {
- System.out.println("Item" + position + "pressed.");
- ListItemAdapter.this.setIndex(position);
- ListItemAdapter.this.notifyDataSetChanged();
- }
- return false;
- }
- });
- }
- else
- {
- holder = (ViewHolder)convertView.getTag();
- }
- if(index == position)
- {
- convertView.findViewById(R.id.content).setSelected(true);
- // convertView.setBackgroundColor(Color.rgb(196, 215, 0));
- convertView.setBackgroundColor(Color.LTGRAY);
- holder.jobTitle.setTextColor(Color.WHITE);
- holder.content.setTextColor(Color.WHITE);
- holder.publishTimeText.setTextColor(Color.WHITE);
- }
- else
- {
- convertView.findViewById(R.id.content).setSelected(false);
- convertView.setBackgroundColor(Color.WHITE);
- holder.jobTitle.setTextColor(Color.BLACK);
- holder.content.setTextColor(Color.BLACK);
- holder.publishTimeText.setTextColor(Color.BLACK);
- }
- convertView.setTag(holder);
- holder.jobTitle.setText( (String)items.get(position).get("jobTitle") );
- holder.content.setText((String)items.get(position).get("content"));
- holder.publishTimeText.setText((String)items.get(position).get("publishTimeText"));
- return convertView;
- }
- private class ViewHolder {
- private TextView jobTitle;
- private TextView content;
- private TextView publishTimeText;
- }
- }
试着跑了一下程序,似乎是可以的。但是多试了几次之后,发现还是有问题,当按住位置靠后的Item时,被按住的子项显示的效果并没有改变,改变的是位置靠前的子项。于是去搜索了一下getView的工作机制,发现convertView并不像我想象的那么简单。在配置适配器时,getView并没有为每一个Item配备一个convertView。设想一下,假如列表中有上百个Item,同时配备这么多convertView是很浪费资源的。所以convertView其实是可以循环利用的。也就是说,随着列表向下拉,屏幕上会显示新的子项,而老的子项会移出屏幕。这是,老的子项会把convertView交给新的子项使用,节省了资源。
再看上面的代码,只有在convertView为空时,才为convertView设置OnTouchListener监听器。这样,老的Item在移交convertView的同时,也把老的监听器移交给了新的Item。所以才会出现按住下面的子项,上面的子项变化样式的诡异情况。清楚了原理之后,把监听器的设置写在了判断convertView是否为空的程序片段的外面,代码如下:
- package org.cdpsn.client.widget.wheel;
- import java.util.List;
- import java.util.Map;
- import org.cdpsn.client.R;
- import android.content.Context;
- import android.graphics.Color;
- import android.view.LayoutInflater;
- import android.view.MotionEvent;
- import android.view.View;
- import android.view.ViewGroup;
- import android.widget.BaseAdapter;
- import android.widget.TextView;
- public class ListItemAdapter extends BaseAdapter
- {
- private LayoutInflater inflater;
- private List<Map<String, Object>> items;
- private static int index = 0;
- public ListItemAdapter(Context context, List<Map<String, Object>> items) {
- super();
- this.inflater = LayoutInflater.from(context);
- this.items = items;
- }
- public void setIndex(int selected)
- {
- index = selected;
- }
- @Override
- public int getCount() {
- // TODO Auto-generated method stub
- return items.size();
- }
- @Override
- public Object getItem(int position) {
- // TODO Auto-generated method stub
- return items.get(position);
- }
- @Override
- public long getItemId(int position) {
- // TODO Auto-generated method stub
- return position;
- }
- @Override
- public View getView(final int position, View convertView, ViewGroup parent) {
- // TODO Auto-generated method stub
- System.out.println("Get in function getView.");
- ViewHolder holder;
- if(convertView == null)
- {
- convertView = inflater.inflate(R.layout.list_job, null);
- holder = new ViewHolder();
- holder.jobTitle = (TextView)convertView.findViewById(R.id.jobTitle);
- holder.content = (TextView)convertView.findViewById(R.id.content);
- holder.publishTimeText = (TextView)convertView.findViewById(R.id.publishTimeText);
- }
- else
- {
- holder = (ViewHolder)convertView.getTag();
- }
- convertView.setOnTouchListener(new View.OnTouchListener() {
- @Override
- public boolean onTouch(View view, MotionEvent motionEvent) {
- if(motionEvent.getAction() == MotionEvent.ACTION_DOWN)
- {
- System.out.println("Item" + position + "pressed.");
- ListItemAdapter.this.setIndex(position);
- ListItemAdapter.this.notifyDataSetChanged();
- }
- return false;
- }
- });
- if(index == position)
- {
- convertView.findViewById(R.id.content).setSelected(true);
- // convertView.setBackgroundColor(Color.rgb(196, 215, 0));
- convertView.setBackgroundColor(Color.LTGRAY);
- holder.jobTitle.setTextColor(Color.WHITE);
- holder.content.setTextColor(Color.WHITE);
- holder.publishTimeText.setTextColor(Color.WHITE);
- }
- else
- {
- convertView.findViewById(R.id.content).setSelected(false);
- convertView.setBackgroundColor(Color.WHITE);
- holder.jobTitle.setTextColor(Color.BLACK);
- holder.content.setTextColor(Color.BLACK);
- holder.publishTimeText.setTextColor(Color.BLACK);
- }
- convertView.setTag(holder);
- holder.jobTitle.setText( (String)items.get(position).get("jobTitle") );
- holder.content.setText((String)items.get(position).get("content"));
- holder.publishTimeText.setText((String)items.get(position).get("publishTimeText"));
- return convertView;
- }
- private class ViewHolder {
- private TextView jobTitle;
- private TextView content;
- private TextView publishTimeText;
- }
- }
这样,无论新的convertView,还是移交过来的老的convertView,都设置了和当前Item位置相对应的OnTouchListener,自然也就达到了预想的效果。