Android自定義時鐘控制元件
阿新 • • 發佈:2019-02-17
專案要求訪問網路是等待狀態要做時鐘的樣子,經過不懈努力,終於寫出來了,現分享出來,功能比較全,直接拷貝程式碼就可以用,僅供有這種需求的碼農們參考,如果採納,請點個贊,謝謝支援。
效果圖
主Activity,主要是在訪問介面的時候開啟時鐘,介面訪問結束關閉時鐘。
package com.example.myapplication; import android.graphics.Color; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); final UFWaitingIndicatorView uFWaitingIndicatorView = (UFWaitingIndicatorView) findViewById(R.id.uFWaitingIndicatorView); findViewById(R.id.open).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { uFWaitingIndicatorView.setTintColor(Color.WHITE); uFWaitingIndicatorView.start();//時鐘開始繪製 } }); findViewById(R.id.close).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { uFWaitingIndicatorView.stopAnimated(false);//停止繪製時鐘 } }); } }
自定義時鐘
package com.cccollector.magazine.view; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.os.Handler; import android.os.Message; import android.support.annotation.Nullable; import android.util.AttributeSet; import android.util.Log; import android.view.View; import com.cccollector.magazine.R; import java.util.Timer; import java.util.TimerTask; /** * 仿小米時鐘 */ public class UFWaitingIndicatorView extends View { private float hourHandStartAngle;//時針當前角度 private float minuteHandStartAngle;//分針當前角度; private float stopAnimateDuration; // 停止動畫時長 private float stopAnimateProgress; // 停止動畫進度, private boolean hiddenWhenStop;//執行完之後是否隱藏; private float minuteHandDuration;//分針一次旋轉多少度; private float speedRatio;//時針是分針的幾倍速度; private float lineWidth;//時針,分針的線段寬度; private float hourHandCurrentAngle;//時針當前角度;; private float minuteHandCurrentAngle;//分針當前角度; private int refreshDuration;//定時器多久重新整理一次; private Paint paint;//畫筆; private float mRadius;//時鐘半徑,不包括padding值; private Canvas mCanvas;//畫布; private Timer waitingTimer;//定時器; private Timer stopTimer; public UFWaitingIndicatorView(Context context) { this(context, null); } public UFWaitingIndicatorView(Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } public UFWaitingIndicatorView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); //配置檔案讀出預設配置,配置的屬性可以寫到xml中生效; TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.UFWaitingIndicatorView); float hourHandStartAngle_attrs = a.getDimension(R.styleable.UFWaitingIndicatorView_hourHandStartAngle, 45); float minuteHandStartAngle_attrs = a.getDimension(R.styleable.UFWaitingIndicatorView_minuteHandStartAngle, 315); float minuteHandDuration_attrs = a.getDimension(R.styleable.UFWaitingIndicatorView_minuteHandDuration, 2000); float speedRatio_attrs = a.getDimension(R.styleable.UFWaitingIndicatorView_speedRatio, 0.375f); float lineWidth_attrs = a.getDimension(R.styleable.UFWaitingIndicatorView_lineWidth, 2); paint = new Paint();//畫筆; hourHandStartAngle = 0; minuteHandStartAngle = 0; hiddenWhenStop = false; minuteHandDuration = minuteHandDuration_attrs; speedRatio = speedRatio_attrs; lineWidth = lineWidth_attrs; hourHandCurrentAngle = 0; minuteHandCurrentAngle = 0; refreshDuration = 50; stopAnimateDuration = 1000; stopAnimateProgress = 1000; setTintColor(Color.parseColor("#15B300")); } //重寫測量方法; @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(measureDimension(widthMeasureSpec), measureDimension(heightMeasureSpec)); } private int measureDimension(int measureSpec) { int result; int mode = MeasureSpec.getMode(measureSpec); int size = MeasureSpec.getSize(measureSpec); if (mode == MeasureSpec.EXACTLY) { result = size; } else { result = 260; if (mode == MeasureSpec.AT_MOST) { result = Math.min(result, size); } } return result; } /** * 重寫方法得到控制元件的寬,即圓的半徑; * @param w * @param h * @param oldw * @param oldh */ @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); //寬和高分別去掉padding值,其中最小值為半徑值 mRadius = (float)Math.min(Math.floor(w /2) , Math.floor(h / 2)) - 3; } /** * 繪製 * @param canvas */ @Override protected void onDraw(Canvas canvas) { //畫布; mCanvas = canvas; //線段的寬; paint.setStrokeWidth(dip2px(getContext(),lineWidth)); mCanvas.save();//儲存畫布; mCanvas.rotate(hourHandCurrentAngle, getWidth() / 2, getHeight() / 2);//以畫布中心為圓點,旋轉畫布; mCanvas.translate(getWidth() / 2, getHeight() / 2); // mCanvas.drawPoint(0,0,paint); mCanvas.drawLine(0f,0f,-(float) (mRadius / Math.sqrt(2)),-(float) (mRadius / Math.sqrt(2)),paint);//畫時針; mCanvas.restore();//復位畫布; mCanvas.save();//儲存畫布; mCanvas.rotate(minuteHandCurrentAngle, getWidth() / 2, getHeight() / 2);//以畫布中心為圓點,旋轉畫布; mCanvas.translate(getWidth() / 2, getHeight() / 2); // mCanvas.drawPoint(0,0,paint); mCanvas.drawLine(0f,0f,-(float) (mRadius / Math.sqrt(2)),(float) (mRadius / Math.sqrt(2)),paint);//畫分針; mCanvas.restore();//復位畫布; } /** * 設定指標顏色; * @param color */ public void setTintColor (int color) { paint.reset();//讓畫筆的設定回到原始狀態; paint.setStrokeCap(Paint.Cap.ROUND);//設定成圓頭; //抗鋸齒 paint.setAntiAlias(true); paint.setColor(color); //重新整理(onDraw)呼叫重繪方法; invalidate(); } /** * 設定背景顏色; * @param color */ public void setBackgroundColor (int color) { setBackgroundColor(color); } /** * 設定線寬; * @param mLineWidth */ public void setLineWidth (float mLineWidth) { this.lineWidth = mLineWidth; } /** * dp轉換成px; * @param context * @param dpValue * @return */ public int dip2px(Context context, float dpValue){ final float scale=context.getResources().getDisplayMetrics().density; return (int)(dpValue*scale+0.5f); } /** * 如果計時器不為空,則說明正在進行,如果為空,,沒進行; * @return */ public boolean started(){ if (waitingTimer != null) { return true; }else { return false; } } public boolean stoping () { return waitingTimer != null && stopAnimateProgress < stopAnimateDuration; } /** * 開始動畫; */ public void start() { // 如果已開始,則返回 if (started()) { return; } // 將起始角度設為當前角度 hourHandCurrentAngle = this.hourHandStartAngle; minuteHandCurrentAngle = this.minuteHandStartAngle; //建立計時器; // 初始化定時器 waitingTimer = new Timer(); waitingTimer.schedule(new TimerTask() { @Override public void run() { // 計算當前角度 hourHandCurrentAngle += (refreshDuration / (minuteHandDuration / speedRatio)) * 360; minuteHandCurrentAngle += (refreshDuration / minuteHandDuration) * 360; mHandler.sendEmptyMessage(Timer_MESSAGE); } }, 0, refreshDuration);//第二個引數,延遲幾秒開始第一次重新整理,第三個引數,每隔幾秒重新整理; } private static final int Timer_MESSAGE =1000; private static final int StopTimer_MESSAGE =1001; //定時器配合handler實現,定時器子執行緒傳送訊息,handler主執行緒重新整理介面; private Handler mHandler = new Handler(){ @Override public void handleMessage(Message msg) { switch (msg.what){ case Timer_MESSAGE: // 重新整理顯示 invalidate(); Log.i("TAG", "handleMessage: "+"ting"); break; case StopTimer_MESSAGE: // 重新整理顯示 invalidate(); // if (hiddenWhenStop) {//執行完之後隱藏 // setVisibility(View.GONE); // } break; } } }; /** * 停止動畫; */ public void stopAnimated(boolean animated) { if (started()) { //停止重新整理等待計時器;。。。。。。。後面要重新開一個停止計時器; waitingTimer.cancel(); waitingTimer = null; } if (animated) { // 縮減到360度以內 hourHandCurrentAngle = (float) Math.floor(hourHandCurrentAngle - Math.floor(hourHandCurrentAngle / 360) * 360); minuteHandCurrentAngle =(float) Math.floor(minuteHandCurrentAngle - Math.floor(minuteHandCurrentAngle / 360) * 360); // 動畫開始時的角度 final float hourHandAnimateBeginAngle = hourHandCurrentAngle; final float minuteHandAnimateBeginAngle = minuteHandCurrentAngle; // 動畫經過的的角度 final double hourHandAnimateAngle = hourHandAnimateBeginAngle < this.hourHandStartAngle ? this.hourHandStartAngle - hourHandAnimateBeginAngle : this.hourHandStartAngle + 360 - hourHandAnimateBeginAngle; final double minuteHandAnimateAngle = minuteHandAnimateBeginAngle < this.minuteHandStartAngle ? this.minuteHandStartAngle - minuteHandAnimateBeginAngle : this.minuteHandStartAngle + 360 - minuteHandAnimateBeginAngle; // 初始化進度 stopAnimateProgress = 0; //停止計時器; stopTimer = new Timer(); stopTimer.schedule(new TimerTask() { @Override public void run() { double t = stopAnimateProgress; double d = stopAnimateDuration;//一秒之內停止進度; // double progress = -1 * (t /= d) * (t - 2); // double progress = (t = t / d - 1) * t * t + 1; // double progress = -((t = t / d - 1) * t * t * t - 1); double progress = (t = t / d - 1) * t * t * t * t + 1; // 計算當前角度 hourHandCurrentAngle = (float) (hourHandAnimateBeginAngle + progress * hourHandAnimateAngle); minuteHandCurrentAngle = (float) (minuteHandAnimateBeginAngle + progress * minuteHandAnimateAngle); // 重新整理顯示 // invalidate(); mHandler.sendEmptyMessage(Timer_MESSAGE); // 更新進度 stopAnimateProgress += refreshDuration; // 如果進度大於等於時長 if (stopAnimateProgress >= stopAnimateDuration) { hourHandCurrentAngle = 0; minuteHandCurrentAngle = 0; // 重新整理顯示 // invalidate(); if (stopTimer != null) { stopTimer.cancel(); stopTimer = null; } mHandler.sendEmptyMessage(StopTimer_MESSAGE); } } },0,refreshDuration); }else { // 恢復初始設定 hourHandCurrentAngle = 0; minuteHandCurrentAngle = 0; // 重新整理顯示 // stopTimer.cancel(); // stopTimer = null; mHandler.sendEmptyMessage(StopTimer_MESSAGE); } } }
為了儘可能的不勞累小夥伴們,特把佈局也貼上
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="com.example.myapplication.MainActivity"> <Button android:id="@+id/open" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="開啟時鐘" /> <Button android:id="@+id/close" android:layout_below="@id/open" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="關閉時鐘" /> <RelativeLayout android:layout_width="40dp" android:layout_height="40dp" android:layout_centerHorizontal="true" android:layout_marginTop="200dp" android:background="@drawable/shape_pointer"> <com.example.myapplication.UFWaitingIndicatorView android:id="@+id/uFWaitingIndicatorView" android:layout_width="22dp" android:layout_height="22dp" android:layout_centerInParent="true"/> </RelativeLayout> </RelativeLayout>
shape_pointer 時鐘橢圓形狀 ,可根據需求,自己定義
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="6dp"/>
<solid android:color="#000000"/>
</shape>
attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="UFWaitingIndicatorView">
<!--時針起始角度-->
<attr name="hourHandStartAngle" format="dimension" />
<!--分針起始角度-->
<attr name="minuteHandStartAngle" format="dimension" />
<!--分針一圈時長-->
<attr name="minuteHandDuration" format="dimension" />
<!--時針分針速度比(0~1)-->
<attr name="speedRatio" format="dimension" />
<!--線寬度-->
<attr name="lineWidth" format="dimension" />
</declare-styleable>
</resources>
到之處程式碼已經寫完,直接拷貝到專案的對應目錄就可以的,如果對你有幫助,請點贊評論,謝謝支援。