功能描述
实现一个左滑删除的功能,效果如下:
功能分析
ViewDragHelper:
- 用来分析touch事件的工具类
使用步骤
获取实例
ViewDragHelper helper =ViewDragHelper.create(ViewGroup, CallBack);
touch分析和监听
@Override public boolean onTouchEvent(MotionEvent event) { mHelper.processTouchEvent(event); return true; }
实现自己的callBack
touch:
- down
- tryCaptureView()
- move
- clampViewPositionHorizontal:水平移动的回调
- up
- onViewReleased()
- down
代码实现
- content.xml
<?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="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/item_tv_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#33000000"
android:gravity="center"
android:textColor="@android:color/white"
android:textSize="25sp"
android:text="内容"/>
</LinearLayout>
- delete.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="120dp"
android:layout_height="80dp"
android:orientation="vertical">
<TextView
android:id="@+id/item_tv_delete"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ffff0000"
android:gravity="center"
android:text="删除"
android:textColor="@android:color/white"
android:textSize="25sp"/>
</LinearLayout>
- item.xml
<?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="match_parent"
android:orientation="vertical">
<com.cdw.sweep.SweepView
android:id="@+id/item_sv"
android:layout_width="match_parent"
android:layout_height="80dp">
<!--内容区-->
<include layout="@layout/content"/>
<!--删除区-->
<include layout="@layout/delete"/>
</com.cdw.sweep.SweepView>
</LinearLayout>
- activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.cdw.sweep.MainActivity">
<ListView
android:id="@+id/lv"
android:layout_width="match_parent"
android:layout_height="match_parent">
</ListView>
</LinearLayout>
- SweepView.java
package com.cdw.sweep;
import android.content.Context;
import android.support.v4.view.ViewCompat;
import android.support.v4.widget.ViewDragHelper;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
/**
* Created by dongwei on 2016/8/3.
*/
public class SweepView extends ViewGroup {
private View mContentView;
private View mDeleteView;
private int mDeleteWidth;
private ViewDragHelper mHelper;
private boolean isOpened;
private OnSweepListener mListener;
public SweepView(Context context) {
this(context, null);
}
public SweepView(Context context, AttributeSet attrs) {
super(context, attrs);
mHelper = ViewDragHelper.create(this, new MyCallBack());
}
@Override
protected void onFinishInflate() {
mContentView = getChildAt(0);
mDeleteView = getChildAt(1);
LayoutParams params = mDeleteView.getLayoutParams();
mDeleteWidth = params.width;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//测量内容
mContentView.measure(widthMeasureSpec, heightMeasureSpec);
//测量删除部分
int deleteWidthMeasureSpec = MeasureSpec.makeMeasureSpec(mDeleteWidth, MeasureSpec.EXACTLY);
mDeleteView.measure(deleteWidthMeasureSpec, heightMeasureSpec);
//确定自己的高度
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(width, height);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
//布局内容区域
mContentView.layout(0, 0, mContentView.getMeasuredWidth(), mContentView.getMeasuredHeight
());
//布局删除部分
mDeleteView.layout(mContentView.getMeasuredWidth(), 0, mContentView.getMeasuredWidth() +
mDeleteWidth, mDeleteView.getMeasuredHeight());
}
@Override
public boolean onTouchEvent(MotionEvent event) {
mHelper.processTouchEvent(event);
return true;
}
class MyCallBack extends ViewDragHelper.Callback {
/**
* 是否分析view的touch
*
* @param child 触摸的view
* @param pointerId touch的id
* @return
*/
@Override
public boolean tryCaptureView(View child, int pointerId) {
return child == mContentView || child == mDeleteView;
}
/**
* 当touch移动后的回调
*
* @param child 谁移动了
* @param left child的左侧的边距
* @param dx 增量的x
* @return 确定要移动多少
*/
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
if (child == mContentView) {
if (left < 0 && -left > mDeleteWidth) {
return -mDeleteWidth;
} else if (left > 0) {
return 0;
}
} else if (child == mDeleteView) {
int measuredWidth = mContentView.getMeasuredWidth();
if (left < measuredWidth - mDeleteWidth) {
return measuredWidth - mDeleteWidth;
} else if (left > measuredWidth) {
return measuredWidth;
}
}
return left;
}
/**
* 当控件位置移动时的回调
*
* @param changedView 哪个view移动了
* @param left view的左上角的坐标
* @param top view的左上角的坐标
* @param dx x方向移动的增量
* @param dy y方向移动的增量
*/
@Override
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
invalidate();
int contentWidth = mContentView.getMeasuredWidth();
int contentHeight = mContentView.getMeasuredHeight();
int deleteHeight = mDeleteView.getMeasuredHeight();
if (changedView == mContentView) {
//如果移动的是内容的view
mDeleteView.layout(contentWidth + left, 0, contentWidth + left + mDeleteWidth,
deleteHeight);
} else if (changedView == mDeleteView) {
//int deleteWidth = mDeleteView.
mContentView.layout(left - contentWidth, 0, left, contentHeight);
}
}
/**
* up时的回调
*
* @param releasedChild 松开了哪个view
* @param xvel x方向的速率
* @param yvel y方向的速率
*/
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
int left = mContentView.getLeft();
if (-left < mDeleteWidth / 2f) {
//关闭
close();
} else {
//打开
open();
}
//invalidate();
}
}
public void open() {
isOpened = true;
if (mListener != null) {
mListener.onSweepChanged(SweepView.this, isOpened);
}
int contentWidth = mContentView.getMeasuredWidth();
//布局内容区域
// mContentView.layout(-mDeleteWidth, 0, contentWidth - mDeleteWidth, mContentView
// .getMeasuredHeight());
// //布局删除部分
// mDeleteView.layout(contentWidth - mDeleteWidth, 0, contentWidth, mDeleteView
// .getMeasuredHeight());
//数据的模拟
mHelper.smoothSlideViewTo(mContentView, -mDeleteWidth, 0);
mHelper.smoothSlideViewTo(mDeleteView, contentWidth - mDeleteWidth, 0);
ViewCompat.postInvalidateOnAnimation(SweepView.this);
}
public void close() {
isOpened = false;
if (mListener != null) {
mListener.onSweepChanged(SweepView.this, isOpened);
}
//布局内容区域
// mContentView.layout(0, 0, mContentView.getMeasuredWidth(), mContentView
// .getMeasuredHeight());
// //布局删除部分
// mDeleteView.layout(mContentView.getMeasuredWidth(), 0, mContentView
// .getMeasuredWidth() + mDeleteWidth, mDeleteView.getMeasuredHeight());
//数据的模拟
mHelper.smoothSlideViewTo(mContentView, 0, 0);
mHelper.smoothSlideViewTo(mDeleteView, mContentView.getMeasuredWidth(), 0);
ViewCompat.postInvalidateOnAnimation(SweepView.this);
}
@Override
public void computeScroll() {
if (mHelper.continueSettling(true)) {
invalidate();
}
}
public void setOnSweepListener(OnSweepListener listener) {
this.mListener = listener;
}
public interface OnSweepListener {
void onSweepChanged(SweepView view, boolean isOpened);
}
}
- mainActivity.java
package com.cdw.sweep;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
public class MainActivity extends Activity {
private ListView mListView;
private List<String> mDatas;
private List<SweepView> mOpenedViews = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mListView = (ListView) findViewById(R.id.lv);
//模拟数据
mDatas = new ArrayList<>();
for (int i = 0; i < 200; i++) {
mDatas.add("第" + i + "条数据");
}
//给listView设置数据
mListView.setAdapter(new MyAdapter());
}
class MyAdapter extends BaseAdapter {
@Override
public int getCount() {
if (mDatas != null) {
return mDatas.size();
}
return 0;
}
@Override
public Object getItem(int position) {
if (mDatas != null) {
return mDatas.get(position);
}
return null;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
if (convertView == null) {
//没有复用
convertView = View.inflate(MainActivity.this, R.layout.item, null);
//初始化Holder
holder = new ViewHolder();
//设置标记
convertView.setTag(holder);
//view的初始化
holder.sv = (SweepView) convertView.findViewById(R.id.item_sv);
holder.tvContent = (TextView) convertView.findViewById(R.id.item_tv_content);
holder.tvDelete = (TextView) convertView.findViewById(R.id.item_tv_delete);
holder.sv.setOnSweepListener(new SweepView.OnSweepListener() {
@Override
public void onSweepChanged(SweepView view, boolean isOpened) {
if (isOpened) {
//打开,记录下来
if (!mOpenedViews.contains(view)) {
mOpenedViews.add(view);
}
} else {
//移除
mOpenedViews.remove(view);
}
}
});
} else {
//有复用
holder = (ViewHolder) convertView.getTag();
}
//数据的加载
final String data = mDatas.get(position);
holder.tvContent.setText(data);
holder.tvDelete.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mDatas.remove(data);
//关闭所有打开的view
closeAll();
notifyDataSetChanged();
}
});
return convertView;
}
}
public void closeAll() {
ListIterator<SweepView> iterator = mOpenedViews.listIterator();
while (iterator.hasNext()) {
SweepView view = iterator.next();
view.close();
}
}
class ViewHolder {
SweepView sv;
TextView tvContent;
TextView tvDelete;
}
}
项目链接:https://github.com/ChenDongWei/Android/tree/0b4bc67b4381082e918ab5c04771c4324a92d430/sweep