本文主要记录一些零碎的东西
在开发中ListView经常使用到,可以点击一行的数据,但是如果想点击一行里面的某个控件,比如按钮,用SimpleAdapter添加一个按钮到ListView的条目中,会发现可以添加,但是却无法获得焦点,点击操作被ListView的Item所覆盖就需要自己实现ListView的adapter,首先看一下效果
项目中遇到,抽出来单独实现一下,看看是怎么实现的
需要写一个类继承BaseAdapter,需要重新它的几个方法,最重要的是getView()方法。
当系统开始绘制ListView的时候,首先调用getCount()方法。得到它的返回值,即ListView的长度。然后系统调用getView()方法,根据这个长度逐一绘制ListView的每一行。也就是说,如果让getCount()返回1,那么只显示一行。而getItem()和getItemId()则在需要处理和取得Adapter中的数据时调用。
ListView优化:通过convertView+ViewHolder来实现,ViewHolder就是一个静态类,使用 ViewHolder 的关键好处是缓存了显示数据的视图(View),加快了 UI 的响应速度。当判断 convertView == null 的时候,如果为空,就会根据设计好的List的Item布局(XML),来为convertView赋值,并生成一个viewHolder来绑定converView里面的各个View控件(XML布局里面的那些控件)。再用convertView的setTag将viewHolder设置到Tag中,以便系统第二次绘制ListView时从Tag中取出。
在getView里对需要有点击监听的控件进行点击事件的监听,下面看看具体代码实现
import android.content.Context; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.TextView; import java.util.List; import java.util.Map; /** 自定义的adapter extends BaseAdapter,可以点击里面的控件 * Created by chenling on 2016/4/19. */ public class MyAdapter extends BaseAdapter { private Context context; private List<Map<String, Object>> list; /**构造函数 * @param context context * @param list List<Map<String, Object>>类型要显示的数据 * */ public MyAdapter(Context context,List<Map<String, Object>> list) { this.context = context; this.list = list; } //在此适配器中所代表的数据集中的条目数 @Override public int getCount() { return list.size(); } //在此适配器中所代表的数据集中的指定位置的条目 @Override public Object getItem(int position) { return list.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { final ViewHolder holder; final String info = list.get(position).get("info").toString(); final int pos = position; if (convertView == null) { convertView = LayoutInflater.from(context).inflate(R.layout.list_item, null); holder = new ViewHolder(); /**得到各个控件的对象*/ holder.pic = (ImageView) convertView.findViewById(R.id.imageView); holder.collect = (ImageView) convertView.findViewById(R.id.collect); holder.textView = (TextView) convertView.findViewById(R.id.textView); convertView.setTag(holder);//绑定ViewHolder对象 }else{ holder = (ViewHolder)convertView.getTag();//取出ViewHolder对象 } holder.pic.setBackgroundResource((Integer)list.get(position).get("img")); holder.textView.setText(list.get(position).get("info").toString()); if("true".equals(list.get(position).get("select"))){ holder.collect.setBackgroundResource(R.drawable.collect_selected); } //点击可以更改图片,模拟收藏 holder.collect.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Log.i("slack","collectOnClick info:"+info); Log.i("slack","current select:"+list.get(pos).get("select")); if("false".equals(list.get(pos).get("select"))){ // unselect --> select holder.collect.setBackgroundResource(R.drawable.collect_selected); list.get(pos).put("select","true"); }else{ // select --> unselect holder.collect.setBackgroundResource(R.drawable.collect); list.get(pos).put("select","false"); } } }); return convertView; } /**存放控件*/ public final class ViewHolder{ public ImageView pic,collect; public TextView textView; } }
import android.app.Activity; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.AdapterView; import android.widget.ListView; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** 使用自定义的adapter listView里可以点击里面的控件 * Created by chenling on 2016/4/19. */ public class MainActivity extends Activity { private int[] pic = {R.drawable.a,R.drawable.b,R.drawable.c,R.drawable.d}; private ListView listView; private MyAdapter myAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); myAdapter = new MyAdapter(this,getDate()); listView = (ListView) findViewById(R.id.lv); listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { Log.i("slack","onItemClick position:"+position); } }); listView.setAdapter(myAdapter); } /**添加一个得到数据的方法,方便使用*/ private List<Map<String, Object>> getDate(){ List<Map<String, Object>> listItem = new ArrayList<>(); /**为动态数组添加数据*/ for(int i=0;i<20;i++) { Map<String, Object> map = new HashMap<>(); map.put("img",pic[(int)((Math.random()*10)%4)]); map.put("info", "这是第"+i+"行"); map.put("select",(i % 2 == 0) ?"true":"false"); listItem.add(map); } return listItem; } }
附件:module下载:http://download.csdn.net/detail/i_do_can/9496141
- - - - - - - - - - - - - - -更新 2014-04-21 - - - - - - - - - - -
新增listView里的item左滑事件,先看看效果
代码是在上面的基础上实现的,思路还是修改自定义adapter,添加Touch监听,实际过程中发现adapter里设置的Touch监听被listView的item点击事件覆盖了,只可以监听到MotionEvent.ACTION_DOWN,其他动作就接收不到了。这个可以通过 return true来不响应Item的点击,但是一旦return true,Item的点击就永远不被监听到啦。
解决思路就是adapter里设置单个view的OnClick事件,这里面回调activity里的点击事件,同时activity里不设置OnItemClick,看看代码:
MainActivity:取消Item的点击事件监听代码,新增一个回调函数
import android.app.Activity; import android.os.Bundle; import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.widget.AdapterView; import android.widget.ListView; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** 使用自定义的adapter listView里可以点击里面的控件 * Created by chenling on 2016/4/19. */ public class MainActivity extends Activity { private int[] pic = {R.drawable.a,R.drawable.b,R.drawable.c,R.drawable.d}; private ListView listView; private MyAdapter myAdapter; private float oldX,newX; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); myAdapter = new MyAdapter(this,getDate()); listView = (ListView) findViewById(R.id.lv); // listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { // @Override // public void onItemClick(AdapterView<?> parent, View view, int position, long id) { // Log.i("slack", "onItemClick position:" + position); // } // }); listView.setAdapter(myAdapter); } /**添加一个得到数据的方法,方便使用*/ private List<Map<String, Object>> getDate(){ List<Map<String, Object>> listItem = new ArrayList<>(); /**为动态数组添加数据*/ for(int i=0;i<20;i++) { Map<String, Object> map = new HashMap<>(); map.put("img",pic[(int)((Math.random()*10)%4)]); map.put("info", "这是第"+i+"行"); map.put("select",(i % 2 == 0) ?"true":"false"); listItem.add(map); } return listItem; } //单个Item 的点击事件(回调,参数为被点击的位置) public void onItemClick(int position){ Log.i("slack", "onItemClick position:..." + position); } }因为左滑时需要显示一个新的控件,所以布局文件需要换一换,新增一个控件,visible=gone
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="5dp"> <ImageView android:id="@+id/imageView" android:layout_width="40dp" android:layout_height="40dp" /> <LinearLayout android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:paddingRight="10dp" > <TextView android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="match_parent" android:singleLine="true" android:ellipsize="end" android:text="1111111" android:layout_weight="1" android:gravity="center" /> <ImageView android:id="@+id/collect" android:paddingTop="3dp" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/collect"/> </LinearLayout> <ImageView android:visibility="gone" android:id="@+id/slideCollect" android:paddingTop="3dp" android:paddingRight="20dp" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/collect" android:layout_alignParentTop="true" android:layout_alignParentRight="true" android:layout_alignParentEnd="true" /> </LinearLayout>
import android.content.Context; import android.util.Log; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import java.util.List; import java.util.Map; /** 自定义的adapter extends BaseAdapter,可以点击里面的控件,左滑事件 * Created by chenling on 2016/4/19. */ public class MyAdapter extends BaseAdapter { private Context context; private List<Map<String, Object>> list; private float downx,upx;//单行左滑时 private ImageView curSelect; /**构造函数 * @param context context * @param list List<Map<String, Object>>类型要显示的数据 * */ public MyAdapter(Context context,List<Map<String, Object>> list) { this.context = context; this.list = list; } //在此适配器中所代表的数据集中的条目数 @Override public int getCount() { return list.size(); } //在此适配器中所代表的数据集中的指定位置的条目 @Override public Object getItem(int position) { return list.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { final ViewHolder holder; final String info = list.get(position).get("info").toString(); final int pos = position; if (convertView == null) { //这个布局可以实现滑动出现,是通过visible 属性,weight = 1 来实现,没有listItem消失一部分 convertView = LayoutInflater.from(context).inflate(R.layout.list_item, null); holder = new ViewHolder(); /**得到各个控件的对象*/ holder.linerLayout = (LinearLayout)convertView.findViewById(R.id.linerLayout); holder.pic = (ImageView) convertView.findViewById(R.id.imageView); holder.collect = (ImageView) convertView.findViewById(R.id.collect); holder.textView = (TextView) convertView.findViewById(R.id.textView); holder.slideCollect = (ImageView)convertView.findViewById(R.id.slideCollect); convertView.setTag(holder);//绑定ViewHolder对象 }else{ holder = (ViewHolder)convertView.getTag();//取出ViewHolder对象 } holder.pic.setBackgroundResource((Integer) list.get(position).get("img")); holder.textView.setText(list.get(position).get("info").toString()); if("true".equals(list.get(position).get("select"))){ holder.collect.setBackgroundResource(R.drawable.collect_selected); holder.slideCollect.setBackgroundResource(R.drawable.collect_selected); } //点击可以更改图片,模拟收藏 holder.collect.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Log.i("slack", "collectOnClick info:" + info); Log.i("slack", "current select:" + list.get(pos).get("select")); if ("false".equals(list.get(pos).get("select"))) { // unselect --> select holder.collect.setBackgroundResource(R.drawable.collect_selected); list.get(pos).put("select", "true"); } else { // select --> unselect holder.collect.setBackgroundResource(R.drawable.collect); list.get(pos).put("select", "false"); } } }); holder.slideCollect.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if ("false".equals(list.get(pos).get("select"))) { // unselect --> select holder.slideCollect.setBackgroundResource(R.drawable.collect_selected); list.get(pos).put("select", "true"); } else { // select --> unselect holder.slideCollect.setBackgroundResource(R.drawable.collect); list.get(pos).put("select", "false"); } } }); //不能写,把listview里的item的点击事件取消了,不过可以强转activity,然后回调里面的方法 convertView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // Log.i("slack","convertView OnClickListener"); //回调MainActivity 里的方法,传回被点击的itemd的position ((MainActivity)context).onItemClick(pos); } }); ////为每一个view项设置触控监听 左滑出现收藏按钮 跟listView.setOnItemClickListener应该是冲突了 convertView.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { final ViewHolder holder = (ViewHolder) v.getTag(); //当按下时处理 if (event.getAction() == MotionEvent.ACTION_DOWN) { //获取按下时的x轴坐标 downx = event.getX(); Log.i("slack","convertView downx:"+downx); //判断之前是否出现了收藏按钮如果存在就隐藏 if (curSelect != null) { if(curSelect.getVisibility() == View.VISIBLE){ curSelect.setVisibility(View.GONE); return true; } } //解决下面的MotionEvent.ACTION_UP接收不到,与listview里的点击事件的冲突,但是这样的话就不相应Item点击了 // return true; } else if (event.getAction() == MotionEvent.ACTION_UP) {// 松开处理 //获取松开时的x坐标 upx = event.getX(); Log.i("slack","convertView upx:"+upx); //判断当前项中按钮控件不为空时 if (holder.slideCollect != null) { // //按下和松开值差当大于20时显示删除按钮,否则不显示 if (downx - upx > 20) {//左滑时 holder.slideCollect.setVisibility(View.VISIBLE); curSelect = holder.slideCollect; return true; } } } else if (event.getAction() == MotionEvent.ACTION_MOVE) {//当滑动时 // Log.i("slack","convertView ACTION_MOVE...."); return true; } else {//其他模式 } return false; } }); return convertView; } /**存放控件*/ public final class ViewHolder{ public LinearLayout linerLayout; public ImageView pic,collect,slideCollect; public TextView textView; } }当然上面的可以实现滑动,但是很丑,滑动时是一个控件出现,listview的Item变小,想着实现滑动时Item往左移
可以试试padding属性,貌似是可以的,但是他只是控件里的内容左移,还是占据着屏幕的宽度,唯一的不好就是不能加背景色,要不就看出来了。
本人动画学的不是太好,界面也是不太好看,单纯技术
- - - - - - - - - - 更新 2016-04-21 - - - - - - - - -
下面的这个效果才算是实现了我想要的效果,先看一下效果
加上颜色,主要是要说明这是上下两个控件,说一下最终的实现思路:
在布局文件里做文章,要实现上下两层控件的覆盖,就需要RelativeLayout。
布局文件的LinerLayout里放两个LinerLayout,第一个的宽度为屏幕的宽,那么第二个及自然被挤到屏幕的外面去了,第一个的LinerLayout给一个背景色,设置透明度为完全不透明。
android:background="#ffffffff" 前两个参数是透明度,后面六位是颜色,第二个LinerLayout给个透明度就行,使用默认的不写也行。
这样滑动时,我是通过设置padding的值,padding是指控件内的控件的间距,主要是right的padding值,原先被挤出屏幕的LinerLayout就会出现,好了,看看布局文件代码
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="50dp" android:padding="5dp"> <!--滑出的控件在下面--> <LinearLayout android:layout_width="wrap_content" android:layout_height="match_parent" android:paddingRight="10dp" android:paddingLeft="5dp" android:layout_alignParentTop="true" android:layout_alignParentRight="true" android:layout_alignParentEnd="true" > <ImageView android:id="@+id/slideCollect" android:paddingTop="3dp" android:layout_width="wrap_content" android:layout_height="match_parent" android:background="@drawable/favor" /> </LinearLayout> <LinearLayout android:id="@+id/linerLayout" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal"> <!--ListView Item在上面,前两个ff 不透明--> <LinearLayout android:background="#ffffffff" android:layout_width="match_parent" android:layout_height="wrap_content" > <ImageView android:id="@+id/imageView" android:layout_width="40dp" android:layout_height="40dp" /> <TextView android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="match_parent" android:singleLine="true" android:ellipsize="end" android:text="1111111" android:textColor="#000" android:layout_weight="1" android:gravity="center" /> <ImageView android:id="@+id/collect" android:paddingTop="3dp" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/collect"/> </LinearLayout> <!--透明的控件--> <LinearLayout android:background="#0fcccccc" android:layout_width="40dp" android:layout_height="match_parent"> </LinearLayout> </LinearLayout> </RelativeLayout>然后修改adapter,贴上全部代码
import android.content.Context; import android.util.Log; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.RelativeLayout; import android.widget.TextView; import java.util.List; import java.util.Map; /** 自定义的adapter extends BaseAdapter,可以点击里面的控件,左滑事件 * 布局文件右边加一个空白的控件,用于滑动显示后面的被挡住的控件 * Created by chenling on 2016/4/19. */ public class MyAdapter extends BaseAdapter { private Context context; private List<Map<String, Object>> list; private float downx,upx;//单行左滑时 private ImageView curSelect;//弹出的收藏按钮 private LinearLayout lastView;//上一次点击滑动的view private int screenWidth;//屏幕宽度,想把LinearLayout的宽度设为屏幕宽 /**构造函数 * @param context context * @param list List<Map<String, Object>>类型要显示的数据 * */ public MyAdapter(Context context,List<Map<String, Object>> list) { this.context = context; this.list = list; screenWidth = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getWidth(); Log.i("slack","screenWidth:"+screenWidth); } //在此适配器中所代表的数据集中的条目数 @Override public int getCount() { return list.size(); } //在此适配器中所代表的数据集中的指定位置的条目 @Override public Object getItem(int position) { return list.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { final ViewHolder holder; final String info = list.get(position).get("info").toString(); final int pos = position; if (convertView == null) { //这个布局可以实现滑动出现,是通过visible 属性,weight = 1 来实现,没有listItem消失一部分 // convertView = LayoutInflater.from(context).inflate(R.layout.list_item, null); convertView = LayoutInflater.from(context).inflate(R.layout.list_item_slide, null); holder = new ViewHolder(); /**得到各个控件的对象*/ holder.linerLayout = (LinearLayout)convertView.findViewById(R.id.linerLayout); // holder.linerLayout.setLayoutParams(new RelativeLayout.LayoutParams(screenWidth,50)); holder.pic = (ImageView) convertView.findViewById(R.id.imageView); holder.collect = (ImageView) convertView.findViewById(R.id.collect); holder.textView = (TextView) convertView.findViewById(R.id.textView); holder.slideCollect = (ImageView)convertView.findViewById(R.id.slideCollect); convertView.setTag(holder);//绑定ViewHolder对象 }else{ holder = (ViewHolder)convertView.getTag();//取出ViewHolder对象 } holder.pic.setBackgroundResource((Integer) list.get(position).get("img")); holder.textView.setText(list.get(position).get("info").toString()); if("true".equals(list.get(position).get("select"))){ holder.collect.setBackgroundResource(R.drawable.collect_selected); holder.slideCollect.setBackgroundResource(R.drawable.favor_fill); } //点击可以更改图片,模拟收藏 holder.collect.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Log.i("slack", "collectOnClick info:" + info); Log.i("slack", "current select:" + list.get(pos).get("select")); if ("false".equals(list.get(pos).get("select"))) { // unselect --> select holder.collect.setBackgroundResource(R.drawable.collect_selected); list.get(pos).put("select", "true"); } else { // select --> unselect holder.collect.setBackgroundResource(R.drawable.collect); list.get(pos).put("select", "false"); } } }); //滑动点赞 holder.slideCollect.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if ("false".equals(list.get(pos).get("select"))) { // unselect --> select holder.slideCollect.setBackgroundResource(R.drawable.favor_fill); list.get(pos).put("select", "true"); } else { // select --> unselect holder.slideCollect.setBackgroundResource(R.drawable.favor); list.get(pos).put("select", "false"); } } }); //不能写,把listview里的item的点击事件取消了,不过可以强转activity,然后回调里面的方法 convertView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // Log.i("slack","convertView OnClickListener"); //回调MainActivity 里的方法,传回被点击的itemd的position ((MainActivity)context).onItemClick(pos); } }); ////为每一个view项设置触控监听 左滑出现收藏按钮 跟listView.setOnItemClickListener应该是冲突了 convertView.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { final ViewHolder holder = (ViewHolder) v.getTag(); //当按下时处理 if (event.getAction() == MotionEvent.ACTION_DOWN) { //获取按下时的x轴坐标 downx = event.getX(); Log.i("slack","convertView downx:"+downx); //上一次滑动的还原,这里的10是因为布局xml里的padding值 if(lastView != null ){ lastView.setPadding(0, 0, 0, 0); } //判断之前是否出现了收藏按钮如果存在就隐藏 // if (curSelect != null) { // if(curSelect.getVisibility() == View.VISIBLE){ // curSelect.setVisibility(View.GONE); // return true; // } // } //解决下面的MotionEvent.ACTION_UP接收不到,与listview里的点击事件的冲突,但是这样的话就不相应Item点击了 // return true; } else if (event.getAction() == MotionEvent.ACTION_UP) {// 松开处理 //获取松开时的x坐标 upx = event.getX(); Log.i("slack","convertView upx:"+upx); //判断当前项中按钮控件不为空时 if (holder.slideCollect != null) { // // //按下和松开绝对值差当大于20时显示删除按钮,否则不显示 lastView = holder.linerLayout; if (Math.abs(downx - upx) > 20) { // if (downx - upx > 20) {//左滑时 // holder.slideCollect.setVisibility(View.VISIBLE); // curSelect = holder.slideCollect; return true; } } } else if (event.getAction() == MotionEvent.ACTION_MOVE) {//当滑动时背景为选中状态 // Log.i("slack","convertView ACTION_MOVE...."); //move过程中通过设置padding属性移动?滑动一定距离以内,跟着滑动,超过这个距离,不继续滑动 if(downx - upx < 70){ // setPadding (int left, int top, int right, int bottom) holder.linerLayout.setPadding(-(int)Math.abs(downx - upx),0,((int)Math.abs(downx - upx))+10,0); }else{ holder.linerLayout.setPadding(-70,0,80,0); } return true; } else {//其他模式 // Log.i("slack","convertView other...."); holder.linerLayout.setPadding(0, 0, 0, 0); } return false; } }); return convertView; } /**存放控件*/ public final class ViewHolder{ public LinearLayout linerLayout; public ImageView pic,collect,slideCollect; public TextView textView; } }
改变点击过的Item的颜色,可以换背景,我这里只是简单的更改一下字体的颜色
在convertView.setOnClickListener点击监听函数里增加一句话即可
convertView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // Log.i("slack","convertView OnClickListener"); //回调MainActivity 里的方法,传回被点击的itemd的position ((MainActivity)context).onItemClick(pos); holder.textView.setTextColor(Color.LTGRAY); } });
附件:最终module:http://download.csdn.net/detail/i_do_can/9498346