CSDN博客已经不在使用,转载请注明出处:http://my.oschina.net/witype716/blog/412738
其实平时研究的东西挺多的,只是没有找时间将他们记录下来,这几天过端午节时间很充裕,所以没事来做了一个switch button。当让如果只是纯写一个这样的自定义View并没有什么难度,只是要搞明白这其中的原理,下面附一篇博文,讲的是android自定义view的原理,深入浅出,有一定基础的同学可以去看看,有4个部分,看完了在理解本文应该就没什么问题了。
Android LayoutInflater原理分析,带你一步步深入了解View(一)
先附图:
实现是效果就是在拨动开关是时候,有一个加长的趋势,左边为false,右边为true,用户通过拖动按钮,拖动过程当中,拖动的按钮会变成一个圆角矩形,在背景的圆角矩形中移动,当移动到要靠近用户手势的趋势位置的时候,会自动靠拢,并且背景颜色会更具当前的true和false渐变的改变颜色。
下面先看一下设计原理
图中
1:true状态的中心点,false状态是对称的。
2:为一个圆角矩形,圆角度数为height/2,也可以根据自己的喜好设计。
3:黑框为中心圆形按钮和外圆角矩形的间隔,可以自己调整,看着舒服就行。
在贴代码之前吧程序中几点需要注意的贴出来下。
//switch按钮状态发生改变的借口 public interface onCheckChangeListener{ void onCheckChanged(View view,boolean isChecked); }
// 设置监听器 public void setOnCheckChangeListener(onCheckChangeListener l){ if(null!=l) this.checkChange = l; }
//覆盖父类的方法,这样空间的大小就可以锁定了,不然会填充所有的父视图 @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // TODO Auto-generated method stub int measuredWidth = MeasureSpec.getSize((int) mRect.width()); int measuredHeight = MeasureSpec.getSize((int) mRect.height()); setMeasuredDimension(measuredWidth, measuredHeight); }
package open.witype.slidingSwitch; 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.util.Log; import android.view.MotionEvent; import android.view.View; /** * * @author WiType716 * */ public class SlidingButton extends View { public SlidingButton(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); // TODO Auto-generated constructor stub init(); } public SlidingButton(Context context, AttributeSet attrs) { super(context, attrs); // TODO Auto-generated constructor stub // cPaint = new Paint(Paint.ANTI_ALIAS_FLAG); // cPaint.setStyle(Paint.Style.FILL); // cPaint.setColor(Color.rgb(255, 255, 255)); // // tPaint = new Paint(Paint.ANTI_ALIAS_FLAG); // tPaint.setStyle(Paint.Style.FILL); // tPaint.setColor(Color.rgb(98, 195, 208)); // // fPaint = new Paint(Paint.ANTI_ALIAS_FLAG); // fPaint.setStyle(Paint.Style.FILL); // fPaint.setColor(Color.rgb(220, 220, 220)); // TypedArray ta = context.obtainStyledAttributes(attrs,R.styleable.sliding_switch); // c_color = ta.getColor(R.styleable.sliding_switch_slideColor,Color.rgb(255, 255, 255)); // t_color = ta.getColor(R.styleable.sliding_switch_backgroundOn,Color.rgb(98, 195, 208)); // f_color = ta.getColor(R.styleable.sliding_switch_backgroundOff, Color.rgb(220, 220, 220)); init(); } public SlidingButton(Context context) { super(context); // TODO Auto-generated constructor stub init(); } private int width = 128; private int height = width / 2; private int oofset = height; private RectF mRect = new RectF(0, 0, width, height); private Paint cPaint; private Paint tPaint; private Paint fPaint; private int c_color; private int t_color; private int f_color; // 间隔 private int space = 2; // 半径(等于圆角矩形的圆角度数 - space) private float circelRadius = mRect.height() / 2 - space; // 左边的中心X点(等于矩形圆角度数+space) private float leftCenter = mRect.height() / 2 + space; // 右边的中心X点(等于矩形的宽 - 圆的半径 - space) private float rightCneter = mRect.width() - (circelRadius + space); // Y轴的中心,始终不变 private float lrCenterY = mRect.height() / 2; // 标识现在中间圆圈的状态LEFT = false,RIGHT = true; private STATE state = STATE.LEFT; // 是否被选中 private boolean isChoise = false; // 点击后的时间监听 private onCheckChangeListener checkChange; public void setOnCheckChangeListener(onCheckChangeListener l) { if (null != l) this.checkChange = l; } public interface onCheckChangeListener { void onCheckChanged(View view, boolean isChecked); } enum STATE { LEFT, RIGHT, MOVE } public boolean isChecked() { // TODO Auto-generated method stub return isChoise; } public void setChecked(boolean checked) { // TODO Auto-generated method stub this.isChoise = checked; switch (state) { case LEFT: if (checked) { state = STATE.RIGHT; invalidate(); } break; case RIGHT: if (!checked) { state = STATE.LEFT; invalidate(); } break; default: break; } } private void init() { c_color = Color.rgb(255, 255, 255); t_color = Color.rgb(98, 195, 208); f_color = Color.rgb(220, 220, 220); // 三个画笔分别是,按钮,背景true,和背景false cPaint = new Paint(Paint.ANTI_ALIAS_FLAG); cPaint.setStyle(Paint.Style.FILL); cPaint.setColor(c_color); tPaint = new Paint(Paint.ANTI_ALIAS_FLAG); tPaint.setStyle(Paint.Style.FILL); tPaint.setColor(t_color); fPaint = new Paint(Paint.ANTI_ALIAS_FLAG); fPaint.setStyle(Paint.Style.FILL); fPaint.setColor(f_color); } @Override protected void onDraw(Canvas canvas) { // TODO Auto-generated method stub super.onDraw(canvas); switch (state) { case LEFT: fPaint.setColor(Color.rgb(220, 220, 220)); canvas.drawRoundRect(mRect, mRect.height() / 2, mRect.height() / 2, fPaint); canvas.drawCircle(leftCenter, lrCenterY, circelRadius, cPaint); break; case RIGHT: tPaint.setColor(Color.rgb(98, 195, 208)); canvas.drawRoundRect(mRect, mRect.height() / 2, mRect.height() / 2, tPaint); canvas.drawCircle(rightCneter, lrCenterY, circelRadius, cPaint); break; case MOVE: // 处理当前按下的点,始终保持X轴在left和right之间 if (nowX < leftCenter) { nowX = leftCenter; } else if (nowX > rightCneter) { nowX = rightCneter; } // 绘制背景的渐变,其实颜色的改变就是设置true背景的透明度, float x = (nowX - circelRadius - space) / (rightCneter - leftCenter + space); canvas.drawRoundRect(mRect, mRect.height() / 2, mRect.height() / 2, fPaint); tPaint.setColor(Color.argb((int) (255 * x), 98, 195, 208)); RectF truebg = new RectF(mRect); canvas.drawRoundRect(truebg, mRect.height() / 2, mRect.height() / 2, tPaint); RectF temp = new RectF(); float right = 0; float left = space; /* * 检查按钮是否越过了中线 如果越过了中线, 向右移动:那么按钮左边应该收缩,右边应该向背景的右边靠拢 * 向左移动:反之,右边收缩,左边向向背景左边靠拢 */ // 越过了中线 toRight if (nowX > mRect.width() / 2) { // 保证圆角按钮的右边开始收缩,按钮充椭圆向圆形变形 right = (nowX + circelRadius) + oofset * (1 - x); // 按钮左边趋势不变 } // toLeft else { right = (nowX + circelRadius) + oofset * x; } if (right > mRect.width() / 3) { left = nowX - circelRadius - oofset * (1 - x); } if (right > mRect.width() - space) right = mRect.width() - space; if (left < space) left = space; if (nowX <= leftCenter) { if (right - left > circelRadius * 2) { right = right - ((right - left) - circelRadius * 2) / 2; left = left + ((right - left) - circelRadius * 2) / 2; } } // 按钮是上和下可以不用改变,始终是space是距离 temp.set(left, space, right, mRect.height() - space); canvas.drawRoundRect(temp, temp.height() / 2, temp.height() / 2, cPaint); // circelMehtonB(canvas); break; } } public void circelMehtonB(Canvas canvas) { RectF rect = new RectF(); float left = nowX - circelRadius - space; float right = nowX + circelRadius; float length = mRect.width() / 2; float x = mRect.width() / 2 - nowX / length; float temp = length - Math.abs((mRect.width() / 2 - nowX)); Log.d("switch", length - Math.abs((mRect.width() / 2 - nowX)) + " / " + length); left = (float) (left - temp * 0.1); right = (float) (right + temp * 0.1); if (right >= mRect.width() / 10 * 9) { right = mRect.width() - space; } if (left <= mRect.width() / 10) { left = space; } rect.set(left, space, right, mRect.height() - space); canvas.drawRoundRect(rect, rect.height() / 2, rect.height() / 2, cPaint); } /** * * @param canvas * @param x */ public void circelMethonA(Canvas canvas, float x) { // 当中间圆圈移动后,绘制椭圆 } private float oldX; private float nowX; @Override public boolean onTouchEvent(MotionEvent event) { // TODO Auto-generated method stub switch (event.getAction()) { case MotionEvent.ACTION_DOWN: oldX = event.getX(); break; case MotionEvent.ACTION_MOVE: state = STATE.MOVE; nowX = event.getX(); break; case MotionEvent.ACTION_UP: if (event.getX() > mRect.width() / 2) { state = STATE.RIGHT; isChoise = true; } else { state = STATE.LEFT; isChoise = false; } if (null != checkChange) checkChange.onCheckChanged(this, isChoise); break; } invalidate(); return true; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // TODO Auto-generated method stub int measuredWidth = MeasureSpec.getSize((int) mRect.width()); int measuredHeight = MeasureSpec.getSize((int) mRect.height()); setMeasuredDimension(measuredWidth, measuredHeight); } }
后续有时间了在改下,改的更智能化和个性化,现在就这样子吧,