项目里面又碰到一个酷炫的刻度盘,还要带平滑动画的,真伤脑筋啊,网上搜索半天无果,果然还是得自己动手啊。
用时半天,做了一个DEMO,效果图如下:
代码如下,复制粘贴就能跑了:
MeterView.java:
/** * Copyright (C) 2015 * * MeterView.java * * Description: * * Author: Liao Longhui * * Ver 1.0, 2015-05-17, Liao Longhui, Create file */ package com.example.test; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Paint.Align; import android.util.AttributeSet; import android.view.View; import android.widget.Scroller; import java.util.ArrayList; import java.util.List; /** * 半圆码表图 * @author LLH * */ public class MeterView extends View { /** * 刻度颜色 */ private int mDegreeColor = Color.LTGRAY; /** * 主刻度颜色 */ private int mMajorDegreeColor = Color.GRAY; /** * 刻度数颜色 */ private int mDegreeTextColor = Color.BLUE; /** * 刻度数大小(px) */ private int mDegreeTextSize; /** * 当前数值文字大小(px) */ private int mContentTextSize; /** * 当前数值文字颜色 */ private int mContentTextColor = Color.BLUE; /** * 单位文字大小(px) */ private int mUnitTextSize; /** * 单位文字颜色 */ private int mUnitTextColor = Color.RED; /** * 标题文字颜色 */ private int mTitleTextColor = Color.BLUE; /** * 标题文字大小(px) */ private int mTitleTextSize; /** * 刻度渐变色起始RGB值 */ private int startColor_r = 0x00; private int startColor_g = 0xFF; private int startColor_b = 0x00; private int endColor_r = 0xFF; private int endColor_g = 0x00; private int endColor_b = 0x00; private Paint mDegreePaint; private Paint mMajorDegreePaint; private Paint mColorDegreePaint; private Paint mUnitPaint; private Paint mContentTextPaint; private Paint mDegreeTextPaint; private Paint mTitleTextPaint; /** * 当前数值 */ private float mContent = 0; /** * 前一数字对应的刻度索引 */ private int mLastDegreeIndex; private String mUnit = "W"; private String mTiltl = "当前功率"; /** * 刻度主刻度数据 */ private List<String> mDegrees = new ArrayList<String>(); /** * 刻度最大值和最小值 */ private float degreeMax, degreeMin; /** * 刻度长度 */ private int mDegreeLen; /** * 刻度盘各刻度线的信息 */ private List<DegreeLine> mDegreeLines; private int width, height; private int radius; private int xPoint, yPoint; private float density; private Scroller mScroller; public MeterView(Context context, AttributeSet attrs) { super(context, attrs); density = getResources().getDisplayMetrics().density; mScroller = new Scroller(context); } public MeterView(Context context) { super(context); } private void initPaint() { mDegreeTextSize = radius / 10; mContentTextSize = radius / 4; mUnitTextSize = radius / 6; mTitleTextSize = radius / 8; mDegreePaint = new Paint(); mDegreePaint.setStyle(Paint.Style.FILL); mDegreePaint.setAntiAlias(true); mDegreePaint.setColor(mDegreeColor); mDegreePaint.setStrokeWidth(2f * density); mMajorDegreePaint = new Paint(); mMajorDegreePaint.setStyle(Paint.Style.FILL); mMajorDegreePaint.setAntiAlias(true); mMajorDegreePaint.setColor(mMajorDegreeColor); mMajorDegreePaint.setStrokeWidth(2.5f * density); mColorDegreePaint = new Paint(); mColorDegreePaint.setStyle(Paint.Style.FILL); mColorDegreePaint.setAntiAlias(true); mColorDegreePaint.setStrokeWidth(2.5f * density); mUnitPaint = new Paint(); mUnitPaint.setStyle(Paint.Style.FILL); mUnitPaint.setAntiAlias(true); mUnitPaint.setColor(mUnitTextColor); mUnitPaint.setStrokeWidth(2); mUnitPaint.setTextAlign(Align.CENTER); mUnitPaint.setTextSize(mUnitTextSize); mContentTextPaint = new Paint(); mContentTextPaint.setStyle(Paint.Style.FILL); mContentTextPaint.setAntiAlias(true); mContentTextPaint.setColor(mContentTextColor); mContentTextPaint.setStrokeWidth(2); mContentTextPaint.setTextAlign(Align.CENTER); mContentTextPaint.setTextSize(mContentTextSize); mDegreeTextPaint = new Paint(); mDegreeTextPaint.setStyle(Paint.Style.FILL); mDegreeTextPaint.setAntiAlias(true); mDegreeTextPaint.setColor(mDegreeTextColor); mDegreeTextPaint.setStrokeWidth(2); mDegreeTextPaint.setTextAlign(Align.CENTER); mDegreeTextPaint.setTextSize(mDegreeTextSize); mTitleTextPaint = new Paint(); mTitleTextPaint.setStyle(Paint.Style.FILL); mTitleTextPaint.setAntiAlias(true); mTitleTextPaint.setColor(mTitleTextColor); mTitleTextPaint.setStrokeWidth(2); mTitleTextPaint.setTextSize(mTitleTextSize); } @Override public void computeScroll() { super.computeScroll(); if (mScroller.computeScrollOffset()) { int x = mScroller.getCurrX(); for (int i = 0; i < mDegreeLines.size(); i++) { if (i <= x) { mDegreeLines.get(i).showColor = true; } else { mDegreeLines.get(i).showColor = false; } } postInvalidate(); } } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); for (int i = 0; i < mDegreeLines.size(); i++) { if (mDegreeLines.get(i).showColor) { mColorDegreePaint.setColor(mDegreeLines.get(i).color); } canvas.drawLine(mDegreeLines.get(i).x1, mDegreeLines.get(i).y1, mDegreeLines.get(i).x2, mDegreeLines.get(i).y2, mDegreeLines.get(i).showColor ? mColorDegreePaint : (mDegreeLines.get(i).isMajor ? mMajorDegreePaint : mDegreePaint)); if (mDegreeLines.get(i).isMajor) { int x = (int) ((5 * mDegreeLines.get(i).x2 - 2 * mDegreeLines.get(i).x1) / 3); int y = (int) ((5 * mDegreeLines.get(i).y2 - 2 * mDegreeLines.get(i).y1) / 3); canvas.drawText(mDegrees.get(i / 10), x, y, mDegreeTextPaint); } } canvas.drawText(mTiltl, 0, mTitleTextSize, mTitleTextPaint); canvas.drawText(mUnit, xPoint, yPoint, mUnitPaint); canvas.drawText(String.valueOf(mContent), xPoint, yPoint - mUnitTextSize - 5 * density, mContentTextPaint); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); initPaint(); initDegrees(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { density = getResources().getDisplayMetrics().density; width = getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec); height = getDefaultSize(getSuggestedMinimumWidth(), heightMeasureSpec); if (width / 2 + 15 * density < height) { height = (int) (width / 2 + 15 * density); } else { width = (int) ((height - 15 * density) * 2); } radius = (int) (width / 2 - 1 * density); xPoint = width / 2; yPoint = (int) (height - 1 * density); setMeasuredDimension(width, height); } /** * 设置当前刻度数值 * * @param content */ public void setContent(float content) { this.mContent = content; changeDegree(); } public float getContent() { return mContent; } /** * 设置主刻度数值 * * @param degrees */ public void setDegrees(List<String> degrees, float degreeMax, float degreeMin) { this.mDegrees = degrees; this.degreeMax = degreeMax; this.degreeMin = degreeMin; mDegreeLen = degrees.size(); mLastDegreeIndex = 0; mContent = 0; initDegrees(); invalidate(); } public String getTiltl() { return mTiltl; } /** * 设置标题 * * @param mTiltl */ public void setTiltl(String mTiltl) { this.mTiltl = mTiltl; invalidate(); } /** * 设置小刻度颜色 * * @param mDegreeColor */ public void setDegreeColor(int mDegreeColor) { this.mDegreeColor = mDegreeColor; invalidate(); } /** * 设置大刻度颜色 * * @param mMajorDegreeColor */ public void setMajorDegreeColor(int mMajorDegreeColor) { this.mMajorDegreeColor = mMajorDegreeColor; invalidate(); } /** * 设置刻度数值文字颜色 * * @param mDegreeTextColor */ public void setDegreeTextColor(int mDegreeTextColor) { this.mDegreeTextColor = mDegreeTextColor; invalidate(); } /** * 设置单位颜色 * * @param mUnitTextColor */ public void setUnitTextColor(int mUnitTextColor) { this.mUnitTextColor = mUnitTextColor; invalidate(); } /** * 设置标题颜色 * * @param mTitleTextColor */ public void setTitleTextColor(int mTitleTextColor) { this.mTitleTextColor = mTitleTextColor; invalidate(); } /** * 设置标题大小(px) * * @param mTitleTextSize */ public void setTitleTextSize(int mTitleTextSize) { this.mTitleTextSize = mTitleTextSize; invalidate(); } /** * 设置单位 * * @param mUnit */ public void setUnit(String mUnit) { this.mUnit = mUnit; invalidate(); } private void changeDegree() { int degreeIndex = (int) (10f * (mDegreeLen - 1) * (mContent - degreeMin) / (degreeMax - degreeMin)); mScroller.startScroll(mLastDegreeIndex, 0, degreeIndex - mLastDegreeIndex, 0, 1500); mLastDegreeIndex = degreeIndex; invalidate(); } private void initDegrees() { mDegreeLines = new ArrayList<DegreeLine>(); double deltaAngle = Math.PI / (10 * (mDegreeLen - 1)); float deltaColor_r = (endColor_r - startColor_r) / (5f * (mDegreeLen - 1)); float deltaColor_g = (endColor_g - startColor_g) / (5f * (mDegreeLen - 1)); float deltaColor_b = (endColor_b - startColor_b) / (10f * (mDegreeLen - 1)); for (int i = 0; i < 10 * (mDegreeLen - 1) + 1; i++) { DegreeLine line = new DegreeLine(); int smallRadius = 7 * radius / 8; int k = -1; if (deltaAngle * i > Math.PI / 2) { k = 1; } if (i % 10 == 0) { line.isMajor = true; smallRadius = 5 * radius / 6; } line.y1 = (float) (yPoint - radius * Math.sin(deltaAngle * i)); line.x1 = (float) (k * Math.sqrt(radius * radius - (yPoint - line.y1) * (yPoint - line.y1)) + xPoint); line.y2 = (float) (yPoint - smallRadius * Math.sin(deltaAngle * i)); line.x2 = (float) (k * Math.sqrt(smallRadius * smallRadius - (yPoint - line.y2) * (yPoint - line.y2)) + xPoint); int color_r = (int) (startColor_r + i * deltaColor_r); int color_g = (int) (startColor_g + i * deltaColor_g); int color_b = (int) (startColor_b + i * deltaColor_b); if (i < 5 * (mDegreeLen - 1) + 1) { color_g = startColor_g; } else { color_r = endColor_r; } line.color = Color.rgb(color_r, color_g, color_b); mDegreeLines.add(line); } } public class DegreeLine { /** * 是否大刻度 */ public boolean isMajor; /** * 是否显示颜色 */ public boolean showColor; /** * 颜色 */ public int color; /** * 端点坐标 */ public float x1; public float y1; public float x2; public float y2; } }
MeterActivity.java:
public class MeterActivity extends Activity { protected Toast toast; private MeterView meterView; private int max = 0; private boolean isAlive; private Handler mHandler = new Handler() { public void handleMessage(Message msg) { if (!isAlive) { return; } switch (msg.what) { case 1: int num = (int) (Math.random() * max); meterView.setContent(num); mHandler.sendEmptyMessageDelayed(1, 1000); break; default: break; } }; }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_meter); isAlive = true; meterView = (MeterView) findViewById(R.id.meterView1); List<String> degrees = new ArrayList<String>(); degrees.add("0 "); degrees.add("20"); degrees.add("40"); degrees.add("60"); degrees.add("80"); max = 80; meterView.setDegrees(degrees, 80f, 0f); mHandler.sendEmptyMessageDelayed(1, 1000); findViewById(R.id.button1).setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub List<String> degrees = new ArrayList<String>(); for (int i = 0; i < 7; i++) { degrees.add(String.valueOf(20 * i)); } max = 20 * 6; degrees.set(0, "0 "); meterView.setDegrees(degrees, max, 0); } }); findViewById(R.id.button2).setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub List<String> degrees = new ArrayList<String>(); for (int i = 0; i < 9; i++) { degrees.add(String.valueOf(20 * i)); } max = 20 * 8; degrees.set(0, "0 "); meterView.setDegrees(degrees, max, 0); } }); findViewById(R.id.button3).setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub List<String> degrees = new ArrayList<String>(); for (int i = 0; i < 10; i++) { degrees.add(String.valueOf(50 * i)); } max = 50 * 9; degrees.set(0, "0 "); meterView.setDegrees(degrees, max, 0); } }); findViewById(R.id.button4).setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub List<String> degrees = new ArrayList<String>(); for (int i = 0; i < 13; i++) { degrees.add(String.valueOf(50 * i)); } max = 50 * 12; degrees.set(0, "0 "); meterView.setDegrees(degrees, max, 0); } }); } @Override protected void onDestroy() { // TODO Auto-generated method stub super.onDestroy(); isAlive = false; } public void makeToast(String msg) { if (toast == null) { toast = Toast.makeText(this, msg, Toast.LENGTH_SHORT); } toast.setText(msg); toast.show(); } }
activity_meter.xml:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="10dp" > <TextView android:id="@+id/text1" android:layout_width="match_parent" android:layout_height="3dp" android:background="#abcdef" android:layout_centerInParent="true"/> <com.example.test.MeterView android:id="@+id/meterView1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignLeft="@+id/text1" android:layout_alignParentTop="true" android:background="#55abcdef" /> <Button android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_below="@+id/text1" android:text="140" /> <Button android:id="@+id/button2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignLeft="@+id/button1" android:layout_below="@+id/button1" android:text="180" /> <Button android:id="@+id/button3" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignRight="@+id/button2" android:layout_below="@+id/button2" android:text="300" /> <Button android:id="@+id/button4" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignLeft="@+id/button3" android:layout_below="@+id/button3" android:text="600" /> </RelativeLayout>