现在很多Android项目上都使用到类似IOS上的风格,比如滑动开关、毛玻璃效果等等,在Android4.0上我们可以使用Switch,但是在4.0以下是没有这样的控件的,所以我们可以自定义这样的控件的风格来进行适配。
先来看下图片的效果吧:
自定义该控件大致需要注意一下几个方面:
1、动态的控制mSlideLeft的值
2、将开点击事件和滑动事件进行隔离
3、滑动的时候注意slideButton不要离开屏幕
下面就直接看自定义View的代码吧:
package com.jun.slidebutton;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
/**
* Created by zengy on 2015/10/26.
*/
public class MySlideButton extends View implements View.OnClickListener {
/**
* 按钮图片
*/
private Bitmap mSlideButton;
/**
* 背景图片
*/
private Bitmap mBackgound;
/**
* 画笔
*/
private Paint mPaint;
/**
* 按钮图片距离左边界的距离
*/
private int mSlideLeft = 0;
public MySlideButton(Context context, AttributeSet attrs) {
super(context, attrs);
/**
* 获得自定义属性的值
*/
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MySlideButton);
//总多少个自定义属性
int count = typedArray.getIndexCount();
for (int i = 0; i < count; i++) {
//获得每个属性的ID
int itemId = typedArray.getIndex(i);
switch (itemId){
case R.styleable.MySlideButton_my_background:
int backgroundId = typedArray.getResourceId(itemId,-1);
if(backgroundId == -1){
throw new RuntimeException("请设置背景图片");
}
mBackgound = BitmapFactory.decodeResource(getResources(), backgroundId);
break;
case R.styleable.MySlideButton_my_slide_button:
int slideButtonId = typedArray.getResourceId(itemId,-1);
if(slideButtonId == -1){
throw new RuntimeException("请设置遮挡图片");
}
mSlideButton = BitmapFactory.decodeResource(getResources(), slideButtonId);
break;
case R.styleable.MySlideButton_curr_state:
isOpen = typedArray.getBoolean(itemId,false);
break;
}
}
// mSlideButton = BitmapFactory.decodeResource(getResources(), R.mipmap.slidebutton);
// mBackgound = BitmapFactory.decodeResource(getResources(), R.mipmap.background);
mPaint = new Paint();
mPaint.setAntiAlias(true);
//设置点击事件的监听
setOnClickListener(this);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
/**
* 设置当前view的大小 width :view的宽度 height :view的高度 (单位:像素)
*/
setMeasuredDimension(mBackgound.getWidth(), mBackgound.getHeight());
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap(mBackgound, 0, 0, mPaint);
canvas.drawBitmap(mSlideButton, mSlideLeft, 0, mPaint);
}
/**
* 判断是否发生拖动, 如果拖动了,就不再响应 onclick 事件
*/
private boolean isDrag = false;
/**
* down 事件时的x值
*/
private int startX;
/**
* touch 事件的上一个x值
*/
private int endX;
@Override
public boolean onTouchEvent(MotionEvent event) {
super.onTouchEvent(event);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: //手指按下屏幕
startX = endX = (int) event.getRawX();
isDrag = false;
break;
case MotionEvent.ACTION_MOVE: //手指在屏幕上移动
int nowX = (int) event.getRawX();
//判断是否已经拖动
if (Math.abs(nowX - startX) > 5) {
isDrag = true;//是拖动状态
}
//手指在屏幕上移动的距离
int dis = nowX - endX;
//将本次位置设置给endX
endX = nowX;
//根据手指一定的距离来设置mSlideLeft的值
mSlideLeft = mSlideLeft + dis;
break;
case MotionEvent.ACTION_UP: //手指离开屏幕
//如果发生了滑动,就执行移动事件,否则就执行点击事件
if (isDrag) {
int maxLeft = mBackgound.getWidth() - mSlideButton.getWidth();
if (mSlideLeft > maxLeft / 2) {
isOpen = true;//如果mSlideLeft比距离最左边的最大值还大,说明是开状态
} else {
isOpen = false;
}
//刷新当前状态
flushState();
}
break;
}
//需要一直刷新当前View的显示效果
flushView();
return true;
}
/**
* 当前的状态,false表示关,ture表示开
*/
private boolean isOpen = false;
@Override
public void onClick(View v) {
// 如果没有拖动,才执行改变状态的动作
if (!isDrag) {
isOpen = !isOpen;
//刷新当前的状态
flushState();
}
}
/**
* 刷新当前的状态
*/
private void flushState() {
if (isOpen) {
//是开的状态,则mSlideLeft为背景图片的宽度减去按钮的宽度
mSlideLeft = mBackgound.getWidth() - mSlideButton.getWidth();
} else {
//关的状态,则mSlideLeft依然是0
mSlideLeft = 0;
}
/*
* 刷新当前视图 导致 执行onDraw执行
*/
invalidate();
}
/**
* 刷新当前视图(View的显示效果)
*/
public void flushView() {
/*
* 对 slideBtn_left 的值进行判断 ,确保其在合理的位置 即 0<=slideBtn_left <= maxLeft
* 如果不进行该值的判断,那么mSlideButton将会划出屏幕,感兴趣的可以将这几行注释看效果
*/
int maxLeft = mBackgound.getWidth() - mSlideButton.getWidth();
// 确保 slideBtn_left >= 0
mSlideLeft = mSlideLeft <= 0 ? 0 : mSlideLeft;
// 确保 slideBtn_left <=maxLeft
mSlideLeft = mSlideLeft >= maxLeft ? maxLeft : mSlideLeft;
/*
* 刷新当前视图 导致 执行onDraw执行
*/
invalidate();
}
}
上面的代码基本上每行都有注释,这里就不过多说了,自定义好了控件之后,我们就可以再布局文件中使用了:
activity_main.xml文件是:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:feng="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<com.jun.slidebutton.MySlideButton
android:id="@+id/mySlideButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
feng:my_background="@mipmap/background"
feng:my_slide_button="@mipmap/slidebutton"
feng:curr_state="true"/>
</LinearLayout >
使用自定义布局文件的时候需要指定全名哦。
这里使用了自定义属性来灵活的设置图片,如果不熟悉自定义属性的朋友请自行脑补哦。