Android自定义View实现开关按钮
来源:互联网 发布:软件测试 设计方法 编辑:程序博客网 时间:2024/05/14 12:49
UI图的效果如下
实现的gif效果图如下,后续做些细节上的优化即可
实现思路,通过自定义继承View,通过Canvas,Paint等api绘制出来
分析思路:
以UI 3倍图作为标准,宽为156px,高为90px,则宽高比为 156 / 90
高度在dimens已经定义好,3倍图的UI,则对应于30dp
则宽度width = height * 156 / 90;
这里的circleThumb 的移动范围,我们发现是这个circleThumb的圆形的X坐标一直在变话,其变化范围为RangeLeft = 这个circleThumb的半径,小于这个,圆圈就出了View区域左边的边界了,同理,RangeRight = View区域宽度 - 半径,超过RangeRight则出了边界;
ok,主要的分析完毕后,就开始撸代码吧,忽略自定义View的流程,及一些熟悉的代码,大家都狠聪明的
1 在onSizeChanged里面做些尺寸的初始化工作
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
this.mHeight = h;
this.mWidth = (int) (h * WIDTH_HEIGHT_RATIO);
this.roundRadius = mHeight / 2;
this.circleRadius = (mHeight - circleMargin) / 2;
circleThumbStartX = circleRadius + circleMargin;
//circle center move range [roundRadius,mWidth - roundRadius]
rangeLeft = circleRadius + circleMargin;
rangeRight = mWidth - circleRadius - circleMargin;
roundBgF = new RectF(0, 0, mWidth, mHeight);
}
2 让自定义View适配WrapContent 和 matchParent模式(相对于父容器Parent)
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
if (heightMode == MeasureSpec.UNSPECIFIED || heightMode == MeasureSpec.AT_MOST) {
heightMeasureSpec = MeasureSpec.makeMeasureSpec((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, DE_H, getResources().getDisplayMetrics()), MeasureSpec.EXACTLY); } if (widthMode == MeasureSpec.AT_MOST || widthMode == MeasureSpec.UNSPECIFIED) { widthMeasureSpec = MeasureSpec.makeMeasureSpec((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, DE_W, getResources().getDisplayMetrics()), MeasureSpec.EXACTLY); } super.onMeasure(widthMeasureSpec, heightMeasureSpec);}
3 首先绘制出底部的bg
private void drawBg(Canvas canvas) {
Paint paint = createPaint();
paint.setStrokeWidth(roundBgStrokeWidth);
if(mState == MoveState.STATE_CLOSE){
paint.setColor(roundBgClosedColor);
}else {
paint.setColor(roundBgOpenedColor);
}
canvas.drawRoundRect(roundBgF, roundRadius, roundRadius, paint);
}
4 绘制CircleThumb
private void drawLeftThumb(Canvas canvas) {
Paint paint = createPaint();
paint.setStyle(Paint.Style.FILL);
if(mState == MoveState.STATE_CLOSE){
paint.setColor(circleClosedColor);
}else {
paint.setColor(circleOpenedColor);
}
canvas.drawCircle(circleThumbStartX, mHeight / 2, circleRadius, paint);
}
5 onTouch里边实现圆圈的移动 我们已经分析了这个边界的处理,代码如下
@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction(); switch (action) { case MotionEvent.ACTION_DOWN: downX = (int) event.getX(); break; case MotionEvent.ACTION_MOVE: int moveX = (int) event.getX(); if (moveX - downX > canMoveX || downX - moveX > canMoveX) { if (moveX <= rangeLeft && mState == MoveState.STATE_OPEN) { circleThumbStartX = rangeLeft; mState = MoveState.STATE_CLOSE; } else if (moveX >= rangeRight && mState == MoveState.STATE_CLOSE) { circleThumbStartX = rangeRight; mState = MoveState.STATE_OPEN; } else if (rangeLeft < moveX && moveX < rangeRight) { circleThumbStartX = moveX; } invalidate(); updageState(mState); } break; case MotionEvent.ACTION_UP: int upX = (int) (event.getX() - downX); if (0 <= upX && upX <= canMoveX) { if (mState == MoveState.STATE_CLOSE) { startRightAnimation(); } else if (mState == MoveState.STATE_OPEN) { startLeftAnimation(); } } else { if (circleThumbStartX > mWidth / 2) { circleThumbStartX = rangeRight; mState = MoveState.STATE_OPEN; } else { circleThumbStartX = rangeLeft; mState = MoveState.STATE_CLOSE; } invalidate(); updageState(mState); } break; } return true;}
6 让其有线性动画的移动效果,则使用属性动画来实现,使用线性的插值器
动画代码如下,模版代码
setClickable(false);
ValueAnimator rightAnimation = ValueAnimator.ofFloat(0, 1);
rightAnimation.setTarget(this);
rightAnimation.setDuration(200);
rightAnimation.setRepeatCount(0);
rightAnimation.setInterpolator(new LinearInterpolator());
rightAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
Float aFloat = (Float) valueAnimator.getAnimatedValue();
circleThumbStartX = (int) ((int) (rangeRight * aFloat) + rangeLeft * (1 - aFloat));
invalidate();
}
});
rightAnimation.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animator) {
} @Override public void onAnimationEnd(Animator animator) { mState = MoveState.STATE_OPEN; updageState(mState); setClickable(true); } @Override public void onAnimationCancel(Animator animator) { } @Override public void onAnimationRepeat(Animator animator) { } }); rightAnimation.start();
7 最后要暴漏给外部使用 及 View 的一些状态处理
private static class MoveState { private static final int STATE_CLOSE = 0; private static final int STATE_OPEN = 1;}private void updageState(int state) { this.mState = state; if (state == MoveState.STATE_CLOSE) { isOpen = false; } else if (state == MoveState.STATE_OPEN) { isOpen = true; } invalidateMainUI(isOpen);}private void invalidateMainUI(boolean state) { if (null != mSlideOpenCloseListener) { mSlideOpenCloseListener.switchOk(state); }}public interface slideOpenCloseListener { public void switchOk(boolean isOpen);}
总结如下:
1 首先要理解安卓的坐标系 这个区域是一个Rect,Rect中是一个圆圈,让圆圈在这个Rect中来回移动即可,我们只需要处理下边界
2 Canvas paint的api,这个多练即可,无需多说
3 属性动画ValueAnimation的熟练使用及掌握,主要是插植器的理解,厉害的自己写出插值器,不过系统的基本能满足现在的需求
代码就不传了,其实就一个主类,如果需要,我会在csdn建立代码块
- Android自定义View实现开关按钮
- Android自定义View之自定义开关按钮
- android自定义view实现开关
- 自定义View开关按钮
- [Android开发] 自定义View之重写View非常简单实现开关按钮SwitchView
- Android 自定义实现switch开关按钮
- Android 自定义实现switch开关按钮
- Android 自定义实现switch开关按钮
- Android 自定义实现switch开关按钮
- Android 自定义实现switch开关按钮
- Android 自定义实现switch开关按钮
- Android 自定义实现switch开关按钮
- Android自定义View实现开关效果
- Android自定义开关按钮
- android 自定义开关按钮
- Android自定义开关按钮
- android 自定义开关按钮
- Android自定义View——开关按钮SwitchButton
- 隐私政策—海钓王手游
- JAVA入门.零基础学Java语言 MOOC 第5周 数组(数据结构)
- Android Multimedia框架总结(一)MediaPlayer介绍之状态图及生命周期
- div+pre标签的组合实现文本原格式显示与自动换行
- HTC VIVE开发教程(一)
- Android自定义View实现开关按钮
- 设计模式总结
- Android Multimedia框架总结(二)MediaPlayer框架及播放网络视频案例
- URAL 1519Formula 1
- 软件工程开发步骤
- Android Multimedia框架总结(三)MediaPlayer中创建到setDataSource过程
- android四大核心组件之一:BroadCast
- 设计模式之享元模式
- Android手机组件调用方法