声明本文是为使用ViewDragHelper技术,直接监听touchEvent来实现开关按钮更方便快捷。ViewDragHelper一般用于ViewGroup中来实现views的拖拽滑动等事件的帮助类。
开关按钮,在iOS和android中均比较常见。主要分为两个状态(on,off)。不多说,先上主布局xml:
<RelativeLayout 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:layout_margin="60dp">
<com.utils.nothingl3.selector_demo.SwitchButton
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerInParent="true">
<ImageView
android:id="@+id/switch_bg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:scaleType="fitCenter"
android:src="@drawable/switch_bg" />
<ImageView
android:id="@+id/slide_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:scaleType="fitCenter"
android:src="@drawable/slide_button" />
</com.utils.nothingl3.selector_demo.SwitchButton>
</RelativeLayout>
此布局中含两张图片,一张背景图片,和滑动的按钮,资源图如下:
我们继续,下面是自定义的代码如下:
public class SwitchButton extends RelativeLayout {
private ImageView mBgImageview;
private ImageView mSlideButton;
private ViewDragHelper mDragger;
private int mRange;
public SwitchButton(Context context) {
this(context, null);
}
public SwitchButton(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SwitchButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mBgImageview= (ImageView) getChildAt(0);
mBgImageview.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if(mSlideButton.getLeft()>0) {
mSlideButton.layout(0, 0, mSlideButton.getWidth(), mSlideButton.getHeight());
}
else{
mSlideButton.layout(mBgImageview.getWidth() - mSlideButton.getWidth(), 0, mBgImageview.getWidth(), mSlideButton.getHeight());
}
}
});
mSlideButton=(ImageView)getChildAt(1);
mDragger= ViewDragHelper.create(this, 1.0f, new ViewDragHelper.Callback() {
@Override
public boolean tryCaptureView(View child, int pointerId) {
return true;
}
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
if(child==mSlideButton) {//滑动按钮
final int leftBound = 0;
final int rightBound = mBgImageview.getWidth() - mSlideButton.getWidth()- leftBound;
final int newLeft = Math.min(Math.max(left, leftBound), rightBound);
return newLeft;
}
return 0;
}
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
mRange=mBgImageview.getWidth()-mSlideButton.getWidth();
if((releasedChild==mSlideButton)) {
if ((xvel >= 0) && (mSlideButton.getLeft() >= mRange / 2)) {//button右滑超过一半,重置位置
mSlideButton.layout(mRange, 0, mRange + mSlideButton.getWidth(), mSlideButton.getHeight());
} else if ((xvel >=0) && (mSlideButton.getLeft() < mRange / 2)) {//button右滑不足,回到起始点
mSlideButton.layout(0, 0, mSlideButton.getWidth(), mSlideButton.getHeight());
} else if ((xvel < 0) && (mSlideButton.getLeft() >= mRange / 2)) {//button左滑超过不足
mSlideButton.layout(mRange, 0, mRange + mSlideButton.getWidth(), mSlideButton.getHeight());
} else if ((xvel < 0) && (mSlideButton.getLeft() < mRange / 2)) {//button左滑回到起始点
mSlideButton.layout(0, 0, mSlideButton.getWidth(), mSlideButton.getHeight());
}
}
}
@Override
public int getViewHorizontalDragRange(View child) {
return mRange=mBgImageview.getWidth() - mSlideButton.getWidth();
}
});
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return mDragger.shouldInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event){
mDragger.processTouchEvent(event);
return true;
}
}
由于我们想实现slideButton滑动来达到切换开关的目的。故我们用简单的相对布局,将slidebutton放于上层即达到覆盖背景目的。
首先,几个构造函数不再赘述。
我们看ViewDragHelper的初始化:
ViewDragHelper.create(this, 1.0f, new ViewDragHelper.Callback())
三个参数:第一个是作用的ViewGroup,第二个是敏感度(这里取默认值),第三个为callback参数。
其次我们看最后两个函数:
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return mDragger.shouldInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event){
mDragger.processTouchEvent(event);
return true;
}
即将事件拦截和处理交个ViewDragHelper类Calback具体执行。
下面详细讲述下Callback的几个需要复写的方法。
public boolean tryCaptureView(View child, int pointerId) {
return true;
}
该函数表示是否捕捉该子view(drag),这里返回false的话,不对该View的事件进出处理。
public int clampViewPositionHorizontal(View child, int left, int dx)
该函数是view水平运动(与之相对有Vertical方向)时调用,left即为此时运动的值。由于mSlideButton滑动按钮运动范围只有【0,背景图宽度】,故须进行调整,具体调整如
上述代码。最后松手释放时调用
public void onViewReleased(View releasedChild, float xvel, float yvel)
此代码分别调用mSlideButton.layout()对按钮位置进行设置。
参考:1. http://blog.youkuaiyun.com/lmj623565791/article/details/46858663
2. http://www.apkbus.com/android-245699-1-1.html?_dsign=b8a273ef