Android 圓環帶刻度條進度動畫效果實現

效果圖
圓環帶刻度進度

需求是根據傳感器做一個重力球效果,先實現了動畫后續加上跟傳感器聯動.
又是擺爛的一天, 尚能呼吸,未來可期啊

View源碼

package com.android.circlescalebar.view;import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PointF;
import android.util.AttributeSet;
import android.view.View;
import com.android.circlescalebar.R;
import com.android.circlescalebar.utils.ChartUtils;
import com.android.circlescalebar.utils.DensityUtils;public class CircleGearView extends View {private Context mContext;private Paint mPaint; // 畫筆對象的引用private PointF mProgressPoint;private float mRoundWidth = DensityUtils.dp2px(4); // 圓環的寬度private int centerX, centerY;private int radius, roundRadius;private int paddingOuterThumb;//外邊距private int minValidateTouchArcRadius; // 最小有效點擊半徑private int maxValidateTouchArcRadius; // 最大有效點擊半徑private int mMainColor; //主題顏色private int mInnerRoundColor; //內圓 寬度 、顏色private float mInnerRoundWidth;private int mTxtProgress = 1; // 顯示進度private int max = 200; // 最大進度 -- 總共200個刻度 所以這樣定義private float progress = 1;private double mOuterRoundProgress = 0f;//外圈進度private boolean mOuterSences = true; //true 正向----false方向public CircleGearView(Context context) {this(context, null);}public CircleGearView(Context context, AttributeSet attrs) {this(context, attrs, 0);}public CircleGearView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);mContext = context;initView(attrs);}private void initView(AttributeSet attrs){setLayerType(View.LAYER_TYPE_SOFTWARE, null);  // 關閉硬件加速this.setWillNotDraw(false);                    // 調用此方法后,才會執行 onDraw(Canvas) 方法mPaint = new Paint();//獲取自定義屬性和默認值TypedArray typedArray = mContext.obtainStyledAttributes(attrs, R.styleable.CGViewStyleable);mRoundWidth = typedArray.getDimension(R.styleable.CGViewStyleable_round_width, DensityUtils.dp2px(7));mMainColor = typedArray.getColor(R.styleable.CGViewStyleable_round_color, getResources().getColor(R.color.green));mInnerRoundWidth = typedArray.getDimension(R.styleable.CGViewStyleable_inner_round_width, DensityUtils.dp2px(2));mInnerRoundColor = typedArray.getColor(R.styleable.CGViewStyleable_inner_round_color, getResources().getColor(R.color.white33));paddingOuterThumb = DensityUtils.dp2px(20);}@Overrideprotected void onSizeChanged(int width, int height, int oldw, int oldh) {centerX = width / 2;centerY = height / 2;int minCenter = Math.min(centerX, centerY);radius = (int) (minCenter - mRoundWidth / 2 - paddingOuterThumb); //圓環的半徑roundRadius = radius - (int)(3 * mRoundWidth);minValidateTouchArcRadius = (int) (radius - paddingOuterThumb * 1.5f);maxValidateTouchArcRadius = (int) (radius + paddingOuterThumb * 1.5f);super.onSizeChanged(width, height, oldw, oldh);}@Overridepublic void onDraw(Canvas canvas) {//  setLayerType(LAYER_TYPE_SOFTWARE, null);//對單獨的View在運行時階段禁用硬件加速initOnDraw(canvas);}/** start circle -*/private void initOnDraw(Canvas canvas) {/** 畫刻度-200份- 還分正反切換---start */mPaint.setStrokeWidth(DensityUtils.dp2px(1));for (int i = 0; i < 200; i++){//radius:模糊半徑,radius越大越模糊,越小越清晰,但是如果radius設置為0,則陰影消失不見//dx:陰影的橫向偏移距離,正值向右偏移,負值向左偏移//dy:陰影的縱向偏移距離,正值向下偏移,負值向上偏移//color: 繪制陰影的畫筆顏色,即陰影的顏色(對圖片陰影無效)if (i < mOuterRoundProgress) {if (mOuterSences) {
//                    mPaint.setShadowLayer(30, 0, 0, mMainColor);mPaint.setColor(getResources().getColor(R.color.green));} elsemPaint.setColor(getResources().getColor(R.color.white33));} else {if (mOuterSences)mPaint.setColor(getResources().getColor(R.color.white33));else {
//                    mPaint.setShadowLayer(30, 0, 0, mMainColor);mPaint.setColor(getResources().getColor(R.color.green));}}float mProgress = (i)* 1.0f/ 200 * max;PointF mProgressPoint = ChartUtils.calcArcEndPointXY(centerX, centerY, radius, 360 * mProgress / max, 90);//圓上到圓心float scale1 = radius * 1.0F / mRoundWidth;float scale2 = radius * 1.0F / (radius - mRoundWidth);//計算內圓上的點float disX = (scale1*mProgressPoint.x + scale2*centerX)/(scale1+ scale2);float disY =  (scale1*mProgressPoint.y + scale2*centerY)/(scale1+ scale2);//計算外圓上的點float disX2 = mProgressPoint.x*2 - disX;float disY2 =  mProgressPoint.y*2 - disY;
//            if (mProgress%6 == 0){
//                //直線3/4高度
//                canvas.drawLine(disX2 ,disY2,disX,disY, mPaint);
//            }else{//直線1/2高度float disX3 = (disX*1 + disX2)/2;float disY3 =  (disY*1 + disY2)/2;canvas.drawLine(disX3 ,disY3,disX,disY, mPaint);
//            }}/** 畫刻度-200份- 還分正反切換---end */// 移動圓點mProgressPoint = ChartUtils.calcArcEndPointXY(centerX, centerY, radius - 55, 360 *progress / max, (float)90);
//        //直接用畫筆畫mPaint.setColor(getResources().getColor(R.color.green));  //設置進度的顏色// 設置漸變
//        Shader shader = new RadialGradient(
//                0, 0, 50, // 圓的中心坐標和半徑
//                mMainColor, mInnerRoundColor, // 漸變的起止顏色
//                Shader.TileMode.CLAMP // 漸變模式
//        );
//        mPaint.setShader(shader);canvas.drawCircle(mProgressPoint.x, mProgressPoint.y,30 ,mPaint);canvas.restore();canvas.save();}@Overrideprotected void onDetachedFromWindow() {super.onDetachedFromWindow();}/*** 設置進度,此為線程安全控件,由于考慮多線的問題,需要同步* 刷新界面調用postInvalidate()能在非UI線程刷新** @param progress*/public synchronized void setProgress(float progress) {if (progress < 0) {mTxtProgress = 1;progress = 0;}mTxtProgress =  Math.round(progress);float ss = progress * 200 / 100;progress = (int) ss;if (progress < 0) {throw new IllegalArgumentException("progress not less than 0");}if (progress > max) {progress = max;mOuterRoundProgress = progress + 1;}if (progress <= max) {this.progress = progress;mOuterRoundProgress = progress + 1;postInvalidate();}}
}

