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

简介:

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

        要显示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里对是否在展示做简单的判断,在显示则隐藏。

	@Override
	public 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);
	}

	@Override
	public 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);
	}

	@Override
	public int getCount() {
		// TODO Auto-generated method stub
		return list.size();
	}

	@Override
	public Object getItem(int position) {
		// TODO Auto-generated method stub
		return list.get(position);
	}

	@Override
	public long getItemId(int position) {
		// TODO Auto-generated method stub
		return position;
	}

	@Override
	public 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);// 设置tag
			convertView.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;
		}

		@Override
		public 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>();


	@Override
	protected 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() {
			@Override
			public 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);

	}


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

@Override
	public 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+1
		list.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 寻找差值最小的地方插入list
			if (!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);
					}
				}
				// 遍历完成后拿到差值min
				int 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月前
|
XML 缓存 Android开发
Android开发,使用kotlin学习多媒体功能(详细)
Android开发,使用kotlin学习多媒体功能(详细)
96 0
|
3月前
|
API Android开发 开发者
Android UI设计: 什么是RecyclerView?为什么它比ListView更好?
Android UI设计: 什么是RecyclerView?为什么它比ListView更好?
31 2
|
3月前
|
安全 Linux Android开发
Android 安全功能
Android 安全功能
37 0
|
4月前
|
XML 前端开发 Java
Android App实战项目之实现手写签名APP功能(附源码,简单易懂 可直接实用)
Android App实战项目之实现手写签名APP功能(附源码,简单易懂 可直接实用)
45 0
|
21天前
|
Android开发
Android自带的DrawerLayout和ActionBarDrawerToggle实现侧滑效果
Android自带的DrawerLayout和ActionBarDrawerToggle实现侧滑效果
10 0
|
4月前
|
传感器 物联网 Android开发
【Android App】物联网中查看手机支持的传感器及实现摇一摇功能-加速度传感器(附源码和演示 超详细)
【Android App】物联网中查看手机支持的传感器及实现摇一摇功能-加速度传感器(附源码和演示 超详细)
62 1
|
4月前
|
XML Java 定位技术
【Android App】定位导航GPS中开启手机定位功能讲解及实战(附源码和演示 超详细)
【Android App】定位导航GPS中开启手机定位功能讲解及实战(附源码和演示 超详细)
112 0
|
4月前
|
XML 前端开发 Java
【Android App】三维处理中三维投影OpenGL功能的讲解及实战(附源码和演示 超详细必看)
【Android App】三维处理中三维投影OpenGL功能的讲解及实战(附源码和演示 超详细必看)
33 1
|
4月前
|
JSON 语音技术 Android开发
【Android App】在线语音识别功能实现(使用云知声平台与WebSocket 超详细 附源码)
【Android App】在线语音识别功能实现(使用云知声平台与WebSocket 超详细 附源码)
34 0
|
4月前
|
JSON Java 语音技术
【Android App】实现在线语音合成功能(使用云知声平台和WebSocket 超详细 附源码)
【Android App】实现在线语音合成功能(使用云知声平台和WebSocket 超详细 附源码)
43 0