您的位置:首页 > 其它

ToggleButton自定义View学习

2015-06-04 11:28 357 查看
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));


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

@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
mSpring.addListener(mSpringListener);
}
@Override
protected 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 {

@Override
public 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方法中的绘制过程:

@Override
protected 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 + ", mWhile
a801
RBScale:" + 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);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息