自定义侧滑删除菜单
话不多说。先放效果图,源码仅供参考,出现bug,请留言告知
*- 继承FramLayout
- 构造方法中调用ViewDragHelper
- 转发事件,处理事件
- 重写ViewDragHelper中的方法
- 回调接口
- 导入到ListView中*
1.首先构造函数串联起来,并在三个属性的构造中调用ViewDragHelper帮助类。以及初始化布局
public SlideClean(Context context) {
this(context, null);
}
public SlideClean(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SlideClean(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mViewDragHelper = ViewDragHelper.create(this, mcallback);
}
/**
* 初始化导入Inflate完成
*/
@Override
protected void onFinishInflate() {
super.onFinishInflate();
rightView = getChildAt(0);
mainView = getChildAt(1);
mRangeView = rightView.findViewById(R.id.tv);
}
/**
* get measue size
*
* @param w
* @param h
* @param oldw
* @param oldh
*/
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
//获取测量的数值
mainHeight = mainView.getMeasuredHeight();
mainWidht = mainView.getMeasuredWidth();
meaWidth = mRangeView.getMeasuredWidth();
}
/**
* override Layout
*
* @param changed
* @param left
* @param top
* @param right
* @param bottom
*/
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
setLayout();
}
/**
* 根据矩形设定布局
*/
private void setLayout() {
Rect mainRect = getMainRect();
mainView.layout(mainRect.left, mainRect.top, mainRect.right, mainRect.bottom);
Rect rightRect = getRightRect(mainRect);
rightView.layout(rightRect.left, rightRect.top, rightRect.right, rightRect.bottom);
}
/**
* 根据主背景矩形获取右背景矩形
*
* @param mainRect
* @return
*/
private Rect getRightRect(Rect mainRect) {
return new Rect(mainRect.right, mainRect.top, mainRect.right + mainWidht, mainRect.bottom);
}
/**
* 获取主背景矩形
*
* @return
*/
private Rect getMainRect() {
Rect mainRect = new Rect(0, 0, mainWidht, mainHeight);
return mainRect;
}
2.需要对事件进行处理,避免拦截,同时能让显示的控件滑动
如果有的朋友发现事件被拦截,或者无法实现效果。可以像我一样简单的在事件中打一下Log,记录事件发生的时刻,已经发生的逻辑关系
/**
* 转发事件
*
* @param ev
* @return
*/
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
System.out.println("ssint");
return mViewDragHelper.shouldInterceptTouchEvent(ev);
}
/**
* 请求拦截
*
* @param event
* @return
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction()== MotionEvent.ACTION_UP) {
System.out.println("sstouup");
}if (event.getAction()== MotionEvent.ACTION_DOWN) {
System.out.println("sstoudown");
}
try {
//抓取多点触摸的异常
mViewDragHelper.processTouchEvent(event);
} catch (Exception e) {
e.printStackTrace();
}
return true;
}
3.事件ViewDragHelper的CallBack回调函数
这里用到的几个方法,已经在代码中备注使用原因和原理
针对callBack回调中的参数,对滑动的状态进行判断。
private ViewDragHelper.Callback mcallback = new ViewDragHelper.Callback() {
@Override
public boolean tryCaptureView(View child, int pointerId) {
return true;
}
/**
* 大于0,拦截可以
* @param child
* @return
*/
@Override
public int getViewHorizontalDragRange(View child) {
return mainWidht;
}
//重写之后测能滑动
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
finalState = 2;//每次开始滑动之前设定为2;
if (child == mainView) {
//点击的是主控件,left是这个控件的左边距
if (left > 0) {
left = 0;
} else if (left < -meaWidth) {
left = -meaWidth;
}
} else if (child == rightView) {
//点击的是右控件,left是这个控件的左边距
if (left > mainWidht) {
left = mainWidht;
} else if (left < mainWidht - meaWidth) {
left = mainWidht - meaWidth;
}
}
// System.out.println(left);
return left;
}
/**
* 条目被状态被改变
* @param changedView
* @param left
* @param top
* @param dx
* @param dy
*/
@Override
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
super.onViewPositionChanged(changedView, left, top, dx, dy);
//System.out.println(dx + "---" + left);
if (changedView == mainView) {
//将mainView的偏移量传递给rightview
rightView.offsetLeftAndRight(dx);
} else if (changedView == rightView) {
//将rightview的偏移量传递给mainView
mainView.offsetLeftAndRight(dx);
}
//requestLayout();//请求重新布局
dispatchDragEvent();
invalidate();
}
/**
* 滑动事件被释放
* @param releasedChild
* @param xvel
* @param yvel
*/
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
super.onViewReleased(releasedChild, xvel, yvel);
// System.out.println(xvel);
int releaseLeft = mainView.getLeft();
if (releaseLeft < -30) {
//平滑开启
mViewDragHelper.smoothSlideViewTo(mainView, -meaWidth, 0);
// System.out.println(mainView.getLeft()+"-----------------shifangdian");
finalState = 1;
if(releaseLeft==-60 &&moOnCleanState!=null){//判断特殊情况
moOnCleanState.onOpen(true);
}
} else if (releaseLeft >= -meaWidth / 2) {
//关闭
mViewDragHelper.smoothSlideViewTo(mainView, 0, 0);
finalState = 0;
if(releaseLeft==0 &&moOnCleanState!=null){//判断特殊情况
moOnCleanState.onColse(false);
}
}
}
};
4.同时我这里添加了平滑动画。实现的平滑滑动。避免突兀的滑动。
/**
* 更新状态
*/
private void dispatchDragEvent() {
int currentLeft = mainView.getLeft();
// System.out.println(currentLeft+"--------"+finalState);
if(moOnCleanState != null){//如果设定了监听
if(finalState==2){
moOnCleanState.onDrag();
}else if(finalState==1 && currentLeft==-60){
moOnCleanState.onOpen(true);
}else if(finalState==0&& currentLeft==0){
moOnCleanState.onColse(false);
}
}
}
/**
* 是否完成动画
*/
@Override
public void computeScroll() {
super.computeScroll();
if (mViewDragHelper.continueSettling(true)) {
//如果没有画完。继续作画
ViewCompat.postInvalidateOnAnimation(this);
}
invalidate();
}
5.回调接口,以及回调方式
* 删除状态监听
*/
public void setOnCleanStateListener(onCleanState listener) {
moOnCleanState = listener;
}
/**
* 删除状态监听回调
*/
public interface onCleanState {
void onOpen(boolean flag);
void onColse(boolean flag);
void onDrag();
}
6.简单的布局,
6.1Activity中的布局
<?xml version="1.0" encoding="utf-8"?>
<com.oblivion.ui.SlideClean xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="60dp"
android:id="@+id/sc_clean"
tools:context="com.oblivion.slideclean.MainActivity">
<include layout="@layout/right" />
<include layout="@layout/main" />
</com.oblivion.ui.SlideClean>
6.2显示的主布局
<?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="60dp"
android:background="#ffffff"
android:orientation="horizontal">
<ImageView
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_gravity="center_vertical"
android:layout_marginLeft="10dp"
android:src="@drawable/head" />
<TextView
android:id="@+id/tv_name"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:paddingLeft="10dp"
android:text="宋江"
android:textColor="#000000"
android:textSize="20sp" />
</LinearLayout>
6.3要滑出的菜单的布局
<?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="60dp"
android:orientation="horizontal">
<TextView
android:id="@+id/tv"
android:layout_width="60dp"
android:layout_height="60dp"
android:background="#ff0000"
android:gravity="center"
android:text="删除"
android:textColor="#ffffff" />
</LinearLayout>
7.主Activity中调用
package com.oblivion.slideclean;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.ListView;
import com.oblivion.ui.SlideClean;
import com.oblivion.utils.ToastUtils;
public class MainActivity extends AppCompatActivity {
private ListView lv_test;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.test);
lv_test = (ListView) findViewById(R.id.lv_test);
lv_test.setAdapter(new SlideCleanAdapter(lv_test));
}
}
8.ListView适配器中回调
package com.oblivion.slideclean;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.TextView;
import com.oblivion.ui.SlideClean;
import com.oblivion.utils.Cheeses;
import com.oblivion.utils.ToastUtils;
/**
* Created by oblivion on 2016/10/29.
*/
public class SlideCleanAdapter extends BaseAdapter {
/**
* 条目状态为打开时,默认为false
*/
private boolean isClean = false;
/**
* 根据回调得到的boolean类型值,得到是否打开,默认为false
*/
private boolean currentState = false;
private ListView v;
/**
* 构造函数携带自身View对象过来
*
* @param v
*/
public SlideCleanAdapter(ListView v) {
this.v = v;
}
@Override
public int getCount() {
return Cheeses.NAMES.length;
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return 0;
}
@Override
public View getView(int position, View convertView, final ViewGroup parent) {
final SlideClean view;
if (convertView == null) {
view = (SlideClean) View.inflate(parent.getContext(), R.layout.activity_main, null);
} else {
view = (SlideClean) convertView;
}
TextView tv_name = (TextView) view.findViewById(R.id.tv_name);
tv_name.setText(Cheeses.NAMES[position]);
TextView tv = (TextView) view.findViewById(R.id.tv);
tv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (isClean) {//如果为打开状态
ToastUtils.setToast(parent.getContext(), "清理");
}
}
});
//设定每个条目的监听
view.setOnCleanStateListener(new SlideClean.onCleanState() {
@Override
public void onOpen(boolean flag) {//滑块为打开状态
ToastUtils.setToast(parent.getContext(), "open");
isClean = true;
currentState = flag;
view.setOpenState(currentState);
}
@Override
public void onColse(boolean flag) {//滑块关闭状态
ToastUtils.setToast(parent.getContext(), "close");
isClean = false;
currentState = flag;
}
@Override
public void onDrag() {//滑动状态
ToastUtils.setToast(parent.getContext(), "drag");
}
});
v.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
ToastUtils.setToast(v.getContext(),"wocao");
}
});
return view;
}
}