最近看见很多应用用这种仿IOS的开关控件,最近项目组也有要用到这个控件的需要,实际上最简单的方法是,直接让UI提供两张图片就可以了,分别代表打开和关闭的状态,但是UI实在是不给力,图片迟迟不到,所以,作为一个勤奋好学,自力更生的android程序员来说,好吧,果断自己自定义一个。
其实,要实现这个功能,也不是很难,可以说非常easy。就是通过canvas画一个底层椭圆形的矩形,然后上面画一个开关的小圆圈,底层颜色和小圆圈所处位置根据开关状态不同显示。好了,先上图。
娃
最后效果图就是这样的。代码如下:
package com.lyq.customSwitch;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;
import android.view.View.OnClickListener;
/**
* 仿IOS选择开关
* @author mufeng
*
*/
public class Switch extends View implements OnClickListener{
private Context mContext;
private Paint mPaint;
private float width,height;
private boolean isOpen=true;
private RectF oval;
public Switch(Context context) {
super(context);
init(context);
}
public Switch(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public Switch(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
public Switch(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(context);
}
private void init(Context context) {
mContext=context;
//设置宽高
width=DensityUtils.dip2px(mContext, 80);
height=width/2;
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
setOnClickListener(this);
oval = new RectF(0, 0, width, height);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//根据控件当前状态设置画笔颜色
if(isOpen){
mPaint.setColor(getResources().getColor(R.color.select));
}else{
mPaint.setColor(Color.GRAY);
}
mPaint.setStyle(Paint.Style.FILL);// 充满
//画底层圆角矩形
canvas.drawRoundRect(oval,height/2 , width/4, mPaint);// 第二个参数是x半径,第三个参数是y半径
//画开关圆圈 将画笔颜色设为白色
mPaint.setColor(Color.WHITE);
//根据控件当前状态判断将圆圈画在左边还是右边
if(isOpen){
canvas.drawCircle(width/4*3, height/2, height/2-DensityUtils.dip2px(mContext, 2), mPaint);
}else{
canvas.drawCircle(width/4, height/2, height/2-DensityUtils.dip2px(mContext, 2), mPaint);
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//重新测量控件的大小,主要在控件宽高属性设置为wrap_content时,将控件大小设置为实际大小
setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec));
}
//测量宽度
private int measureWidth(int measureSpec) {
int result = 0;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
if (specMode == MeasureSpec.EXACTLY) {
result = specSize;
} else {
result = (int) DensityUtils.dip2px(mContext, 80) + getPaddingLeft() + getPaddingRight();
if (specMode == MeasureSpec.AT_MOST) {
result = Math.min(result, specSize);
}
}
return result;
}
//测量高度
private int measureHeight(int measureSpec) {
int result = 0;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
if (specMode == MeasureSpec.EXACTLY) {
result = specSize;
} else {
result = (int) DensityUtils.dip2px(mContext, 80)/2 + getPaddingTop() + getPaddingBottom();
if (specMode == MeasureSpec.AT_MOST) {
result = Math.min(result, specSize);
}
}
return result;
}
/**
* 点击切换isOpen值,调用postInvalidate重绘view,可用于异步线程
*/
@Override
public void onClick(View view) {
isOpen=!isOpen;
postInvalidate();
}
/**
* 打开控件开关
*/
public void open(){
if(!isOpen){
isOpen=true;
postInvalidate();
}
}
/**
* 关闭控件开关
*/
public void close(){
if(isOpen){
isOpen=false;
postInvalidate();
}
}
/**
* 获取控件状态
* @return
*/
public boolean isOpen(){
return isOpen;
}
}
代码中各个方法都注释的比较详细,具体的我就不介绍了。最后附上项目 源码。