android自定義可拖拽的儀表盤
本文實(shí)例為大家分享了android自定義可拖拽的儀表盤的具體代碼,供大家參考,具體內(nèi)容如下
因?yàn)轫?xiàng)目最近需要用到儀表盤,又不想使用之前使用的背景圖的方式。主要是想自己寫一點(diǎn)代碼。覺得繪制要比圖片好。于是有了下面這張圖:

面從弧度,刻度,文字,指針都是canvas繪制出來的。
/**
* Created by xulc on 2018/7/18.
*/
public class DashboardView extends View {
private int minWidthDP = 200;
private int minHeightDP = 100;
private Paint arcPaint,arcInnerPaint,linePaint,textPaint;
private int arcColor = Color.parseColor("#0096ff"); //外層弧形顏色
private int arcInnerColor = Color.parseColor("#FFFFFFFF"); //內(nèi)層弧形顏色
private int lineColor = Color.parseColor("#333333"); //線條顏色
private int pointerColor = Color.parseColor("#439AFF"); //指針顏色
private int arcWidthDP = 1;
private RectF arcRectF,arcInnerRectF;
private int widthDash = 0;//表盤的寬度
private int mwidth =0;
private int mheight = 0;
private float shortlineLength = 0 ,longlineLength = 0; //線的長(zhǎng)度
private Path path = new Path();
private Path pointerPath = new Path(); //指針繪制路徑
private Region pointerRegion = new Region(); //指針區(qū)域
private RectF rectF = new RectF();
private boolean isChoosePointer = false;
private int mdegree = 0;
public DashboardView(Context context) {
this(context,null);
}
public DashboardView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
initPaint();
}
//初始化相關(guān)資源
private void initPaint() {
arcPaint = new Paint();
arcPaint.setColor(arcColor);
arcPaint.setAntiAlias(true);
arcPaint.setStrokeCap(Paint.Cap.SQUARE);
arcPaint.setStrokeWidth(1);
arcPaint.setStyle(Paint.Style.FILL);
arcInnerPaint = new Paint();
arcInnerPaint.setColor(arcInnerColor);
arcInnerPaint.setAntiAlias(true);
arcInnerPaint.setStrokeCap(Paint.Cap.SQUARE);
arcInnerPaint.setStrokeWidth(1);
arcInnerPaint.setStyle(Paint.Style.FILL);
linePaint = new Paint();
linePaint.setColor(lineColor);
linePaint.setAntiAlias(true);
linePaint.setStrokeCap(Paint.Cap.SQUARE);
linePaint.setStrokeWidth(arcWidthDP);
linePaint.setStyle(Paint.Style.FILL);
textPaint = new Paint();
textPaint.setColor(lineColor);
textPaint.setAntiAlias(true);
linePaint.setStrokeCap(Paint.Cap.SQUARE);
linePaint.setStrokeWidth(arcWidthDP);
linePaint.setStyle(Paint.Style.STROKE);
textPaint.setTextAlign(Paint.Align.LEFT);
textPaint.setTextSize(30);
arcRectF = new RectF();
arcInnerRectF = new RectF();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
if(widthSize < DensityUtil.dip2px(getContext(),minWidthDP)||heightSize < DensityUtil.dip2px(getContext(),minHeightDP)){
widthSize = DensityUtil.dip2px(getContext(),minWidthDP);
heightSize = DensityUtil.dip2px(getContext(),minHeightDP);
}
if(widthSize/2 != heightSize){
heightSize = widthSize/2;
}
setMeasuredDimension(widthSize,heightSize + 50);
arcRectF.left = 0;
arcRectF.bottom = heightSize*2;
arcRectF.right = widthSize;
arcRectF.top = 0;
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
widthDash = DensityUtil.dip2px(getContext(),50);
arcInnerRectF.left = widthDash;
arcInnerRectF.bottom = arcRectF.bottom - widthDash;
arcInnerRectF.right = arcRectF.bottom -widthDash;
arcInnerRectF.top = widthDash;
shortlineLength = widthDash/7;
longlineLength = widthDash/5;
mwidth = getWidth();
mheight = getHeight() - 50;
Log.d("xulc","mheight----->"+mheight);
Log.d("xulc","arcRectF.bottom----->"+arcRectF.bottom);
}
@Override
protected void onDraw(Canvas canvas) {
arcPaint.setColor(arcColor);
canvas.drawArc(arcRectF,180,180,true,arcPaint); //繪制外弧形
canvas.drawArc(arcInnerRectF,180,180,true,arcInnerPaint); //繪制內(nèi)部弧形
canvas.save();
drawScale(canvas); //繪制刻度
canvas.restore();
drawText(canvas); //繪制文本
drawPointer(canvas,mdegree); //繪制指針
}
private int mradius = 50;
//繪制指針
private void drawPointer(Canvas canvas,float degree){
pointerPath.reset();
if(isChoosePointer){
arcPaint.setColor(pointerColor);
}
pointerPath.reset();
pointerPath.moveTo((float)( mwidth/2 - mradius*Math.sin(degree/180f*Math.PI)),(float)( mheight + mradius*Math.cos(degree/180f*Math.PI))); //下切點(diǎn)
pointerPath.lineTo(mwidth/2 - (float) Math.cos(degree/180f*Math.PI)*(mheight - widthDash - longlineLength -mradius),mheight - (float) Math.sin(degree/180f*Math.PI)*(mheight - widthDash - longlineLength-mradius));
pointerPath.lineTo((float)( mwidth/2 + mradius*Math.sin(degree/180f*Math.PI)),(float)( mheight - mradius*Math.cos(degree/180f*Math.PI)));
pointerPath.close();
pointerPath.computeBounds(rectF,true);
pointerRegion.setPath(pointerPath,new Region((int) rectF.left,(int) rectF.top,(int) rectF.right,(int) rectF.bottom));
canvas.drawPath(pointerPath,arcPaint); //path轉(zhuǎn)化為Region區(qū)域,方便判斷用戶點(diǎn)擊的位置
path.reset();
arcPaint.setColor(arcColor);
path.addCircle(mwidth/2,mheight,mradius, Path.Direction.CW);
canvas.drawPath(path,arcPaint);
textPaint.setTextAlign(Paint.Align.CENTER);
canvas.drawText(""+mdegree,mwidth/2,mheight,textPaint);
}
//設(shè)置度數(shù)
public void setDegree(int degree){
if(0<=degree && degree<=180){
mdegree = degree;
invalidate();
}
}
//觸摸事件
@Override
public boolean onTouchEvent(MotionEvent event) {
float startx ,starty;
if(event.getAction()==MotionEvent.ACTION_DOWN){
startx = event.getX(); starty = event.getY();
if(pointerRegion.contains((int) startx,(int) starty)){ //在其中
isChoosePointer =true;
invalidate();
return true; //消費(fèi)當(dāng)前事件,否則不會(huì)繼續(xù)分發(fā)后續(xù)事件
}
return false;
}else if(event.getAction()==MotionEvent.ACTION_MOVE){
if(isChoosePointer){
float x = event.getX(); float y = event.getY();
if(y <= mheight && x!=mwidth/2){
double degree= Math.atan2((mheight-y) ,(mwidth/2 - x));
setDegree((int) (degree/Math.PI*180));
}else{
if(y > mheight&& x < mwidth/2){ //說明滑到下面了
setDegree(0);
}else if(y > mheight&& x > mwidth/2){
setDegree(180);
}
}
return true;
}else{
return false;
}
}else if(event.getAction()==MotionEvent.ACTION_UP||event.getAction()==MotionEvent.ACTION_CANCEL){
isChoosePointer =false;
invalidate();
return true;
}
return super.onTouchEvent(event);
}
//繪制文字
private void drawText(Canvas canvas) {
textPaint.setTextAlign(Paint.Align.LEFT);
for(int i=0;i<=6;i++){
int degree = i*30;
float textWidth = textPaint.measureText(""+degree);
if(degree ==0){
canvas.drawText("" + degree,mwidth/2 - (float) Math.cos(degree/180f*Math.PI)*(mheight - widthDash - longlineLength -10) - textWidth/2,mheight - (float) Math.sin(degree/180f*Math.PI)*(mheight - widthDash - longlineLength-10)+7,textPaint);
} //向右邊移動(dòng)7個(gè)像素 向下邊移動(dòng)7個(gè)像素
else if(degree == 30){
canvas.drawText("" + degree,mwidth/2 - (float) Math.cos(degree/180f*Math.PI)*(mheight - widthDash - longlineLength-10) - textWidth/2,mheight - (float) Math.sin(degree/180f*Math.PI)*(mheight - widthDash - longlineLength-10)+7 ,textPaint);
}else if(degree ==60){
canvas.drawText("" + degree,mwidth/2 - (float) Math.cos(degree/180f*Math.PI)*(mheight - widthDash - longlineLength-10) - textWidth/2,mheight - (float) Math.sin(degree/180f*Math.PI)*(mheight - widthDash - longlineLength-10)+7 ,textPaint);
}else if(degree ==90){
canvas.drawText("" + degree,mwidth/2 - (float) Math.cos(degree/180f*Math.PI)*(mheight - widthDash - longlineLength-10) - textWidth/2,mheight - (float) Math.sin(degree/180f*Math.PI)*(mheight - widthDash - longlineLength-10)+7 ,textPaint);
}else{
canvas.drawText("" + degree,mwidth/2 - (float) Math.cos(degree/180f*Math.PI)*(mheight - widthDash - longlineLength - 10) - textWidth,mheight - (float) Math.sin(degree/180f*Math.PI)*(mheight - widthDash - longlineLength-10)+7 ,textPaint);
}
}
}
//繪制刻度
private void drawScale(Canvas canvas){
for(int i=0;i<=36;i++){ //180角度,30度一個(gè)長(zhǎng)線 0 30 60 90 120 150 180 5條小線 5度一個(gè)小線
if(i%6==0){//長(zhǎng)線
canvas.drawLine(widthDash,mheight,widthDash + longlineLength ,mheight,linePaint);
}else{ //短線
canvas.drawLine(widthDash,mheight,widthDash + shortlineLength ,mheight,linePaint);
}
canvas.rotate(5,mwidth/2,mheight);
}
}
}
整體代碼差不多就這樣,代碼中詳盡的注釋。代碼基本上都在這了,就不上傳git了。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
詳解用RxJava實(shí)現(xiàn)事件總線(Event Bus)
本篇文章主要介紹了用RxJava實(shí)現(xiàn)事件總線(Event Bus),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-11-11
Android實(shí)現(xiàn)數(shù)據(jù)按照時(shí)間排序
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)數(shù)據(jù)按照時(shí)間排序的方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-09-09
Android實(shí)現(xiàn)背景顏色滑動(dòng)漸變效果的全過程
在Android開發(fā)中,經(jīng)常需要設(shè)置控件的背景顏色或者圖片的src顏色,下面這篇文章主要給大家介紹了關(guān)于Android實(shí)現(xiàn)背景顏色滑動(dòng)漸變效果的相關(guān)資料,本文通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2021-09-09
ContentProvider客戶端處理provider邏輯分析
這篇文章主要為大家介紹了ContentProvider客戶端處理provider邏輯分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-10-10
Android 中 requestWindowFeature()的應(yīng)用
本文主要介紹 Android requestWindowFeature()方法,這里對(duì) requestWindowFeature()方法進(jìn)行詳解,對(duì)應(yīng)用程序窗體顯示狀態(tài)的操作有進(jìn)一步了解,希望能幫助有需要的小伙伴2016-07-07
android通過代碼的形式來實(shí)現(xiàn)應(yīng)用程序的方法
因?yàn)閼?yīng)用程序的安裝與卸載模塊在android系統(tǒng)中已經(jīng)寫好了,所以我們只需要激活就行了2013-10-10
Android開發(fā)筆記 今天學(xué)到的一些屬性
離開實(shí)驗(yàn)室之前再貼上今天下午自己學(xué)到的一些基礎(chǔ)知識(shí) 上午干嘛了呢,忙著數(shù)據(jù)恢復(fù)呢2012-11-11

