如何自定义ProgressBar看起来像温度计?有可能改变颜色。
我的建议是将progressBar旋转90°变为垂直,然后覆盖一个空的温度计图像,但这是一个糟糕而混乱的解决方案。
我认为最好是扩展View或ProgressBar类并自定义绘制方法,但我不知道如何绘制温度计,任何帮助将不胜感激。
答案 0 :(得分:5)
package com.janslab.thermometer.widgets;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.widget.Scroller;
import com.janslab.thermometer.R;
public class DummyThermometer extends View {
private Paint mInnerCirclePaint;
private Paint mOuterCirclePaint;
private Paint mFirstOuterCirclePaint;
//thermometer arc paint
private Paint mFirstOuterArcPaint;
//thermometer lines paints
private Paint mInnerLinePaint;
private Paint mOuterLinePaint;
private Paint mFirstOuterLinePaint;
//thermometer radii
private int mOuterRadius;
private int mInnerRadius;
private int mFirstOuterRadius;
//thermometer colors
private int mThermometerColor = Color.rgb(200, 115, 205);
//circles and lines variables
private float mLastCellWidth;
private int mStageHeight;
private float mCellWidth;
private float mStartCenterY; //center of first cell
private float mEndCenterY; //center of last cell
private float mStageCenterX;
private float mXOffset;
private float mYOffset;
// I 1st Cell I 2nd Cell I 3rd Cell I
private static final int NUMBER_OF_CELLS = 3; //three cells in all ie.stageHeight divided into 3 equal cells
//animation variables
private float mIncrementalTempValue;
private boolean mIsAnimating;
private Animator mAnimator;
public DummyThermometer(Context context) {
this(context, null);
}
public DummyThermometer(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public DummyThermometer(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
if (attrs != null) {
final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Thermometer, defStyle, 0);
mThermometerColor = a.getColor(R.styleable.Thermometer_therm_color, mThermometerColor);
a.recycle();
}
init();
}
private void init() {
mInnerCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mInnerCirclePaint.setColor(mThermometerColor);
mInnerCirclePaint.setStyle(Paint.Style.FILL);
mInnerCirclePaint.setStrokeWidth(17f);
mOuterCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mOuterCirclePaint.setColor(Color.WHITE);
mOuterCirclePaint.setStyle(Paint.Style.FILL);
mOuterCirclePaint.setStrokeWidth(32f);
mFirstOuterCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mFirstOuterCirclePaint.setColor(mThermometerColor);
mFirstOuterCirclePaint.setStyle(Paint.Style.FILL);
mFirstOuterCirclePaint.setStrokeWidth(60f);
mFirstOuterArcPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mFirstOuterArcPaint.setColor(mThermometerColor);
mFirstOuterArcPaint.setStyle(Paint.Style.STROKE);
mFirstOuterArcPaint.setStrokeWidth(30f);
mInnerLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mInnerLinePaint.setColor(mThermometerColor);
mInnerLinePaint.setStyle(Paint.Style.FILL);
mInnerLinePaint.setStrokeWidth(17f);
mOuterLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mOuterLinePaint.setColor(Color.WHITE);
mOuterLinePaint.setStyle(Paint.Style.FILL);
mFirstOuterLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mFirstOuterLinePaint.setColor(mThermometerColor);
mFirstOuterLinePaint.setStyle(Paint.Style.FILL);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mStageCenterX = getWidth() / 2;
mStageHeight = getHeight();
mCellWidth = mStageHeight / NUMBER_OF_CELLS;
//center of first cell
mStartCenterY = mCellWidth / 2;
//move to 3rd cell
mLastCellWidth = (NUMBER_OF_CELLS * mCellWidth);
//center of last(3rd) cell
mEndCenterY = mLastCellWidth - (mCellWidth / 2);
// mOuterRadius is 1/4 of mCellWidth
mOuterRadius = (int) (0.25 * mCellWidth);
mInnerRadius = (int) (0.656 * mOuterRadius);
mFirstOuterRadius = (int) (1.344 * mOuterRadius);
mFirstOuterLinePaint.setStrokeWidth(mFirstOuterRadius);
mOuterLinePaint.setStrokeWidth(mFirstOuterRadius / 2);
mFirstOuterArcPaint.setStrokeWidth(mFirstOuterRadius / 4);
mXOffset = mFirstOuterRadius / 4;
mXOffset = mXOffset / 2;
//get the d/f btn firstOuterLine and innerAnimatedline
mYOffset = (mStartCenterY + (float) 0.875 * mOuterRadius) - (mStartCenterY + mInnerRadius);
mYOffset = mYOffset / 2;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
drawFirstOuterCircle(canvas);
drawOuterCircle(canvas);
drawInnerCircle(canvas);
drawFirstOuterLine(canvas);
drawOuterLine(canvas);
animateInnerLine(canvas);
drawFirstOuterCornerArc(canvas);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//take care of paddingTop and paddingBottom
int paddingY = getPaddingBottom() + getPaddingTop();
//get height and width
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
height += paddingY;
setMeasuredDimension(width, height);
}
private void drawInnerCircle(Canvas canvas) {
drawCircle(canvas, mInnerRadius, mInnerCirclePaint);
}
private void drawOuterCircle(Canvas canvas) {
drawCircle(canvas, mOuterRadius, mOuterCirclePaint);
}
private void drawFirstOuterCircle(Canvas canvas) {
drawCircle(canvas, mFirstOuterRadius, mFirstOuterCirclePaint);
}
private void drawCircle(Canvas canvas, float radius, Paint paint) {
canvas.drawCircle(mStageCenterX, mEndCenterY, radius, paint);
}
private void drawOuterLine(Canvas canvas) {
float startY = mEndCenterY - (float) (0.875 * mOuterRadius);
float stopY = mStartCenterY + (float) (0.875 * mOuterRadius);
drawLine(canvas, startY, stopY, mOuterLinePaint);
}
private void drawFirstOuterLine(Canvas canvas) {
float startY = mEndCenterY - (float) (0.875 * mFirstOuterRadius);
float stopY = mStartCenterY + (float) (0.875 * mOuterRadius);
drawLine(canvas, startY, stopY, mFirstOuterLinePaint);
}
private void drawLine(Canvas canvas, float startY, float stopY, Paint paint) {
canvas.drawLine(mStageCenterX, startY, mStageCenterX, stopY, paint);
}
//simulate temperature measurement for now
private void animateInnerLine(Canvas canvas) {
if (mAnimator == null)
measureTemperature();
if (!mIsAnimating) {
mIncrementalTempValue = mEndCenterY + (float) (0.875 * mInnerRadius);
mIsAnimating = true;
} else {
mIncrementalTempValue = mEndCenterY + (float) (0.875 * mInnerRadius) - mIncrementalTempValue;
}
if (mIncrementalTempValue > mStartCenterY + mInnerRadius) {
float startY = mEndCenterY + (float) (0.875 * mInnerRadius);
drawLine(canvas, startY, mIncrementalTempValue, mInnerCirclePaint);
} else {
float startY = mEndCenterY + (float) (0.875 * mInnerRadius);
float stopY = mStartCenterY + mInnerRadius;
drawLine(canvas, startY, stopY, mInnerCirclePaint);
mIsAnimating = false;
stopMeasurement();
}
}
private void drawFirstOuterCornerArc(Canvas canvas) {
float y = mStartCenterY - (float) (0.875 * mFirstOuterRadius);
RectF rectF = new RectF(mStageCenterX - mFirstOuterRadius / 2 + mXOffset, y + mFirstOuterRadius, mStageCenterX + mFirstOuterRadius / 2 - mXOffset, y + (2 * mFirstOuterRadius) + mYOffset);
canvas.drawArc(rectF, -180, 180, false, mFirstOuterArcPaint);
}
public void setThermometerColor(int thermometerColor) {
this.mThermometerColor = thermometerColor;
mInnerCirclePaint.setColor(mThermometerColor);
mFirstOuterCirclePaint.setColor(mThermometerColor);
mFirstOuterArcPaint.setColor(mThermometerColor);
mInnerLinePaint.setColor(mThermometerColor);
mFirstOuterLinePaint.setColor(mThermometerColor);
invalidate();
}
//simulate temperature measurement for now
private void measureTemperature() {
mAnimator = new Animator();
mAnimator.start();
}
private class Animator implements Runnable {
private Scroller mScroller;
private final static int ANIM_START_DELAY = 1000;
private final static int ANIM_DURATION = 4000;
private boolean mRestartAnimation = false;
public Animator() {
mScroller = new Scroller(getContext(), new AccelerateDecelerateInterpolator());
}
public void run() {
if (mAnimator != this)
return;
if (mRestartAnimation) {
int startY = (int) (mStartCenterY - (float) (0.875 * mInnerRadius));
int dy = (int) (mEndCenterY + mInnerRadius);
mScroller.startScroll(0, startY, 0, dy, ANIM_DURATION);
mRestartAnimation = false;
}
boolean isScrolling = mScroller.computeScrollOffset();
mIncrementalTempValue = mScroller.getCurrY();
if (isScrolling) {
invalidate();
post(this);
} else {
stop();
}
}
public void start() {
mRestartAnimation = true;
postDelayed(this, ANIM_START_DELAY);
}
public void stop() {
removeCallbacks(this);
mAnimator = null;
}
}
private void stopMeasurement() {
if (mAnimator != null)
mAnimator.stop();
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
measureTemperature();
}
@Override
protected void onDetachedFromWindow() {
stopMeasurement();
super.onDetachedFromWindow();
}
@Override
public void setVisibility(int visibility) {
super.setVisibility(visibility);
switch (visibility) {
case View.VISIBLE:
measureTemperature();
break;
default:
stopMeasurement();
break;
}
}
}
attrs.xml文件
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="Thermometer">
<attr name="therm_color" format="color" />
</declare-styleable>
</resources>
答案 1 :(得分:3)
首先我会提供2个setter,一个用于颜色,一个用于温度值,从0 ... 1标准化,其中0表示没有可见条形,1表示完全可见条形。
public void setColor(int color) {
mColor = color;
invalidate(); // important, this triggers onDraw
}
public void setValue(float value) {
mValue = -(value - 1);
invalidate(); // important, this triggers onDraw
}
注意价值,我反转了价值,因为我们从下往上绘制条形,而不是从上到下。这在canvas.drawRect方法中是有意义的。
如果您的CustomView可能有自定义尺寸,请在onSizeChanged
中设置progressBar的尺寸(我将内栏称为 progressBar ),因此当View改变它的大小时调用它。
如果它是固定大小,您可以在init函数或构造函数中静态提供这些值。
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mProgressRect = new Rect(
/*your bar left offset relative to base bitmap*/,
/*your bar top offset relative to base bitmap*/,
/*your bar total width*/,
/*your max bar height*/
);
}
然后在提取时,考虑这些值并相应地绘制。
mValue * totalHeight
。例如:
@Override
protected void onDraw(Canvas canvas) {
// draw your thermometer base, bitmap based on color value
canvas.drawBitmap( /*your base thermometer bitmap here*/ );
// draw the "progress"
canvas.drawRect(mProgressRect.left, mProgressRect.top + (mValue * mProgressRect.bottom - mProgressRect.top), mProgressRect.right, mProgressRect.bottom, mPaint);
}
希望有所帮助。
<强> P.S:强> 如果你想要动态绘制温度计基础图像,这是一个稍微不同的故事,它将涉及首先创建一个路径并用Paint对象绘制它,而不是绘制位图。
修改强>
更好的是,如果你想要一个简单的解决方案来实现&#34;圆度&#34;栏中,画一条线而不是直线。
定义一个这样的线条绘画对象:
mPaint = new Paint();
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(20); // thickness of your bar
然后在onDraw中,而不是drawRect
:
// draw the "progress"
canvas.drawLine(mProgressRect.left, mProgressRect.top + (mValue * mProgressRect.bottom - mProgressRect.top), mProgressRect.left, mProgressRect.bottom, mPaint);
请务必相应地调整mProgressRect
。