Android listView item侧滑实现删除和置顶功能

来源:互联网 发布:软件 qa qc区别 编辑:程序博客网 时间:2024/05/21 14:47

         第一次写博客,先说下大概思路吧~

        要显示item侧滑显示删除,置顶。首先要隐藏一部分item的布局(自定义隐藏布局宽度,在adapter里设置LayoutParams)。然后重写listview的onInterceptTouchEvent()和onTouchEvent()方法,然后对listview的滑动进行判断,最后进行相应的操作(删除啦,置顶啦,取消置顶bulabula)。删除需要dataList.remove(position),置顶就是将点击的item先执行dataList.add(0,object),然后执行dataList.remove(position),最后adapter.notifyDataSetChanged();先来两张效果图



1.新建attrs.xml,设置好自定义属性(其实就是右边隐藏布局的宽度啦),代码很简单,直接贴上来了


<?xml version="1.0" encoding="utf-8"?><resources>     <declare-styleable name="slidingitemlistview">        <attr name="right_width" format="dimension"></attr>    </declare-styleable></resources>
2.继承listview实现我们自己想要的效果~

(1)第一步在构造方法里获取自定义的宽度(右边局部隐藏的宽度)

public SlidingItemListView(Context context, AttributeSet attrs) {super(context, attrs);TypedArray typedArray = context.obtainStyledAttributes(attrs,R.styleable.slidingitemlistview);mRightViewWidth = (int) typedArray.getDimension(R.styleable.slidingitemlistview_right_width, 200);typedArray.recycle();}


(2)重写onInterceptTouchEvent()和onTouchEvent()方法,在ACTION_DOWN里获取mCurrentItemView,mPreItemView,mFirstX,mFirstY等。ACTION_UP里对是否在展示做简单的判断,在显示则隐藏。

