效果圖:
素材:
需求:手指撥動指針,更改還款數字。
源碼:
import android.graphics.*;
import android.support.*;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.view.*;
public class DialTableViewextends View {
private Bitmap btmNormalDial, btmCheckedDial, btmNeedle;
private int needleWidth, needleHeight;//指針精心化控制,寬高比為10:1。指針寬度是半徑的2分之1
? ? private Paint mPaint;
private Path maskPath;//遮罩層路徑
? ? private RectF maskRectF;//遮罩層畫弧度需要的方形區域
? ? private float radius =0;//表盤半徑
? ? private float degrees =0;//指針角度
? ? /**
? ? * @return 角度的浮點值? 0-180之間
? ? */
? ? public float getDegrees() {
return degrees;
}
/**
? ? * @param degrees 角度的浮點值? 0-180之間
? ? */
? ? public void setDegrees(@FloatRange(from =0, to =180)float degrees) {
this.degrees = degrees;
invalidate();
}
/**
? ? * @return 角度的整數值 0-180之間
? ? */
? ? public int getDegreesInt() {
return (int) degrees;
}
/**
? ? * @param degrees 角度的整數值 0-180之間
? ? */
? ? public void setDegreesInt(@IntRange(from =0, to =180)int degrees) {
this.degrees = degrees;
invalidate();
}
/**
? ? * @return 角度的百分比 0-100之間
? ? */
? ? public int getDegreesPer() {
return (int) (degrees *100 /180);
}
/**
? ? * @param percent 角度的百分比 0-100之間
? ? */
? ? public void setDegreesPer(@IntRange(from =0, to =100)int percent) {
this.degrees = percent *180.0f /100;
invalidate();
}
@Override
protected void onMeasure(int widthMeasureSpec,int heightMeasureSpec) {
//無論寬高設置多少,最終View的高總是取 (width/2 :height)中的最小值,且寬總是高的2倍。
? ? ? ? int measureWidth = MeasureSpec.getSize(widthMeasureSpec);
int measureHeight = MeasureSpec.getSize(heightMeasureSpec);
int measureRadius = Math.min(measureWidth /2, measureHeight);
setMeasuredDimension(measureRadius *2, measureRadius);
}
@Override
protected void onSizeChanged(int w,int h,int oldw,int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
radius = Math.min(w /2, h);
needleWidth = (int) (radius /2);
needleHeight = needleWidth /10;
btmNeedle = getScaledBitmap(R.drawable.needle, needleWidth, needleHeight);
btmNormalDial = getScaledBitmap(R.drawable.dial_normal, (int) (2 * radius), (int) radius);
btmCheckedDial = getScaledBitmap(R.drawable.dial_checked, (int) (2 * radius), (int) radius);
mPaint =new Paint();
mPaint.setAntiAlias(true);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setColor(Color.BLUE);
maskPath =new Path();
maskRectF =new RectF();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//計算運算變量
? ? ? ? int width = canvas.getWidth();
int height = canvas.getHeight();
float rotateX = width /2;//旋轉中心X
? ? ? ? float rotateY = height - needleHeight /2;//指針旋轉中心Y有一些偏移
? ? ? ? //canvas.drawColor(Color.GRAY);
? ? ? ? //畫白色
? ? ? ? canvas.save();
canvas.drawBitmap(btmNormalDial,0,0, mPaint);
canvas.restore();
//畫紅色
? ? ? ? canvas.save();
maskPath.reset();
maskPath.moveTo(width /2, height);
maskPath.lineTo(0, height);
maskRectF.set(0,0, width,2 * height);
maskPath.arcTo(maskRectF,180, degrees);
maskPath.close();
canvas.clipPath(maskPath);
canvas.drawBitmap(btmCheckedDial,0,0, mPaint);
canvas.restore();
//畫指針
? ? ? ? canvas.save();
canvas.rotate(degrees, rotateX, rotateY);
canvas.drawBitmap(btmNeedle, width /4 + needleHeight /2, height - needleHeight, mPaint);
canvas.restore();
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getY() >= radius) {
return true;
}
return gestureDetector.onTouchEvent(event);
}
GestureDetector gestureDetector =new GestureDetector(getContext(),new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onDown(MotionEvent e) {
//獲取點擊點計算角度,然后開始動畫
? ? ? ? ? ? double pointX = e.getX() - radius;//轉成圓形計算
? ? ? ? ? ? double pointY = e.getY() - radius;//同上
? ? ? ? ? ? double angel = Math.atan2(pointX, pointY);//根據斜率求弧度,tan函數的逆運算
? ? ? ? ? ? int newDegrees = (int) (180 * angel / Math.PI);
if (newDegrees >=90 && newDegrees <=180) {
newDegrees =180 - newDegrees +90;
}else if (newDegrees >= -180 && newDegrees <= -90) {
newDegrees = -newDegrees -90;
}
//Log.i("myk","角度:"+newDegrees+"");
? ? ? ? ? ? degrees = newDegrees;
if (mDegreesChangedListener !=null) {
mDegreesChangedListener.onDegreesChanged(degrees);
}
invalidate();
return true;
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2,float distanceX,float distanceY) {
double pointX = e2.getX() - radius;//轉成圓形計算
? ? ? ? ? ? double pointY = e2.getY() - radius;//+同上
? ? ? ? ? ? double angel = Math.atan2(pointX, pointY);//根據斜率求弧度,tan函數的逆運算
? ? ? ? ? ? int newDegrees = (int) (180 * angel / Math.PI);
if (newDegrees >=90 && newDegrees <=180) {
newDegrees =180 - newDegrees +90;
}else if (newDegrees >= -180 && newDegrees <= -90) {
newDegrees = -newDegrees -90;
}
//Log.i("myk","角度:"+newDegrees+"");
? ? ? ? ? ? degrees = newDegrees;
invalidate();
if (mDegreesChangedListener !=null) {
mDegreesChangedListener.onDegreesChanged(newDegrees);
}
return true;
}
});
//表盤指針事件監聽
? ? private DegreesChangedListener mDegreesChangedListener;
public void setOnDegreesChangedListener(DegreesChangedListener degreesChangedListener) {
this.mDegreesChangedListener = degreesChangedListener;
}
public interface DegreesChangedListener {
void onDegreesChanged(float newDegrees);
}
private Bitmap getScaledBitmap(@DrawableResint sourceId,int width,int height) {
Bitmap bitmap = BitmapFactory.decodeResource(getContext().getResources(), sourceId);
if (bitmap.getWidth() == width && bitmap.getHeight() == height) {
return bitmap;
}
return Bitmap.createScaledBitmap(bitmap, width, height,true);
}
private int dp2px(Context context,int dip) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dip * scale +0.5f);
}
private int sp2px(Context context,float spValue) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, spValue, context.getResources().getDisplayMetrics());
}
public DialTableView(Context context) {
this(context,null);
}
public DialTableView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
}
后話:
? ? ? ? 這也沒啥技術含量,就是官方的api用用,基本的計算公式擺擺,也不多廢話了。
反正用代碼畫圖挺好玩的,文章里可能會有些你一時沒想到的小技巧吧。
? ? ? ? 本來還想加個屬性動畫的,手指點擊,指針慢慢轉過去,我太懶,以后再寫。