工具類

package com.android.circlescalebar.utils;import android.graphics.PointF;public class ChartUtils {/*** 依圓心坐標,半徑,扇形角度,計算出扇形終射線與圓弧交叉點的xy坐標** @param cirX     圓centerX* @param cirY     圓centerY* @param radius   圓半徑* @param cirAngle 當前弧角度* @return 扇形終射線與圓弧交叉點的xy坐標*/public static PointF calcArcEndPointXY(float cirX, float cirY, float radius, floatcirAngle) {float posX = 0.0f;float posY = 0.0f;//將角度轉換為弧度float arcAngle = (float) (Math.PI * cirAngle / 180.0);if (cirAngle < 90) {posX = cirX + (float) (Math.cos(arcAngle)) * radius;posY = cirY + (float) (Math.sin(arcAngle)) * radius;} else if (cirAngle == 90) {posX = cirX;posY = cirY + radius;} else if (cirAngle > 90 && cirAngle < 180) {arcAngle = (float) (Math.PI * (180 - cirAngle) / 180.0);posX = cirX - (float) (Math.cos(arcAngle)) * radius;posY = cirY + (float) (Math.sin(arcAngle)) * radius;} else if (cirAngle == 180) {posX = cirX - radius;posY = cirY;} else if (cirAngle > 180 && cirAngle < 270) {arcAngle = (float) (Math.PI * (cirAngle - 180) / 180.0);posX = cirX - (float) (Math.cos(arcAngle)) * radius;posY = cirY - (float) (Math.sin(arcAngle)) * radius;} else if (cirAngle == 270) {posX = cirX;posY = cirY - radius;} else {arcAngle = (float) (Math.PI * (360 - cirAngle) / 180.0);posX = cirX + (float) (Math.cos(arcAngle)) * radius;posY = cirY - (float) (Math.sin(arcAngle)) * radius;}return new PointF(posX, posY);}/*** 依圓心坐標,半徑,扇形角度,計算出扇形終射線與圓弧交叉點的xy坐標** @param cirX       圓centerX* @param cirY       圓centerY* @param radius     圓半徑* @param cirAngle   當前弧角度* @param orginAngle 起點弧角度* @return 扇形終射線與圓弧交叉點的xy坐標*/public static PointF calcArcEndPointXY(float cirX, float cirY, float radius, floatcirAngle, float orginAngle) {cirAngle = (orginAngle + cirAngle) % 360;return calcArcEndPointXY(cirX, cirY, radius, cirAngle);}
}
package com.android.circlescalebar.utils;import android.content.res.Resources;public class DensityUtils {public float density;public DensityUtils() {density = Resources.getSystem().getDisplayMetrics().density;}/*** 根據手機的分辨率從 dp 的單位 轉成為 px(像素)* @param dpValue 虛擬像素* @return 像素*/public static int dp2px(float dpValue) {return (int) (0.5f + dpValue * Resources.getSystem().getDisplayMetrics().density);}/*** 根據手機的分辨率從 px(像素) 的單位 轉成為 dp* @param pxValue 像素* @return 虛擬像素*/public static float px2dp(int pxValue) {return (pxValue / Resources.getSystem().getDisplayMetrics().density);}/*** 根據手機的分辨率從 dp 的單位 轉成為 px(像素)* @param dpValue 虛擬像素* @return 像素*/public int dip2px(float dpValue) {return (int) (0.5f + dpValue * density);}/*** 根據手機的分辨率從 px(像素) 的單位 轉成為 dp* @param pxValue 像素* @return 虛擬像素*/public float px2dip(int pxValue) {return (pxValue / density);}
}

調用實現

    private int count = 0;  private Handler handler = new Handler();  private Runnable updateTextRunnable;  private CircleGearView circleGearView;@Override  protected void onCreate(Bundle savedInstanceState) {  super.onCreate(savedInstanceState);  setContentView(R.layout.activity_main);  circleGearView = findViewById(R.id.circleGearView); updateTextRunnable = new Runnable() {  @Override  public void run() {  circleGearView.setProgress(count);count++;  if (count > 100) {  // 停止循環  handler.removeCallbacks(this);  } else {  // 繼續循環  handler.postDelayed(this, 1000); // 每秒更新一次  }  }  };  // 開始循環  handler.post(updateTextRunnable);  }  @Override  protected void onDestroy() {  super.onDestroy();  // 確保在Activity銷毀時移除所有回調和消息,防止內存泄漏  handler.removeCallbacks(updateTextRunnable);  }  

布局

    <com.android.circlescalebar.view.CircleGearViewandroid:id="@+id/circleGearView"android:layout_width="match_parent"android:layout_height="match_parent"app:inner_round_color="@color/white33"app:inner_round_width="2dp"app:round_color="@color/green"app:round_width="7dp" />  

attrs

    <declare-styleable name="CGViewStyleable"><!-- 圓的寬度 --><attr name="round_width" format="dimension"/><attr name="round_color" format="color"/><attr name="inner_round_width" format="dimension"/><attr name="inner_round_color" format="color"/></declare-styleable>

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/697395.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/697395.shtml
英文地址,請注明出處:http://en.pswp.cn/news/697395.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

C++ //練習 7.58 下面的靜態數據成員的聲明和定義有錯誤嗎?請解釋原因。

C Primer&#xff08;第5版&#xff09; 練習 7.58 練習 7.58 下面的靜態數據成員的聲明和定義有錯誤嗎&#xff1f;請解釋原因。 //example.h class Example{public:static double rate 6.5;static const int vecSize 20;static vector<double> vec(vecSize); };//e…

【治愈系】心靈雞湯美文:溫暖你的每一寸心田

1.人生就像一杯茶&#xff0c;不會苦一輩子&#xff0c;但總會苦一陣子。只有經歷過苦澀&#xff0c;才能品味到甜美的滋味。 2.每一次失敗都是一次寶貴的經驗&#xff0c;它教會我們如何更好地面對困難和挑戰。不要害怕失敗&#xff0c;因為失敗是成功的前奏。 3.人生最重要的…

【Vue】本地使用 axios 調用第三方接口并處理跨域

前端處理跨域 一. 開發準備 開發工具&#xff1a;VScode框架&#xff1a;Vue2項目結構&#xff1a;vue腳手架生成的標準項目&#xff08;以下僅顯示主要部分&#xff09; 本地已搭建好的端口&#xff1a;8080要請求的第三方接口&#xff1a;http://1.11.1.111:端口號/xxx-api…

刪除文件中的注釋(C語言)

【題目描述】刪除文件中的注釋&#xff1a;將C語言源程序(hello.c)文件中的所有注釋去掉后存入另一個文件(new_hello.c)。試編寫相應程序。 【代碼】 #include <stdio.h> #include <stdlib.h> int main(void) {FILE *fp1, *fp2;if ((fp1fopen("hello.c"…

【Git工具實戰】實用真實 Git 開發工作流程

前言 最近工作中發現&#xff0c;很多開發人員連最基本的Git怎么使用都不知道&#xff0c;比如什么時候切分支&#xff0c;什么時候合并代碼&#xff0c;代碼遇到沖突怎么辦&#xff0c;經常出現掉代碼&#xff0c;代碼合并后丟失的情況。以下為個人總結的常規Git開發工作流程…

Java架構師之路五、微服務:微服務架構、服務注冊與發現、服務治理、服務監控、容器化等。

目錄 微服務架構&#xff1a; 服務注冊與發現&#xff1a; 服務治理&#xff1a; 服務監控&#xff1a; 容器化&#xff1a; 上篇&#xff1a;Java架構師之路四、分布式系統&#xff1a;分布式架構、分布式數據存儲、分布式事務、分布式鎖、分布式緩存、分布式消息中間件、…

C語言系列15——C語言的安全性與防御性編程

目錄 寫在開頭1 緩沖區溢出&#xff1a;如何防范與處理1.1 緩沖區溢出的原因1.2 預防與處理策略 2. 安全的字符串處理函數與使用技巧2.1 strncpy函數2.2 snprintf函數2.3 strlcpy函數2.4 使用技巧 3 防御性編程的基本原則與實際方法3.1 基本原則3.2 實際方法 寫在最后 寫在開頭…

思騰合力攜京東打造服務器采購解決方案,助企業高校提升算力

隨著云計算、大數據、人工智能的快速發展&#xff0c;服務器需求不斷擴大&#xff0c;市場規模持續保持增長。IDC數據顯示&#xff0c;預計2023年我國服務器市場規模將增至308億美元。基于對服務器市場的趨勢洞察&#xff0c;思騰合力攜手京東品牌持續深化合作&#xff0c;在保…

深入淺出JVM(六)之前端編譯過程與語法糖原理

本篇文章將圍繞Java中的編譯器&#xff0c;深入淺出的解析前端編譯的流程、泛型、條件編譯、增強for循環、可變長參數、lambda表達式等語法糖原理 編譯器與執行引擎 編譯器 Java中的編譯器不止一種&#xff0c;Java編譯器可以分為&#xff1a;前端編譯器、即時編譯器和提前編…

(提供數據集下載)基于大語言模型LangChain與ChatGLM3-6B本地知識庫調優:數據集優化、參數調整、Prompt提示詞優化實戰

文章目錄 &#xff08;提供數據集下載&#xff09;基于大語言模型LangChain與ChatGLM3-6B本地知識庫調優&#xff1a;數據集優化、參數調整、提示詞Prompt優化本地知識庫目標操作步驟問答測試的預設問題原始數據情況數據集優化&#xff1a;預處理&#xff0c;先后準備了三份數據…

mac下C、C++項目出現‘stdio.h’ file not found的解決方法

【轉載】https://www.cnblogs.com/yongfengnice/p/14260997.html 有時候更新mac系統或者項目配置之后&#xff0c;打開之前的項目&#xff0c;發現出現莫名其妙的‘stdio.h’ file not found等頭文件找不到。 解決這個問題之前&#xff0c;我們要弄清楚開發工具是引用了系統哪…

C++:STL簡介

1. 什么是STL STL(standard template libaray- 標準模板庫 ) &#xff1a; 是 C 標準庫的重要組成部分 &#xff0c;不僅是一個可復用的組件庫&#xff0c;而且 是一個包羅數據結構與算法的軟件框架 。 2. STL的版本 3. STL的六大組件 4.STL的缺陷 1. STL庫的更新太慢了。這…

用于將Grafana默認數據庫sqlite3遷移到MySQL數據庫

以下是一個方案&#xff0c;用于將Grafana數據遷移到MySQL數據庫。 背景: grafana 默認采用的是sqlite3&#xff0c;當我們要以集群形式部署的時使用mysql較為方便&#xff0c;試了很多sqlite轉mysql的方法要么收費,最后放棄。選擇自己動手風衣足食。 目標: 遷移sqlite3切換…

速評谷歌開源大模型Gemma 7B

大家好,我是herosunly。985院校碩士畢業,現擔任算法研究員一職,熱衷于機器學習算法研究與應用。曾獲得阿里云天池比賽第一名,CCF比賽第二名,科大訊飛比賽第三名。擁有多項發明專利。對機器學習和深度學習擁有自己獨到的見解。曾經輔導過若干個非計算機專業的學生進入到算法…

day16_ListSet課后練習題 - 參考答案

文章目錄 day16_課后練習題第1題第2題第3題第4題第5題第6題第7題第8題 day16_課后練習題 第1題 案例&#xff1a; ? 1、用一個String[]數組存點數 ? 2、用一個String[]數組存花色 ? 3、用一個String[]數組存大王、小王 ? 4、用上面的數組&#xff0c;生成一副撲克牌 …

C++ 文件操作-文本文件-讀取和打開文件方法詳解

讀文件步驟 #include <iostream> using namespace std; #include <fstream> #include <string> //文本文件 讀文件void test(){// 1 包含頭文件// 2 創建流對象ifstream ifs;// 3 打開文件 并且判斷是否打開成功ifs.open("table.txt",ios::in); //…

VS 2015 發布 WebService

本文介紹了使用VS2015發布WebService的步驟 右鍵項目點擊發布 選擇文件系統和目標位置 配置選擇Debug-Any CPU&#xff08;選其他也可以&#xff09; 4. 點擊發布&#xff0c;在對應文件夾中可以看到發布出來的內容。 記錄遇到的問題&#xff0c; 發布前要選擇刪除所有現有文…

【PostgreSQL】PostgreSQL詳細介紹

PostgreSQL詳細介紹 一、什么是PostgreSQL&#xff1f;二、為什么要使用PostgreSQL&#xff1f;三、PostgreSQL功能列表3.1 數據類型3.2 數據完整性3.3 并發&#xff0c;性能3.4 可靠性、災難恢復3.5 安全3.6 可擴展性3.7 國際化&#xff0c;文本搜索 四、參考資料 關于Postgre…

使用MongoDB數據庫和Mongoose庫在Node.js中進行數據存儲

在Node.js中使用MongoDB數據庫和Mongoose庫進行數據存儲是前端開發中常用的技術之一。MongoDB是一種非關系型數據庫&#xff0c;具有高性能、易擴展等優點&#xff1b;而Mongoose是在Node.js中對MongoDB進行操作的框架&#xff0c;簡化了數據庫操作&#xff0c;并提供了豐富的功…

音視頻技術-雙聲道立體聲與卡儂平衡線的“糾葛”

目錄 一、新問題 二、問題排查 三、故障總結 四、原理分析 五、解決方案 1、救急 2、轉接線1 3、轉接線2