@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) {float lastX = ev.getX();float lastY = ev.getY();switch (ev.getAction()) {case MotionEvent.ACTION_DOWN:mIsHorizontal = null;mFirstX = lastX;mFirstY = lastY;int position = pointToPosition((int) mFirstX, (int) mFirstY);if (position >= 0) {View view = getChildAt(position - getFirstVisiblePosition());mPreItemView = mCurrentItemView;mCurrentItemView = view;}Log.i("TAG", "onInterceptTouchEvent----->ACTION_DOWN");break;case MotionEvent.ACTION_MOVE:break;case MotionEvent.ACTION_UP:Log.i("TAG", "onInterceptTouchEvent----->ACTION_UP");/**点击隐藏布局会执行MotionEvent.ACTION_UP*/if (mIsShown) {hideRightView(mCurrentItemView);}break;default:break;}return super.onInterceptTouchEvent(ev);}@Overridepublic boolean onTouchEvent(MotionEvent ev) {float lastX = ev.getX();float lastY = ev.getY();switch (ev.getAction()) {case MotionEvent.ACTION_DOWN:Log.i("TAG", "onTouchEvent---->ACTION_DOWN");break;case MotionEvent.ACTION_MOVE:float dx = lastX - mFirstX;float dy = lastY - mFirstY;Log.i("TAG", "onTouchEvent---->ACTION_MOVE");if (mIsHorizontal == null) {if (!judgeScrollDirection(dx, dy)) {// 没判断出方向break;}}if (mIsHorizontal) {if (mIsShown&&mPreItemView!=mCurrentItemView) {//正在展示,前视图不等于后视图  //则隐藏前视图hideRightView(mPreItemView);}// 在mPreItemView!=mCurrentItemView执行 显示隐藏的宽度if (dx < 0 && dx > -mRightViewWidth) {Log.i("TAG", "onTouchEvent---->MOVE   -dx=" + -dx);mCurrentItemView.scrollTo((int) (-dx), 0);}// return true;} else {if (mIsShown) {//竖直方向滚动//则隐藏前视图hideRightView(mPreItemView);}}break;case MotionEvent.ACTION_UP:if (mIsShown) {//点击时如果有在显示的View//则隐藏前视图Log.i("TAG", "MotionEvent.ACTION_UP 隐藏前视图");//hideRightView(mCurrentItemView);hideRightView(mPreItemView);}if (mIsHorizontal != null && mIsHorizontal) {if (mFirstX - lastX > mRightViewWidth / 2) {showRight(mCurrentItemView);} else {// 不到一半则隐藏hideRightView(mCurrentItemView);}Log.i("TAG", "成功接管OnTouchEvent  CANCLE return TRUE"); return true;}break;default:break;}return super.onTouchEvent(ev);}/** * 展示隐藏的布局 * @param mCurrentItemView2 */private void showRight(View mCurrentItemView2) {mCurrentItemView2.scrollTo(mRightViewWidth, 0);mIsShown = true;}/**隐藏布局*/private void hideRightView(View mCurrentItemView2) {mCurrentItemView2.scrollTo(0, 0);mIsShown = false;}



这里面涉及到一个方法judgeScrollDirection,判断滑动方向,我是这么判断的


<span style="white-space:pre"></span>/** * @param 水平距离差 * @param 竖直距离差 * @return 水平滑动或者竖直滑动都返回true 没有判断出滑动方向则返回false */private boolean judgeScrollDirection(float dx, float dy) {if (Math.abs(dx) > 30 && Math.abs(dx) > Math.abs(dy) * 2) {mIsHorizontal = true;return true;}if (Math.abs(dy) > 30 && Math.abs(dy) > Math.abs(dx) * 2) {mIsHorizontal = false;return true;}return false;}

(3)啊,最后还有一个get setRightViewWidth方法不要我忘了,后面实例化adapter时还要用

<span style="white-space:pre"></span>public int getRightViewWidth() {return mRightViewWidth;}public void setRightViewWidth(int mRightViewWidth) {this.mRightViewWidth = mRightViewWidth;}


自定义listview到此就大功告成了,是不是直接就可以使用了呢?我很负责任的告诉你:绝对不可以!adapter表示自己不乐意!

下面就来写一个adapter吧。继承BaseAdapter重写getCount,getItem,getItemId,getView。当然最重要的是getView。这些比较简单,直接贴代码了

private Context mContext;private LayoutInflater mInflater;private List<SlidingItembean> list;private int mRightViewWidth;public SlidingItemListViewAdapter(Context mContext,List<SlidingItembean> list, int mRightViewWidth) {super();this.mContext = mContext;this.list = list;this.mRightViewWidth = mRightViewWidth;mInflater = LayoutInflater.from(mContext);}@Overridepublic int getCount() {// TODO Auto-generated method stubreturn list.size();}@Overridepublic Object getItem(int position) {// TODO Auto-generated method stubreturn list.get(position);}@Overridepublic long getItemId(int position) {// TODO Auto-generated method stubreturn position;}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {ViewHolder viewHolder;onClick listener;if (convertView == null) {convertView = mInflater.inflate(R.layout.item_sliding_listview,null);viewHolder = new ViewHolder();listener = new onClick();// 实例化viewHolder.Re_left = (RelativeLayout) convertView.findViewById(R.id.Re_left);viewHolder.ll_right = (LinearLayout) convertView.findViewById(R.id.ll_right);viewHolder.num = (TextView) convertView.findViewById(R.id.tv_num_Re_left);viewHolder.name = (TextView) convertView.findViewById(R.id.tv_name_Re_left);viewHolder.path = (TextView) convertView.findViewById(R.id.tv_path_Re_left);viewHolder.play = (ImageView) convertView.findViewById(R.id.img_play_Re_left);viewHolder.setTop= (TextView) convertView.findViewById(R.id.tv_setTop);viewHolder.ll_delete = (LinearLayout) convertView.findViewById(R.id.ll_delete_ll_right);viewHolder.ll_setTop = (LinearLayout) convertView.findViewById(R.id.ll_setTop_ll_right);viewHolder.ll_setTop.setOnClickListener(listener);// 监听viewHolder.ll_delete.setOnClickListener(listener);// 监听viewHolder.play.setOnClickListener(listener);// 监听convertView.setTag(viewHolder.play.getId(), listener);// 设置tagconvertView.setTag(viewHolder);} else {viewHolder = (ViewHolder) convertView.getTag();listener = (onClick) convertView.getTag(viewHolder.play.getId());// 获取实例}listener.setPosition(position);// 传递position// 设置布局参数LayoutParams lp_left = new LayoutParams(android.widget.LinearLayout.LayoutParams.MATCH_PARENT,android.widget.LinearLayout.LayoutParams.MATCH_PARENT);viewHolder.Re_left.setLayoutParams(lp_left);LayoutParams lp_right = new LayoutParams(mRightViewWidth,android.widget.LinearLayout.LayoutParams.MATCH_PARENT);viewHolder.ll_right.setLayoutParams(lp_right);SlidingItembean slidingItembean = list.get(position);viewHolder.num.setText(slidingItembean.getNum());viewHolder.name.setText(slidingItembean.getName());viewHolder.path.setText(slidingItembean.getPath());viewHolder.setTop.setText(slidingItembean.getSetTop());return convertView;}
static class ViewHolder {RelativeLayout Re_left;LinearLayout ll_right;LinearLayout ll_delete;LinearLayout ll_setTop;TextView num;TextView name;TextView path;ImageView play;TextView setTop;   }}

 

细心的同学可能会发现onClick 对象,listener。这个listener是干什么的呢?原来啊这是个继承OnClickListener的类,目的和ViewHolder一样,复用item。以前只是复用item控件,这下连监听事件都可以复用了,嘿嘿。代码是不会骗人的,来看看这个Onclick类吧

class onClick implements OnClickListener {int position;public void setPosition(int position) {this.position = position;}@Overridepublic void onClick(View v) {switch (v.getId()) {case R.id.img_play_Re_left:Toast.makeText(mContext, "play--->position=" + position,Toast.LENGTH_SHORT).show();break;case R.id.ll_delete_ll_right:list.remove(position);SlidingItemListViewAdapter.this.notifyDataSetChanged();break;case R.id.ll_setTop_ll_right:if (mySetTopInterface!=null) {mySetTopInterface.Onclick_ll_setTop_ll_right(v,position);}else {Toast.makeText(mContext, "mySetTopInterface==null",Toast.LENGTH_SHORT).show();}break;default:break;}}}


这里用到了一个自己定义的接口MySetTopInterface,作用显而易见,设置置顶的时候调用此接口,传递两个参数,一个是被点击的View,另一个是position。

MySetTopInterface mySetTopInterface;public interface MySetTopInterface {void Onclick_ll_setTop_ll_right(View view,int position);}public void setMySetTopInterface(MySetTopInterface mySetTopInterface) {this.mySetTopInterface = mySetTopInterface;}
adapter表示自己作用已完成,等待领导指示!

领导表示listview的item布局忘贴上来了,,,
下面贴item_sliding_listview布局,,,

<?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="55dp"    android:background="#fff"    android:orientation="horizontal" >    <RelativeLayout        android:id="@+id/Re_left"        android:layout_width="match_parent"        android:layout_height="match_parent" >        <TextView            android:id="@+id/tv_num_Re_left"            android:layout_width="20dp"            android:layout_height="20dp"            android:layout_centerVertical="true"            android:layout_marginLeft="10dp"            android:layout_marginRight="10dp"            android:background="@drawable/tv_num_bg"            android:gravity="center"            android:text="1"            android:textColor="#fff"            android:textSize="12sp" />        <RelativeLayout            android:layout_width="match_parent"            android:layout_height="wrap_content"            android:layout_centerVertical="true"            android:layout_toRightOf="@id/tv_num_Re_left" >            <TextView                android:id="@+id/tv_name_Re_left"                android:layout_width="match_parent"                android:layout_height="wrap_content"                android:layout_marginBottom="5dp"                android:text="《好久不见》"                android:textColor="#000"                android:textSize="16sp" />            <TextView                android:id="@+id/tv_path_Re_left"                android:layout_width="match_parent"                android:layout_height="wrap_content"                android:layout_below="@id/tv_name_Re_left"                android:text="/var/mobile/Contalners/Application"                android:textSize="10sp" />        </RelativeLayout>        <ImageView            android:id="@+id/img_play_Re_left"            android:layout_width="30dp"            android:layout_height="30dp"            android:layout_alignParentRight="true"            android:layout_centerVertical="true"            android:layout_marginRight="10dp"            android:scaleType="fitXY"            android:src="@drawable/wechat_icon" />    </RelativeLayout>    <LinearLayout        android:id="@+id/ll_right"        android:layout_width="wrap_content"        android:layout_height="match_parent"        android:orientation="horizontal" >        <LinearLayout            android:id="@+id/ll_delete_ll_right"            android:layout_width="0dp"            android:layout_height="match_parent"            android:layout_weight="1"            android:background="#F77D48"            android:gravity="center"            android:orientation="vertical"            android:padding="5dp" >            <ImageView                android:layout_width="30dp"                android:layout_height="30dp"                android:scaleType="fitXY"                android:src="@drawable/del_icon_normal" />            <TextView                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:text="删除"                android:textColor="#fff"                android:textSize="16sp" />        </LinearLayout>        <LinearLayout            android:id="@+id/ll_setTop_ll_right"            android:layout_width="0dp"            android:layout_height="match_parent"            android:layout_weight="1"            android:background="#FED33F"            android:gravity="center"            android:orientation="vertical"            android:padding="5dp" >            <ImageView                android:layout_width="30dp"                android:layout_height="30dp"                android:scaleType="fitXY"                android:src="@drawable/qq_icon" />            <TextView                android:id="@+id/tv_setTop"                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:text="置顶"                android:textColor="#fff"                android:textSize="12sp" />        </LinearLayout>    </LinearLayout></LinearLayout>


至此,listview的初始化算完事了。

下面来看看怎么应用吧(实现item置顶,取消置顶)

(1)在activity_main.xml里添加自定义的listview。其中  xmlns:dyk="http://schemas.android.com/apk/res/com.example.qqslidingitem"为自定义命名空间

 <com.example.qqslidingitem.SlidingItemListView        xmlns:dyk="http://schemas.android.com/apk/res/com.example.qqslidingitem"        android:id="@+id/listview"        android:layout_width="match_parent"        android:layout_height="match_parent"        android:background="#fff4f7f9"        android:cacheColorHint="#00000000"        android:divider="#dddbdb"        android:dividerHeight="1dp"        dyk:right_width="120dp" />
(2)接下来就该实现刚留的MySetTopInterface接口,复写Onclick_ll_setTop_ll_right方法。(初始化工作直接贴代码)

<span style="white-space:pre"></span>private SlidingItemListView mListView;private SlidingItemListViewAdapter adapter;private List<SlidingItembean> list = new ArrayList<SlidingItembean>();@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);// requestWindowFeature(Window.FEATURE_NO_TITLE);setContentView(R.layout.activity_main);initView();initData();initEvent();}private void initEvent() {adapter = new SlidingItemListViewAdapter(MainActivity.this, list,mListView.getRightViewWidth());mListView.setAdapter(adapter);adapter.setMySetTopInterface(this);// mListView.setSelection(position);mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {@Overridepublic void onItemClick(AdapterView<?> parent, View view,int position, long id) {Toast.makeText(MainActivity.this,"item onclick " + list.get(position).getNum(),Toast.LENGTH_SHORT).show();}});}private void initData() {for (int i = 0; i < 50; i++) {SlidingItembean slidingItembean = null;if (i % 3 == 0) {slidingItembean = new SlidingItembean(String.valueOf(i),"你会不会忽然的出现", "/var/mobile/Contalners/Application","置顶");} else if (i % 3 == 1) {slidingItembean = new SlidingItembean(String.valueOf(i),"在街角的咖啡店", "/var/mobile/Contalners/Application","置顶");} else {slidingItembean = new SlidingItembean(String.valueOf(i),"我会带着笑脸,和你,坐着聊聊天", "/var/mobile/Contalners/Application","置顶");}list.add(slidingItembean);}}/** * 初始化界面 */private void initView() {mListView = (SlidingItemListView) findViewById(R.id.listview);}


接下来重点完成置顶和取消置顶功能~

@Overridepublic void Onclick_ll_setTop_ll_right(View view, int position) {if (list.get(position).getSetTop().equals("置顶")) {setTop(position);} else if (list.get(position).getSetTop().equals("取消置顶")) {unSetTop(position);}}


置顶setTop

<span style="white-space:pre"></span>/** * 置顶 * @param position */private void setTop(int position) {list.get(position).setSetTop("取消置顶");list.add(0, list.get(position));// 置顶后list.size增加一 所以要position+1list.remove(position + 1);adapter.notifyDataSetChanged();}


取消置顶我采用的策略是先遍历datalist,然后找到前一项比他小后一项比他大的位置然后插入。细心的同学会发现这样写有一个小小的bug要是选择项就是最小的没法处理,所以会有些特殊情况要单独拿出来讨论。

<span style="white-space:pre"></span>/** * 取消置顶 * @param position */private void unSetTop(int position) {boolean isAdd = false;/** 差值 */int min = 9999999;/** 当前position的数值 */int num;// 差值最小处的行数int j = 0;int num2 = 0;int jumpNum = 0;list.get(position).setSetTop("置顶");num = Integer.parseInt(list.get(position).getNum());// list长度为2特殊处理if (list.size() == 2) {// 第一行确定为取消置顶if (list.get(1).getSetTop().equals("取消置顶")) {if (position == 0) {if (num == 0) {list.add(2, list.get(position));}if (num == 1) {list.add(2, list.get(position));}list.remove(position);adapter.notifyDataSetChanged();} else {list.add(2, list.get(position));list.remove(position);adapter.notifyDataSetChanged();}} else {if (num == 0) {list.add(1, list.get(position));}if (num == 1) {list.add(2, list.get(position));}list.remove(position);adapter.notifyDataSetChanged();}} else {for (int i = 0; i < list.size(); i++) {if (num > Integer.parseInt(list.get(i).getNum())&& num < Integer.parseInt(list.get(i + 1).getNum())) {list.add(i + 1, list.get(position));isAdd = true;break;}}// 如果没有比自己小的值 例如0 则isAdd=false// 遍历list 寻找差值最小的地方插入listif (!isAdd) {for (int i = 0; i < list.size(); i++) {if (i == position || list.get(i).getSetTop().equals("取消置顶")) {// 排除与自身相比较// 排除置顶item比较Log.i("TAG", "调过" + i);jumpNum++;if (jumpNum == list.size()) {j = list.size();}continue;}num2 = Integer.parseInt(list.get(i).getNum());if (num2 - num < min) {min = num2 - num;// 记录行号j = i;Log.i("TAG", "插入行数J=" + j);}}// 遍历完成后拿到差值minint number = min + num;list.add(j, list.get(position));Log.i("TAG", "*********插入行数J=" + j);}list.remove(position);adapter.notifyDataSetChanged();}}

 

大功告成!接下来做一个小总结吧。首先是自定义属性,其次是对布局隐藏的处理,第三是对getView中item的复用,最后是对自定义接口中删除,置顶,取消置顶功能实现的处理。第一次写的博客,不好的地方请谅解。



源码下载:http://download.csdn.net/detail/qq_17250009/9228877

1 0
原创粉丝点击