




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
【移動(dòng)應(yīng)用開發(fā)技術(shù)】Android中怎么實(shí)現(xiàn)一個(gè)圓弧刷新動(dòng)畫
Android中怎么實(shí)現(xiàn)一個(gè)圓弧刷新動(dòng)畫,相信很多沒有經(jīng)驗(yàn)的人對(duì)此束手無策,為此本文總結(jié)了問題出現(xiàn)的原因和解決方法,通過這篇文章希望你能解決這個(gè)問題。之前刷動(dòng)畫的效果是三段圓弧進(jìn)行旋轉(zhuǎn),同時(shí)弧度也在逐漸增大縮小,這里采用的是在onDraw中繪制三段圓弧。//
繪制圓弧
mPaint.setColor(mTopColor);
canvas.drawArc(left,
top,
right,
bottom,
startAngle,
sweepAngle,
false,
mPaint);
mPaint.setColor(mLeftColor);
canvas.drawArc(left,
top,
right,
bottom,
startAngle
-
120,
sweepAngle,
false,
mPaint);
mPaint.setColor(mRightColor);
canvas.drawArc(left,
top,
right,
bottom,
startAngle
+
120,
sweepAngle,
false,
mPaint);動(dòng)畫的基礎(chǔ)是在onDraw中,依次繪制三種不同顏色的圓弧。三段圓弧每每相隔120度,這樣就可以剛好平分整個(gè)圓,比較美觀。注意這里的startAngle的初始值是-90,剛好是圓的最上面一點(diǎn)。這里需要注意的是canvas的drawArc方法中,前四個(gè)參數(shù)是決定圓弧的位置的矩形的坐標(biāo),startAngle指的是圓弧開始的角度,0度是圓的最右側(cè)的點(diǎn),以順時(shí)針為正、逆時(shí)針為負(fù)。所以-90度剛好是圓的最上面的點(diǎn)。sweepAngle是指圓弧掃過的角度,同樣順時(shí)針為正,逆時(shí)針為負(fù)。這里sweepAngle的大小初始值是-1,這樣在動(dòng)畫未開始之前也能夠繪制出一個(gè)圓點(diǎn)(實(shí)際上是角度為1的圓弧,近似圓點(diǎn))。后面一個(gè)參數(shù)是useCenter,指的是是否使用圓心,為true時(shí)就會(huì)將圓弧的兩個(gè)端點(diǎn)連向圓心構(gòu)成一個(gè)扇形,為false時(shí)則不會(huì)連接圓心。另外要注意paint的style要設(shè)置為stroke,默認(rèn)情況下是fill模式,也就是會(huì)直接填充。對(duì)于這里的圓弧,會(huì)直接連接圓弧的兩個(gè)端點(diǎn)構(gòu)成閉合圖形然后進(jìn)行填充。從上面也可以看出,要繪制圓弧必須要有四個(gè)坐標(biāo),這里的坐標(biāo)是以這種方式得到的:以View的長寬中最短的一邊作為組成圓的正方形的邊長,然后居中顯示。int
width
=
getMeasuredWidth();
int
height
=
getMeasuredHeight();
int
side
=
Math.min(width
-
getPaddingStart()
-
getPaddingEnd(),
height
-
getPaddingTop()
-
getPaddingBottom())
-
(int)
(mStrokeWidth
+
0.5F);
//
確定動(dòng)畫位置
float
left
=
(width
-
side)
/
2F;
float
top
=
(height
-
side)
/
2F;
float
right
=
left
+
side;
float
bottom
=
top
+
side;上面的一段代碼就是定位圓弧的正方形坐標(biāo)的實(shí)現(xiàn),這里可以看到在計(jì)算邊長side的時(shí)候,去掉了view的padding和mStrokenWidth。其中mStrokenWidth是圓弧的弧線的寬度,由于圓弧的線較寬的時(shí)候(此時(shí)相當(dāng)于圓環(huán))會(huì)向內(nèi)外均勻延伸,也就是內(nèi)邊距和外邊距的中間到圓心的距離才是半徑。因此在確定圓弧的位置時(shí),要去除線寬,以防止在交界處圓弧無法完全繪制。另外,我們自定義View時(shí),默認(rèn)的wrap_content模式下會(huì)與match_parent的效果一樣,因此需要在onMeasure中進(jìn)行處理。這里就簡單的設(shè)置wrap_content模式下為20dp。@Override
protected
void
onMeasure(int
widthMeasureSpec,
int
heightMeasureSpec)
{
super.onMeasure(widthMeasureSpec,
heightMeasureSpec);
int
widthMode
=
MeasureSpec.getMode(widthMeasureSpec);
int
heightMode
=
MeasureSpec.getMode(heightMeasureSpec);
int
width
=
MeasureSpec.getSize(widthMeasureSpec);
int
height
=
MeasureSpec.getSize(heightMeasureSpec);
//
對(duì)于wrap_content
,設(shè)置其為20dp。默認(rèn)情況下wrap_content和match_parent是一樣的效果
if
(widthMode
==
MeasureSpec.AT_MOST)
{
width
=
(int)
(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
20,
getContext().getResources().getDisplayMetrics())
+
0.5F);
}
if
(heightMode
==
MeasureSpec.AT_MOST)
{
height
=
(int)
(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
20,
getContext().getResources().getDisplayMetrics())
+
0.5F);
}
setMeasuredDimension(width,
height);
}以上的操作就是動(dòng)畫的整個(gè)基礎(chǔ),而讓View動(dòng)起來的操作就是不斷地修改圓弧的startAngle和sweepAngle,然后觸發(fā)View的重繪。這個(gè)過程使用ValueAnimator來生成一系列數(shù)字,然后根據(jù)這個(gè)來計(jì)算圓弧的開始角度和掃描角度。//
最小角度為1度,是為了顯示小圓點(diǎn)
sweepAngle
=
-1;
startAngle
=
-90;
curStartAngle
=
startAngle;
//
擴(kuò)展動(dòng)畫
mValueAnimator
=
ValueAnimator.ofFloat(0,
1).setDuration(mDuration);
mValueAnimator.setRepeatMode(ValueAnimator.REVERSE);
mValueAnimator.setRepeatCount(ValueAnimator.INFINITE);
mValueAnimator.addUpdateListener(animation
->
{
float
fraction
=
animation.getAnimatedFraction();
float
value
=
(float)
animation.getAnimatedValue();
if
(mReverse)
fraction
=
1
-
fraction;
startAngle
=
curStartAngle
+
fraction
*
120;
sweepAngle
=
-1
-
mMaxSweepAngle
*
value;
postInvalidate();
});
mValueAnimator.addListener(new
AnimatorListenerAdapter()
{
@Override
public
void
onAnimationRepeat(Animator
animation)
{
curStartAngle
=
startAngle;
mReverse
=
!mReverse;
}
});上面就是計(jì)算的過程,該動(dòng)畫采用的是value值從0到1再到0,對(duì)應(yīng)著其中一段圓弧從原點(diǎn)伸展到最大再縮小回原點(diǎn)。其中sweepAngle的計(jì)算是sweepAngle=-1-mMaxSweepAngle*value,也就是在整個(gè)過程中,圓弧的角度逐漸增大到maxSweepAngle。這里采用的是負(fù)值,也就是從startAngle按逆時(shí)針方向進(jìn)行繪制。-1是基礎(chǔ)值,以防止縮小到最小時(shí)也能夠顯示出一個(gè)圓點(diǎn)。startAngle的計(jì)算則是根據(jù)動(dòng)畫過程的fraction,而不是動(dòng)畫值,也就是從0到1,在整個(gè)動(dòng)畫過程中逐漸增加120度。由于整個(gè)View是由三段相同的圓弧形成的,也就是說每段圓弧最大只能占據(jù)120度,否則就會(huì)重疊。那么在0到1這個(gè)過程中,弧度增大到120度,startAngle則必須移動(dòng)120度給圓弧騰出位置,這就是120度的由來。并且監(jiān)聽Reverse狀態(tài),因?yàn)樵赗everse狀態(tài)下,fraction是從1到0的,而我們需要的是startAngle一直逐漸增大,因此在Reverse下通過1-fraction使之與原動(dòng)畫一致。并且每次reverse監(jiān)聽下,記錄startAngle作為新的當(dāng)前位置和記錄reverse狀態(tài)。以上就是整個(gè)圓弧動(dòng)畫的實(shí)現(xiàn)細(xì)節(jié)了,整體比較簡單,就是通過對(duì)弧度的startAngle和sweepAngle進(jìn)行改變?nèi)缓笸ㄖ猇iew重繪。下面是實(shí)現(xiàn)的完整代碼,這里抽取了一些基礎(chǔ)變量放到屬性中,用于簡便控制動(dòng)畫的顯示:values/attrs.xml<?xml
version="1.0"
encoding="utf-8"?>
<resources>
<declare-styleable
name="RefreshView">
<attr
name="top_color"
format="color"/>
<attr
name="left_color"
format="color"/>
<attr
name="right_color"
format="color"/>
<!--
圓弧的寬度
-->
<attr
name="border_width"
format="dimension"/>
<!--
每個(gè)周期的時(shí)間,從點(diǎn)到最大弧為一個(gè)周期,ms
-->
<attr
name="duration"
format="integer"/>
<!--
圓弧掃過的最大角度
-->
<attr
name="max_sweep_angle"
format="integer"/>
<!--
是否自動(dòng)開啟動(dòng)畫
-->
<attr
name="auto_start"
format="boolean"/>
</declare-styleable>
</resources>RefreshView.javapackage
com.pgaofeng.mytest.other;
import
android.animation.Animator;
import
android.animation.AnimatorListenerAdapter;
import
android.animation.ValueAnimator;
import
android.content.Context;
import
android.content.res.TypedArray;
import
android.graphics.Canvas;
import
android.graphics.Color;
import
android.graphics.Paint;
import
android.support.annotation.Nullable;
import
android.util.AttributeSet;
import
android.util.TypedValue;
import
android.view.View;
import
com.pgaofeng.mytest.R;
/**
*
@author
gaofengpeng
*
@date
2019/9/16
*
@description
:
*/
public
class
RefreshView
extends
View
{
/**
*
動(dòng)畫的三種顏色
*/
private
int
mTopColor;
private
int
mLeftColor;
private
int
mRightColor;
private
Paint
mPaint;
/**
*
掃描角度,用于控制圓弧的長度
*/
private
float
sweepAngle;
/**
*
開始角度,用于控制圓弧的顯示位置
*/
private
float
startAngle;
/**
*
當(dāng)前角度,記錄圓弧旋轉(zhuǎn)的角度
*/
private
float
curStartAngle;
/**
*
用動(dòng)畫控制圓弧顯示
*/
private
ValueAnimator
mValueAnimator;
/**
*
每個(gè)周期的時(shí)長
*/
private
int
mDuration;
/**
*
圓弧線寬
*/
private
float
mStrokeWidth;
/**
*
動(dòng)畫過程中最大的圓弧角度
*/
private
int
mMaxSweepAngle;
/**
*
是否自動(dòng)開啟動(dòng)畫
*/
private
boolean
mAutoStart;
/**
*
用于判斷當(dāng)前動(dòng)畫是否處于Reverse狀態(tài)
*/
private
boolean
mReverse
=
false;
public
RefreshView(Context
context)
{
this(context,
null);
}
public
RefreshView(Context
context,
@Nullable
AttributeSet
attrs)
{
this(context,
attrs,
0);
}
public
RefreshView(Context
context,
@Nullable
AttributeSet
attrs,
int
defStyleAttr)
{
super(context,
attrs,
defStyleAttr);
initAttr(context,
attrs);
init();
}
private
void
initAttr(Context
context,
AttributeSet
attrs)
{
TypedArray
array
=
context.obtainStyledAttributes(attrs,
R.styleable.RefreshView);
mTopColor
=
array.getColor(R.styleable.RefreshView_top_color,
Color.BLUE);
mLeftColor
=
array.getColor(R.styleable.RefreshView_left_color,
Color.YELLOW);
mRightColor
=
array.getColor(R.styleable.RefreshView_right_color,
Color.RED);
mDuration
=
array.getInt(R.styleable.RefreshView_duration,
600);
if
(mDuration
<=
0)
{
mDuration
=
600;
}
mStrokeWidth
=
array.getDimension(R.styleable.RefreshView_border_width,
8F);
mMaxSweepAngle
=
array.getInt(R.styleable.RefreshView_max_sweep_angle,
90);
if
(mMaxSweepAngle
<=
0
||
mMaxSweepAngle
>
120)
{
//
對(duì)于不規(guī)范值直接采用默認(rèn)值
mMaxSweepAngle
=
90;
}
mAutoStart
=
array.getBoolean(R.styleable.RefreshView_auto_start,
true);
array.recycle();
}
private
void
init()
{
mPaint
=
new
Paint();
mPaint.setAntiAlias(true);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(mStrokeWidth);
mPaint.setStrokeCap(Paint.Cap.ROUND);
//
最小角度為1度,是為了顯示小圓點(diǎn)
sweepAngle
=
-1;
startAngle
=
-90;
curStartAngle
=
startAngle;
//
擴(kuò)展動(dòng)畫
mValueAnimator
=
ValueAnimator.ofFloat(0,
1).setDuration(mDuration);
mValueAnimator.setRepeatMode(ValueAnimator.REVERSE);
mValueAnimator.setRepeatCount(ValueAnimator.INFINITE);
mValueAnimator.addUpdateListener(animation
->
{
float
fraction
=
animation.getAnimatedFraction();
float
value
=
(float)
animation.getAnimatedValue();
if
(mReverse)
fraction
=
1
-
fraction;
startAngle
=
curStartAngle
+
fraction
*
120;
sweepAngle
=
-1
-
mMaxSweepAngle
*
value;
postInvalidate();
});
mValueAnimator.addListener(new
AnimatorListenerAdapter()
{
@Override
public
void
onAnimationRepeat(Animator
animation)
{
curStartAngle
=
startAngle;
mReverse
=
!mReverse;
}
});
}
@Override
protected
void
onMeasure(int
widthMeasureSpec,
int
heightMeasureSpec)
{
super.onMeasure(widthMeasureSpec,
heightMeasureSpec);
int
widthMode
=
MeasureSpec.getMode(widthMeasureSpec);
int
heightMode
=
MeasureSpec.getMode(heightMeasureSpec);
int
width
=
MeasureSpec.getSize(widthMeasureSpec);
int
height
=
MeasureSpec.getSize(heightMeasureSpec);
//
對(duì)于wrap_content
,設(shè)置其為20dp。默認(rèn)情況下wrap_content和match_parent是一樣的效果
if
(widthMode
==
MeasureSpec.AT_MOST)
{
width
=
(int)
(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
20,
getContext().getResources().getDisplayMetrics())
+
0.5F);
}
if
(heightMode
==
MeasureSpec.AT_MOST)
{
height
=
(int)
(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
20,
getContext().getResources().getDisplayMetrics())
+
0.5F);
}
setMeasuredDimension(width,
height);
}
@Override
protected
void
onDraw(Canvas
canvas)
{
super.onDraw(canvas);
int
width
=
getMeasuredWidth();
int
height
=
getMeasuredHeight();
int
side
=
Math.min(width
-
getPaddingStart()
-
getPaddingEnd(),
height
-
getPaddingTop()
-
getPaddingBottom())
-
(int)
(mStrokeWidth
+
0.5F);
//
確定動(dòng)畫位置
float
left
=
(width
-
side)
/
2F;
float
top
=
(height
-
side)
/
2F;
float
right
=
left
+
side;
float
bottom
=
top
+
side;
//
繪制圓弧
mPaint.setColor(mTopColor);
canvas.drawArc(left,
top,
right,
bottom,
startAngle,
sweepAngle,
false,
mPaint);
mPai
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 總經(jīng)理工作總結(jié)報(bào)告
- 2025年建筑考試-建筑水電檢測歷年參考題庫含答案解析(5套典型考題)
- 2025年建筑水利市政公路三類人員-黑龍江建筑三類人員考試歷年參考題庫含答案解析(5套典型考題)
- 2025年大學(xué)試題(計(jì)算機(jī)科學(xué))-通信工程設(shè)計(jì)歷年參考題庫含答案解析(5套典型考題)
- 2025年大學(xué)試題(計(jì)算機(jī)科學(xué))-verilog歷年參考題庫含答案解析(5套典型考題)
- 臨床輸血病例分析
- 2025年大學(xué)試題(管理類)-市政學(xué)歷年參考題庫含答案解析(5套典型考題)
- 2025年大學(xué)試題(汽車專業(yè))-汽車?yán)碚摎v年參考題庫含答案解析(5套典型考題)
- 2025年大學(xué)試題(政治學(xué))-馬克思主義歷年參考題庫含答案解析(5套典型考題)
- 2025年大學(xué)試題(歷史學(xué))-中國教育史歷年參考題庫含答案解析(5套典型考題)
- DZ∕T 0390-2021 區(qū)域地質(zhì)調(diào)查數(shù)字填圖技術(shù)規(guī)程(正式版)
- 《公路交通安全設(shè)施施工技術(shù)規(guī)范》(JTG-T3671-2021)
- 臨床醫(yī)學(xué)檢驗(yàn):造血檢驗(yàn)試題及答案真題
- 金屬非金屬礦山重大事故隱患排查表
- (高清版)TDT 1063-2021 國土空間規(guī)劃城市體檢評(píng)估規(guī)程
- JGT366-2012 外墻保溫用錨栓
- 靜電接地培訓(xùn)課件
- 2020年高考全國乙卷英語試卷
- 常見動(dòng)物致傷診療規(guī)范
- (36)-外部強(qiáng)制對(duì)流傳熱實(shí)驗(yàn)關(guān)聯(lián)式
- 移動(dòng)機(jī)器人SLAM技術(shù) 課件 【ch05】移動(dòng)機(jī)器人路徑規(guī)劃
評(píng)論
0/150
提交評(píng)論