高仿ios SwitchButton----(自认为仿的还不错)

这篇文章来介绍这两天的成果,android自带的switchbutton太难看了,于是照着ios的switchbutton做了一个高仿的自定义switchbutton,目前还不是很完美,代码也还比较乱,但还是分享出来,望大家指教!

先看效果图(不知道怎么搞gif图片,就先勉强看下好了,也希望会录制gif图的大神指导下)


高仿ios SwitchButton----(自认为仿的还不错)_第1张图片

首先来讲一下实现的大致流程:首先这个控件继承自view实现,在onlayout或者onWindowFocuschanged方法中去获取height和width,依此确定一些属性的大小,比如滑动的圆圈的半径,确定属性之后再创建一个ShapeDrawable(也就是中间滑动的小球),然后在创建一个小的圆角矩形(仔细观察ios的switchbutton你就会发现里面有个小的圆角矩形)

	@Override
	protected void onLayout(boolean changed, int left, int top, int right,
			int bottom) {
		super.onLayout(changed, left, top, right, bottom);
		
		if(isFirst){
			final int width = getWidth();
			final int height = getHeight();
			radius = Math.min(width, height) * 0.5f;
			spotSize = height - 2 * borderWidth;
			float spotR = spotSize * 0.5f;
			spotMinX = 2 * borderWidth;
			spotMaxX = width - height;
			spotX = toggleOn ? spotMaxX : spotMinX;
			
			// 创建一个圆
			OvalShape circle = new OvalShape();
			// 设置该椭圆的宽、高
			circle.resize(spotSize, spotSize);
			// 将圆包装成Drawable对象
			ShapeDrawable drawable = new ShapeDrawable(circle);
			ss = new SqqShape(drawable);
			ss.setX(spotX);
			ss.setY(centerY);
			Paint paints = drawable.getPaint();
			paints.setColor(spotColor);

			// 创建小圆角矩形
			float radiu = spotR;
			float[] out = {radiu,radiu,radiu,radiu,radiu,radiu,radiu,radiu};
			RoundRectShape recta = new RoundRectShape(out, null, null);
			if(toggleOn){
				recta.resize(0, 0);
			}else{
				recta.resize(width- 4 * borderWidth, height - 2 * borderWidth);
			}
			ShapeDrawable background = new ShapeDrawable(recta);
			bb = new SqqShape(background);
			bb.setX(borderWidth * 2);
			bb.setY(borderWidth);
			bb.setColor(closeColor);
			bb.setWidth(width- 4 * borderWidth);
			bb.setHeight(height - 2 * borderWidth);
			isFirst = false;
		}
	}

这里选择在onlayout方法中设置,因为这里能获取view的高度和宽度,而且因为是继承view的自定义控件,该方法一般不会被调用多次,这里为了确保还是加了判断只在第一次调用加载属性和生成我们需要的几个Shape,这里有个SqqShape类是为了记录shape的属性用的实现如下

public class SqqShape {

	private float x = 0, y = 0;
	private ShapeDrawable shape;
	private int color;
	private float width,height;
	public SqqShape(ShapeDrawable s)
	{
		shape = s;
	}

	public float getX()
	{
		return x;
	}

	public void setX(float x)
	{
		this.x = x;
	}

	public float getY()
	{
		return y;
	}

	public void setY(float y)
	{
		this.y = y;
	}

	public ShapeDrawable getShape()
	{
		return shape;
	}

	public void setShape(ShapeDrawable shape)
	{
		this.shape = shape;
	}

	public int getColor() {
		return color;
	}

	public void setColor(int color) {
		this.color = color;
	}

	
	
	public float getWidth() {
		return width;
	}

	public void setWidth(float width) {
		this.width = width;
	}

	public float getHeight() {
		return height;
	}

	public void setHeight(float height) {
		this.height = height;
	}

	public void setScale(float x){
		Shape s = shape.getShape();
		shape.setBounds(0,(int)(height-height*x)/2,(int)(width*x),(int)(height*x));
		
		Log.d("sqq", "width: "+width+" height:"+height);
		//s.resize(x*width, s.getHeight());
	}
}

