1. ToggleButton项目地址

2. rebound项目地址

3. 本地下载

4. 相关参考

android中onMeasure初看,深入理解布局之一!

Android 自定义View onMeasure方法的实现

ANDROID自定义视图——onMeasure,MeasureSpec源码 流程 思路详解

Android视图绘制流程完全解析,带你一步步深入了解View(二)



昨天学习了ToggleButton,一个挺好看的开关。并且自己重新实现了一遍。感觉还是很不错的。今早又重新复习了自定义View的相关知识,包括绘制的流程及回调函数的作用。想到不知怎样才能达到一个高度,有些失落。

这个View的效果图是这样的(图来自这里):


看起来很舒服,但是是怎样实现的呢?实现起来还是需要思考的。结合项目的源码,可以把这个View分解为几部分。这也提供了一种思路,要实现一个复杂漂亮的控件,是一层一层效果叠加起来第一层是最里边的RoundRect,它的效果是由灰色变成青色(手机里还没了解哪个制作gif软件好用)。但是这个变化的过程是如何产生的,这也是整个View最核心的部分。包括滑动过程的控制。作者使用了一个开源的库,是由Facebook团队开发维护的rebound。它的作用是模拟物理的弹簧力的效果,想象一下,一根弹簧固定立起来,把手压下去,当手放开时,弹簧的伸缩变化。即是rebound所实现的,这里涉及到两个系数,就是张力tension和摩擦力friction。rebound的使用可以参考项目的例子或者作者的写法。先是创建Spring实例:

mBaseSpringSystem = SpringSystem.create();mSpring = mBaseSpringSystem.createSpring();mSpring.setSpringConfig(SpringConfig.fromOrigamiTensionAndFriction(50, 7));

然后在适当的位置进行监听和解除监听:

@Overrideprotected void onAttachedToWindow() {super.onAttachedToWindow();mSpring.addListener(mSpringListener);}
@Overrideprotected void onDetachedFromWindow() {super.onDetachedFromWindow();mSpring.removeListener(mSpringListener);}

其中,传入的mSpringListener监听对象继承了SimpleSpringListener,关键在于它的onSpringUdate(Spring spring)方法,我们要重写它的方法。并且进行映射。其中关键的函数是:

mWhileLTScale = (int) SpringUtil.mapValueFromRangeToRange(currentValue, 0, 1, 0, mWhileLTScaleWidth);

它传入了从spring中获取的currentValue,映射的起始范围,和映射的结束范围。返回的值是物理的弹簧力学模型。仔细观察上面开关中间的滑动圆形,可以看到它的伸缩性。我们应该去它的 文档说明网站,里边有介绍如何使用,和一个非常形象的演示系统。便于理解其中的效果。其中,我想到了之前没有深入学习的属性动画中的插值器,这个应该也是做同样的事情。等下要去学习弄懂。这样最重要的事情别人就帮我们做好了。而我们现在,只要在监听回调函数中,进行draw绘画所需属性的修改,就可以完成这个自定义View。但是其中,还是有一些涉及到计算的事情,不能搞错。这也是较为重要和繁琐的工作。比如,我们要计算长和宽、可以匹配的最大范围值,外RountRect的半径,内RountRect的半径等:

int width = getWidth();int height = getHeight();mWhileLeftMargin = (int) mOffBorderWidth;mWhileTopMargin = (int) mOffBorderWidth;mWhileRightMargin = (int) (width - mOffBorderWidth);mWhileBottomMargin = (int) (height - mOffBorderWidth);mWhileLTScaleWidth = width - (width-(int)mOffBorderWidth*2)/3 - width/15;mWhileRBScaleWidth = (height-(int)mOffBorderWidth*2)/4;mRadius = (Math.min(width, height)  ) / 2;mRadius2 = mRadius - mOffBorderWidth;mRadius3 = mRadius2 - mOffBorderWidth;mRectMoveFillLeft = mOffBorderWidth;mRectMoveFillTop = mOffBorderWidth;mRectMoveFillRight = mRectMoveFillLeft + mRadius2 * 2;mRectMoveFillBottom = mOffBorderWidth + mRadius2 * 2;mRectMoveMax = width - mRadius2*2 - mOffBorderWidth;

还有,这段代码应该放在哪个地方。onMeasure和onLayout的作用。当然,这个在给出的参考地址中都有讲解。我们把它放在onLayout函数中,其中相关的只是要获取到getWidth()和getHeight(),其它的都只是纯计算。

现在,我们看第二层绘画,即是一个比第一层图像小一个BorderWidth的内RountRect,并且颜色为白色。打开开关时,它会同时移动到距右边的三分之一,并且缩小。它的移动、缩放的值也是在spring的监听回调函数中改变。第三层是一个圆形的RoundRect,它从左边移动到右边,并且颜色渐变。第四层动画是一个比第三层小一个BorderWidth的圆形RoundRect,颜色为固定的色值,这里为白色,并且同样从左边移动到右边。最后,如果把这四层效果一同绘制,就是上面ToggleButton的效果了。当实现完成后,发现并不是很难,给了自己信心。下面贴几段代码:

①获取自定义的参数值

