上篇文章我们实现了LockView的弹性滑动效果,那么本篇文章我们承接上篇来实现LockView的中心CircleWaveView。当然,LockView上下的两个圆也是自定义View,但是由于过于简单,我们就不再多费口舌了。
那么对于中心View的实现,主要有一下几点:
- 一.定义CircleWaveView类。
- 二.绘制CircleWaveView的内容
- 三.滑动、心跳动画以及状态改变动画实现
一.定义CircleWaveView类
1.添加自定义属性(这次补上来)。在attrs.xml文件中添加一下内容:
<declare-styleable name="CircleWaveView">
<!--选中时的圆点图片-->
<attr name="wave_color" format="color"/>
<attr name="wave_text_color" format="color"/>
<attr name="wave_text_size" format="dimension"/>
<attr name="wave_text_str" format="string"/>
</declare-styleable>
2.新建CircleWaveView类继承View,重写相应的方法,初始化相关数据并读取自定义属性(都是老套路了,写起来自己都觉得烦哎)。
public CircleWaveView(Context context) {
this(context, null);
}
public CircleWaveView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CircleWaveView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
if (attrs != null) {
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CircleWaveView);
circleColor = typedArray.getColor(R.styleable.CircleWaveView_wave_color, context.getResources().getColor(R.color.red));
mTextColor = typedArray.getColor(R.styleable.CircleWaveView_wave_text_color, context.getResources().getColor(R.color.white));
mTextSize = typedArray.getDimension(R.styleable.CircleWaveView_wave_text_size, DensityUtils.dp2px(context, 16));
mText = typedArray.getString(R.styleable.CircleWaveView_wave_text_str);
typedArray.recycle();
}
init(context);
}
private void init(Context context) {
mContext = context;
mPaint = new Paint();
mPaint.setStyle(Paint.Style.FILL);
mPaint.setAntiAlias(true);
mPaint.setColor(circleColor);
mPaintText = new Paint();
mPaintText.setColor(mTextColor);
mPaintText.setStyle(Paint.Style.FILL);
mPaintText.setTextAlign(Paint.Align.CENTER);
mPaintText.setAntiAlias(true);
dp13 = DensityUtils.dp2px(mContext, 13);
mScroller = new Scroller(context);
bounds = new Rect();
arrowUp = BitmapFactory.decodeResource(getResources(),
R.drawable.arrow_up);
arrowDown = BitmapFactory.decodeResource(getResources(),
R.drawable.arrow_down);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mHeight = getHeight();
mWidth = getWidth();
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
mPieCenterX = mWidth / 2;
mPieCenterY = mHeight / 2;
}
二.绘制CircleWaveView的内容
重写onDraw方法,在onDraw里边绘制内容。
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
drawCircle(canvas);
drawText(canvas);
drawTriangle(canvas);
}
// 绘制圆
private void drawCircle(Canvas canvas) {
mPaint.setColor(circleColor);
int verticalCenter = getHeight() / 2;
int horizontalCenter = getWidth() / 2;
int mRadius = Math.min(verticalCenter, horizontalCenter) - Math.min(verticalCenter, horizontalCenter) / 5;
radius = Math.min(verticalCenter, horizontalCenter) - Math.min(verticalCenter, horizontalCenter) / 5;
if (transforming) {
mPaint.setColor(getResources().getColor(R.color.green));
canvas.drawCircle(mPieCenterX, mPieCenterY, mRadius, mPaint);
mRadius = isLock ? transformDelta : mRadius - transformDelta;
mPaint.setColor(getResources().getColor(R.color.red));
canvas.drawCircle(mPieCenterX, mPieCenterY, mRadius, mPaint);
} else {
mRadius = mRadius - waveDelta;
if (!isBluetoothConnect) {
if (isNoNetData) {
mPaint.setColor(getColor(R.color.gray));
} else
mPaint.setColor(isLock ? getColor(R.color.redLight) : getColor(R.color.greenLight));
} else {
if (isLockPrepared) {
mPaint.setColor(getColor(R.color.redDark));
} else if (isUnLockPrePared) {
mPaint.setColor(getColor(R.color.greenDark));
} else {
mPaint.setColor(isLock ? getColor(R.color.red) : getColor(R.color.green));
}
}
canvas.drawCircle(mPieCenterX, mPieCenterY, mRadius, mPaint);
}
}
// 绘制圆中两个三角
private void drawTriangle(Canvas canvas) {
int left = (mWidth - arrowUp.getWidth()) / 2;
canvas.drawBitmap(arrowUp, left, mHeight / 2 - radius + dp13, mPaint);
canvas.drawBitmap(arrowDown, left, mHeight / 2 + radius - dp13 - arrowDown.getHeight(), mPaint);
/*int radius = Math.min(mHeight, mWidth) / 2 - Math.min(mHeight, mWidth) / 8;
mPaintTrangel.setStyle(Paint.Style.FILL);
mPaintTrangel.setShadowLayer(4, 0, 3, Color.GRAY);
// 三角形顶点到圆边的距离
int h0 = DensityUtils.dp2px(mContext, 10);
// 三角形高
int h1 = DensityUtils.dp2px(mContext, 12);
// 三角形底边长
int w = DensityUtils.dp2px(mContext, 14);
mPaintTrangel.setColor(getResources().getColor(R.color.transparent_33));
mPath.moveTo(mWidth / 2, mHeight / 2 - (radius - h0));
mPath.lineTo(mWidth / 2 - w, mHeight / 2 - (radius - h1 - h0));
mPath.lineTo(mWidth / 2 + w, mHeight / 2 - (radius - h1 - h0));
canvas.drawPath(mPath, mPaintTrangel);
mPaintTrangel.setShadowLayer(4, 0, -3, Color.GRAY);
mPath.moveTo(mWidth / 2, mHeight / 2 + (radius - h0));
mPath.lineTo(mWidth / 2 - w, mHeight / 2 + (radius - h1 - h0));
mPath.lineTo(mWidth / 2 + w, mHeight / 2 + (radius - h1 - h0));
canvas.drawPath(mPath, mPaintTrangel);*/
}
// 绘制圆中的文字
private void drawText(Canvas canvas) {
if (isConnecting) return;
if (TextUtils.isEmpty(mText)) { // 绘制单行文字
String text = mContext.getResources().getString(R.string.ble_not_connect);
canvas.drawText(text, mPieCenterX, getBaseline(text), mPaintText);
return;
}
if (isBluetoothConnect) { // 绘制单行文字
canvas.drawText(mText, mPieCenterX, getBaseline(mText), mPaintText);
} else { // 绘制两行文字
String text = mContext.getResources().getString(R.string.ble_not_connect);
int baseline = getBaseline(text);
canvas.drawText(text, mPieCenterX, baseline - 30, mPaintText);
mPaintText.setTextSize(DensityUtils.dp2px(mContext, 12));
canvas.drawText(mText, mPieCenterX, baseline + 30, mPaintText);
}
}
private int getBaseline(String text) {
mPaintText.setTextSize(mTextSize);
mPaintText.getTextBounds(text, 0, text.length(), bounds);
Paint.FontMetricsInt fontMetricsInt = mPaintText.getFontMetricsInt();
return (getMeasuredHeight() - fontMetricsInt.bottom + fontMetricsInt.top) / 2
- fontMetricsInt.top;
}
三.滑动及心跳动画实现
1.使用Scroller来对该View进行滑动操作,并对LockView提供调用接口。
public void smoothScroll(int destX, int destY) {
int scrollY = getScrollY();
int delta = destY - scrollY;
mScroller.startScroll(destX, scrollY, 0, delta, 400);
invalidate();
}
@Override
public void computeScroll() {
if (mScroller.computeScrollOffset()) {
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
postInvalidate();
}
}
2.心跳动画效果的实现。我们这里使用属性动画,通过ValueAnimator动画来计算缩放比例,根据比例计算圆半径的大小,并通过invalidate()方法不断重绘View,从而达到一个心跳动画的效果。
// 开始心跳动画
public void startWave() {
if (animator != null && animator.isRunning())
animator.end();
animator = ValueAnimator.ofFloat(0f, 1f, 0f);
animator.setRepeatCount(ValueAnimator.INFINITE);
animator.setRepeatMode(ValueAnimator.RESTART);
animator.setDuration(600);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int verticalCenter = getHeight() / 2;
int horizontalCenter = getWidth() / 2;
waveDelta = (int) (Math.min(verticalCenter, horizontalCenter) * (float) animation.getAnimatedValue() / 16);
invalidate();
}
});
animator.start();
}
// 停止心跳动画
public void stopWave() {
if (animator != null && animator.isRunning())
animator.end();
}
- 改变锁的状态。这里也是通过ValueAnimator动画来实现一个半径从0扩展到最大或者从最大缩小到0的一个动画。
public void changeLockState(final boolean lock) {
stopWave();
if (this.isLock != lock) {
transforming = true;
ValueAnimator valueAnimator = ValueAnimator.ofFloat(0f, 0.99f);
valueAnimator.setDuration(500);
valueAnimator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
transforming = false;
isLock = lock;
invalidate();
}
@Override
public void onAnimationCancel(Animator animation) {
transforming = false;
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int verticalCenter = getHeight() / 2;
int horizontalCenter = getWidth() / 2;
transformDelta = (int) ((Math.min(verticalCenter, horizontalCenter) - Math.min(verticalCenter, horizontalCenter) / 6)
* (float) animation.getAnimatedValue());
invalidate();
}
});
valueAnimator.start();
}
}
至此,关于LockView的绘制到这里就完全结束了。最后还是放一下源码,需要学习的可以自己取啦!