实现了上述两步之后,就要开始考虑onTouchEvent事件,首先点击之后抬起,也就是切换的时候要实现切换的动画,做法就是给shape对象绑定动画,动画去改变shape的属性,最后在ondraw中根据shape的属性画出这个shape

	private void SpringAnim(float startX,float endX){
		
		if(mCuranim !=null){
			mCuranim.cancel();
		}
		ObjectAnimator alpAnim = null;
		if (!toggleOn) {
			PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("scale",
					0f, 1f);
			alpAnim= ObjectAnimator.ofPropertyValuesHolder(bb,pvhY);
			alpAnim.addUpdateListener(this);
		}
		
		if(startX!=endX){
			float back = (float) (0.05*(startX-endX));
			/**
			 * 小球滑到目标位置的动画
			 */
			ObjectAnimator sAnim = ObjectAnimator.ofFloat(ss, "x",
					startX, endX-back);
			// 设置sAnim动画的插值方式:加速插值
			sAnim.setInterpolator(new AccelerateInterpolator());
			sAnim.setDuration(120);
			sAnim.addUpdateListener(this);
			
			/**
			 * 小球弹到最终位置
			 */
			ObjectAnimator springAnim1 = ObjectAnimator.ofFloat(ss, "x",
					endX-back,endX);
			springAnim1.setInterpolator(new DecelerateInterpolator());
			springAnim1.addUpdateListener(this);
			
			// 定义一个AnimatorSet1来组合动画
			AnimatorSet animatorSet = new AnimatorSet();
			AnimatorSet animatorSet1 = new AnimatorSet();
			
			
			// 指定在播放springAnim1之前,先播放sAnim动画
			if(alpAnim !=null){
				animatorSet1.play(springAnim1).with(alpAnim);
				animatorSet1.setDuration(160);
				animatorSet.play(sAnim).before(animatorSet1);
			}else{
				animatorSet.play(sAnim).before(springAnim1);
			}
			animatorSet.addListener(new AnimatorListenerAdapter() {
				@Override
				public void onAnimationEnd(Animator animation) {
					mCuranim = null;
					super.onAnimationEnd(animation);
				}
				@Override
				public void onAnimationCancel(Animator animation) {
					mCuranim = null;
					super.onAnimationCancel(animation);
				}
			});
			// 开发播放动画
			animatorSet.start();
			mCuranim = animatorSet;
		}else{
			if(alpAnim!=null)
				alpAnim.start();
		}
	}

	@Override
	protected void onDraw(Canvas canvas) {
		/**
		 * 颜色改变部分
		 */
		if(toggleOn){
			borderColor = onColor;
		}else{
			borderColor = offBorderColor;
		}
		
		Paint paint;
		paint = new Paint(Paint.ANTI_ALIAS_FLAG);
		paint.setStyle(Style.FILL);
		paint.setStrokeCap(Cap.ROUND);
		rect.set(0, 0, getWidth(), getHeight());
		paint.setColor(borderColor);
		canvas.drawRoundRect(rect, radius, radius, paint);
		
		canvas.save();		
		canvas.translate(bb.getX(), bb.getY());
		Paint pain = bb.getShape().getPaint();
		pain.setColor(bb.getColor());
		bb.getShape().draw(canvas);
		canvas.restore();
		
		canvas.save();		
		canvas.translate(ss.getX(), ss.getY());
		ss.getShape().draw(canvas);
		// 恢复Canvas坐标系统
		canvas.restore();
		super.onDraw(canvas);
	}

最后还要在ontouchevent事件中写手指滑动时候的逻辑,小球跟随手机移动,根据移动的多少判定是否切换等等

	@Override
	public boolean onTouchEvent(MotionEvent event) {
		float x = event.getX();
		float y = event.getY();
		float deltaX = Math.abs(x - mFirstDownX);
		float deltaY = Math.abs(y - mFirstDownY);
		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			mFirstDownX = x;
			mFirstDownY = y;
			ChangeCBAnim();
			break;
		case MotionEvent.ACTION_MOVE:
			float offset = x-mFirstDownX;
			float finalx = ss.getX()+offset/8;
			if(finalx<spotMinX){
				finalx = spotMinX;
			}
			if(finalx>spotMaxX){
				finalx = spotMaxX;
			}
			ss.setX(finalx);
			break;
		case MotionEvent.ACTION_UP:
			if (deltaX == 0 || deltaY == 0 || deltaX > mTouchSlop) {
				if (deltaX > mTouchSlop) {
					if (ss.getX() > mTouchSlop) {
						toggleOn = true;
					} else {
						toggleOn = false;
					}
				}
				if(deltaX == 0 || deltaY == 0){
					toggleOn = !toggleOn;
				}
				SpringAnim(ss.getX(), toggleOn ? spotMaxX
						: spotMinX);
				
				if (listener != null) {
					listener.onToggle(toggleOn);
				}
			}

			break;
		default:
			break;
		}
		invalidate();
		return true;
	}

其实最主要的就只是几个属性动画,其他也没什么好讲的,如果对属性动画不是那么了解可以去看我的另一篇文章http://blog.csdn.net/u012806692/article/details/50944606

源码:http://download.csdn.net/detail/u012806692/9453035

你可能感兴趣的:(android,UI)