TypedArray typedArray = null;try {typedArray = context.obtainStyledAttributes(set, R.styleable.ToggleButton);mOnColor = typedArray.getColor(R.styleable.ToggleButton_onColor, mOnColor);mOffColor = typedArray.getColor(R.styleable.ToggleButton_offColor, mOffColor);mOffBorderWidth = typedArray.getDimension(R.styleable.ToggleButton_offBorderWidth, mOffBorderWidth);mOffBorderColor = typedArray.getColor(R.styleable.ToggleButton_offBorderColor, mOffBorderColor);mTension = typedArray.getInteger(R.styleable.ToggleButton_tension, mTension);mResistance = typedArray.getInteger(R.styleable.ToggleButton_resistance, mResistance);} finally {if (typedArray != null ) {typedArray.recycle();}}

②Spring中的监听回调:

private class SpringListener extends SimpleSpringListener {@Overridepublic void onSpringUpdate(Spring spring) {super.onSpringUpdate(spring);double currentValue = spring.getCurrentValue();changeEffect(currentValue );log("currentValue: " + currentValue );}private void changeEffect(double currentValue) {//得到变化的背景颜色值int or = Color.red(mOffColor);int og = Color.green(mOffColor);int ob = Color.blue(mOffColor);int dr = Color.red(mOnColor);int dg = Color.green(mOnColor);int db = Color.blue(mOnColor);int cr = (int) SpringUtil.mapValueFromRangeToRange(1-currentValue, 0, 1, dr, or);int cg = (int) SpringUtil.mapValueFromRangeToRange(1-currentValue, 0, 1, dg, og);int cb = (int) SpringUtil.mapValueFromRangeToRange(1-currentValue, 0, 1, db, ob);cr = modify(cr); cg = modify(cg);cb = modify(cb);mCurrentColor = Color.rgb(cr, cg, cb);//改变第二层效果,即白色的内框移动缩小mWhileLTScale = (int) SpringUtil.mapValueFromRangeToRange(currentValue, 0, 1, 0, mWhileLTScaleWidth);mWhileRBScale = (int) SpringUtil.mapValueFromRangeToRange(currentValue, 0, 1, 0, mWhileRBScaleWidth);log("mWhileLTScale:" + mWhileLTScale + ", mWhileRBScale:" + mWhileRBScale );//移动的圆圈,第三层mCurRectMove = SpringUtil.mapValueFromRangeToRange(currentValue, 0, 1, 0, mRectMoveMax);if (Looper.myLooper() == Looper.getMainLooper() ) {invalidate();} else {postInvalidate();}}private int modify(int cr) {int t = cr > 0 ? cr : 0;t = t < 255 ? t : 255;return t;}}

③onDraw方法中的绘制过程:

@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);log("onDraw()");mPaint.setColor(mCurrentColor);mOutRect.set(0, 0, getWidth(), getHeight());canvas.drawRoundRect(mOutRect, mRadius, mRadius, mPaint);mPaint.setColor(Color.parseColor("#FCFCFC") );//有质感的白色//mOutRect.set(mWhileLeftMargin+mWhileLTScale, mWhileTopMargin+mWhileLTScale, mWhileRightMargin-mWhileRBScale, mWhileBottomMargin-mWhileRBScale);int left = mWhileLeftMargin+mWhileLTScale;int top = mWhileTopMargin+mWhileRBScale;int right = mWhileRightMargin-mWhileRBScale;int bottom =  mWhileBottomMargin-mWhileRBScale;float r2 = mRadius-mOffBorderWidth-mWhileRBScale;log("zb", "mWhileLTScale:" + mWhileLTScale + ", mWhileRBScale:" + mWhileRBScale);log("zb", "left: " + left + ", top: " + top + ", right: " + right + ", bottom :" + bottom );mOutRect.set(left, top, right, bottom);canvas.drawRoundRect(mOutRect, r2, r2, mPaint);//画第三部分,一个移动变化的圆圈mPaint.setColor(mCurrentColor);mOutRect.set(mRectMoveFillLeft+(float)mCurRectMove, mRectMoveFillTop, mRectMoveFillRight+(float)mCurRectMove, mRectMoveFillBottom);canvas.drawRoundRect(mOutRect, mRadius, mRadius, mPaint);//画第四部分,最里面的移动的白色圆圈mPaint.setColor(Color.WHITE);mOutRect.set(mRectMoveFillLeft+(float)mCurRectMove+mOffBorderWidth, mRectMoveFillTop+mOffBorderWidth, mRectMoveFillRight+(float)mCurRectMove-mOffBorderWidth, mRectMoveFillBottom-mOffBorderWidth);canvas.drawRoundRect(mOutRect, mRadius3, mRadius3, mPaint);}




更多相关文章

  1. Android设置透明、半透明等效果
  2. Android(安卓)点击空白或滑动时候关闭软键盘(有scrollview的坑)
  3. Android水波纹效果
  4. 移动开发者大会.html5。Android。ios。wp联盟
  5. android2.3 api demo 学习系列(4)--App/Activity/Custom Title
  6. android UI进阶之弹窗的使用(2)--实现通讯录的弹窗效果
  7. Android关于RecyclerView 设置选中效果和AndroidTV Item选中效果
  8. Android动画中Interpolator 加速器的使用
  9. android:maxLines和android:ellipsize同时使用导致显示异常

随机推荐

  1. android中gps的使用以及解析nmea0183协议
  2. android 在调用执行了reboot系统层做部分
  3. 系出名门Android(7) - 控件(View)之ZoomC
  4. Android 相机拓展库,能够实时采集并且识别
  5. android中使用DisplayMetrics获取屏幕参
  6. Android keystore 调试
  7. Android仿微信QQ群头像生成
  8. Android如何接收locale改变的消息 || loc
  9. Android(安卓)AsyncTask 完美解析 看不懂
  10. ubuntu 14.04 adb 配置及使用