Android實(shí)現(xiàn)啟動(dòng)頁(yè)倒計(jì)時(shí)效果
今天介紹一個(gè)很簡(jiǎn)單的倒計(jì)時(shí)動(dòng)畫(huà),仿酷狗音樂(lè)的啟動(dòng)頁(yè)倒計(jì)時(shí)效果,也是大多數(shù)APP在用的一個(gè)動(dòng)畫(huà),來(lái)看看效果圖:

整體的思路就是用一個(gè)平滑的幀動(dòng)畫(huà)來(lái)畫(huà)圓弧就行了。
這篇文章學(xué)到什么?
了解屬性動(dòng)畫(huà)ValueAnimator的用法
了解動(dòng)畫(huà)屬性插值Interpolator,讓動(dòng)畫(huà)過(guò)度得更自然
如何畫(huà)圓弧
開(kāi)始準(zhǔn)備
新建一個(gè)類繼承TextView,因?yàn)橹虚g還有跳過(guò)的文本,所以選擇用TextView來(lái)畫(huà)個(gè)動(dòng)起來(lái)的背景圖。
/**
* 倒計(jì)時(shí)文本
*/
@SuppressLint("AppCompatCustomView")
public class CountDownTextView extends TextView {
// 倒計(jì)時(shí)動(dòng)畫(huà)時(shí)間
private int duration = 5000;
// 動(dòng)畫(huà)掃過(guò)的角度
private int mSweepAngle = 360;
// 屬性動(dòng)畫(huà)
private ValueAnimator animator;
// 矩形用來(lái)保存位置大小信息
private final RectF mRect = new RectF();
// 圓弧的畫(huà)筆
private Paint mBackgroundPaint;
public CountDownTextView(Context context) {
this(context, null);
}
public CountDownTextView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CountDownTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
// 設(shè)置畫(huà)筆平滑
mBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
// 設(shè)置畫(huà)筆顏色
mBackgroundPaint.setColor(Color.WHITE);
// 設(shè)置畫(huà)筆邊框?qū)挾?
mBackgroundPaint.setStrokeWidth(5);
// 設(shè)置畫(huà)筆樣式為邊框類型
mBackgroundPaint.setStyle(Paint.Style.STROKE);
}
}
開(kāi)始動(dòng)畫(huà)
原理:利用圓的360度角來(lái)做屬性動(dòng)畫(huà),讓它平滑的分配做每幀動(dòng)畫(huà)的角度值,然后調(diào)用invalidate()來(lái)重繪自己本身,從而進(jìn)入到本身的onDraw()方法來(lái)畫(huà)圖。
/**
* 開(kāi)始倒計(jì)時(shí)
*/
public void start() {
// 在動(dòng)畫(huà)中
if (mSweepAngle != 360) return;
// 初始化屬性動(dòng)畫(huà)
animator = ValueAnimator.ofInt(mSweepAngle).setDuration(duration);
// 設(shè)置插值
animator.setInterpolator(new LinearInterpolator());
// 設(shè)置動(dòng)畫(huà)監(jiān)聽(tīng)
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
// 獲取屬性動(dòng)畫(huà)返回的動(dòng)畫(huà)值
mSweepAngle = (int) animation.getAnimatedValue();
// 重繪自己
invalidate();
}
});
// 開(kāi)始動(dòng)畫(huà)
animator.start();
}
畫(huà)圓弧
畫(huà)圓弧比較簡(jiǎn)單, 從效果圖來(lái)看,有的同學(xué)可能剛開(kāi)始以為要畫(huà)兩個(gè)圓,一個(gè)背景的內(nèi)圓和一個(gè)白色邊框的大圓,其實(shí)這里可以利用畫(huà)筆設(shè)置畫(huà)筆樣式paint.setStyle()和寬度大小paint.setStrokeWidth()的特性來(lái)實(shí)現(xiàn)。代碼很簡(jiǎn)單,開(kāi)始的角度選擇-90,從頭頂開(kāi)始畫(huà)。這樣實(shí)現(xiàn)的是一個(gè)順時(shí)針的倒計(jì)時(shí)效果。如果你想實(shí)現(xiàn)酷狗的逆時(shí)針效果,就控制mSweepAngle的值用mSweepAngle = 360 - mSweepAngle開(kāi)始就可以了。
@Override
protected void onDraw(Canvas canvas) {
int padding = dp2px(4);
mRect.top = padding;
mRect.left = padding;
mRect.right = getWidth() - padding;
mRect.bottom = getHeight() - padding;
// 畫(huà)倒計(jì)時(shí)線內(nèi)圓
canvas.drawArc(mRect, //弧線所使用的矩形區(qū)域大小
-90, //開(kāi)始角度
mSweepAngle, //掃過(guò)的角度
false, //是否使用中心
mBackgroundPaint); // 設(shè)置畫(huà)筆
super.onDraw(canvas);
}
什么是插值動(dòng)畫(huà)?
為了讓動(dòng)畫(huà)過(guò)度的更加自然或者添加一些動(dòng)畫(huà)效果,比如勻速運(yùn)動(dòng)、加速運(yùn)動(dòng)、減速運(yùn)動(dòng)、彈跳運(yùn)動(dòng)等等,這些的動(dòng)畫(huà)的效果就是靠插值來(lái)實(shí)現(xiàn)的。在Android中系統(tǒng)內(nèi)置了一些插值,更加直觀的展示下面介紹的動(dòng)畫(huà)效果。
| 插值 | 說(shuō)明 |
|---|---|
| LinearInterpolator | 以常量速率改變 |
| BounceInterpolator | 動(dòng)畫(huà)結(jié)束的時(shí)候彈起 |
| CycleInterpolator | 動(dòng)畫(huà)循環(huán)播放特定的次數(shù),速率改變沿著正弦曲線 |
| DecelerateInterpolator | 在動(dòng)畫(huà)開(kāi)始的地方快然后慢 |
| OvershootInterpolator | 向前甩一定值后再回到原來(lái)位置 |
| AccelerateInterpolator | 在動(dòng)畫(huà)開(kāi)始的地方速率改變比較慢,然后開(kāi)始加速 |
| AnticipateInterpolator | 開(kāi)始的時(shí)候向后然后向前甩 |
| AccelerateDecelerateInterpolator | 在動(dòng)畫(huà)開(kāi)始與介紹的地方速率改變比較慢,在中間的時(shí)候加速 |
| AnticipateOvershootInterpolator | 開(kāi)始的時(shí)候向后然后向前甩一定值后返回最后的值 |
項(xiàng)目使用
這里要定義文本的寬高,因?yàn)闆](méi)有畫(huà)底部的黑色圓背景,還要設(shè)置一下背景圖。
<com.example.viewdemo.CountDownTextView
android:id="@+id/tv_skip"
style="@style/Widget.AppCompat.Button.Borderless"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_gravity="center"
android:background="@drawable/bg_count_down"
android:text="跳過(guò)"
android:textColor="#ffffff"
android:textSize="12sp"
android:visibility="visible" />
背景圖
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true">
<shape android:shape="oval">
<solid android:color="#302d2d2d" />
</shape>
</item>
<item>
<shape android:shape="oval">
<solid android:color="#7F2d2d2d" />
</shape>
</item>
</selector>
完整代碼
/**
* 倒計(jì)時(shí)文本
*/
@SuppressLint("AppCompatCustomView")
public class CountDownTextView extends TextView {
// 倒計(jì)時(shí)動(dòng)畫(huà)時(shí)間
private int duration = 5000;
// 動(dòng)畫(huà)掃過(guò)的角度
private int mSweepAngle = 360;
// 屬性動(dòng)畫(huà)
private ValueAnimator animator;
// 矩形用來(lái)保存位置大小信息
private final RectF mRect = new RectF();
// 圓弧的畫(huà)筆
private Paint mBackgroundPaint;
public CountDownTextView(Context context) {
this(context, null);
}
public CountDownTextView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CountDownTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
@Override
protected void onDraw(Canvas canvas) {
int padding = 5;
mRect.top = padding;
mRect.left = padding;
mRect.right = getWidth() - padding;
mRect.bottom = getHeight() - padding;
// 畫(huà)倒計(jì)時(shí)線內(nèi)圓
canvas.drawArc(mRect, //弧線所使用的矩形區(qū)域大小
-90, //開(kāi)始角度
mSweepAngle, //掃過(guò)的角度
false, //是否使用中心
mBackgroundPaint); // 設(shè)置畫(huà)筆
start();
super.onDraw(canvas);
}
private void init() {
// 設(shè)置畫(huà)筆平滑
mBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
// 設(shè)置畫(huà)筆顏色
mBackgroundPaint.setColor(Color.WHITE);
// 設(shè)置畫(huà)筆邊框?qū)挾?
mBackgroundPaint.setStrokeWidth(5);
// 設(shè)置畫(huà)筆樣式為邊框類型
mBackgroundPaint.setStyle(Paint.Style.STROKE);
}
/**
* 開(kāi)始倒計(jì)時(shí)
*/
public void start() {
// 在動(dòng)畫(huà)中
if (mSweepAngle != 360) return;
// 初始化屬性動(dòng)畫(huà)
animator = ValueAnimator.ofInt(mSweepAngle).setDuration(duration);
// 設(shè)置插值
animator.setInterpolator(new LinearInterpolator());
// 設(shè)置動(dòng)畫(huà)監(jiān)聽(tīng)
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
// 獲取屬性動(dòng)畫(huà)返回的動(dòng)畫(huà)值
mSweepAngle = (int) animation.getAnimatedValue();
// 重繪自己
invalidate();
}
});
// 開(kāi)始動(dòng)畫(huà)
animator.start();
}
}
以上就是Android實(shí)現(xiàn)啟動(dòng)頁(yè)倒計(jì)時(shí)效果的詳細(xì)內(nèi)容,更多關(guān)于Android 啟動(dòng)頁(yè)倒計(jì)時(shí)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android studio實(shí)現(xiàn)加法軟件
這篇文章主要為大家詳細(xì)介紹了Android studio實(shí)現(xiàn)加法軟件,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-03-03
Android利用Flutter實(shí)現(xiàn)立體旋轉(zhuǎn)效果
本文主要介紹了Flutter繪圖如何使用ImageShader填充圖形,并且利用 Matrix4的三維變換加上動(dòng)畫(huà)實(shí)現(xiàn)了立體旋轉(zhuǎn)的動(dòng)畫(huà)效果,感興趣的可以嘗試一下2022-06-06
Android UI控件之ProgressBar進(jìn)度條
這篇文章主要為大家詳細(xì)介紹了Android UI控件之ProgressBar進(jìn)度條的實(shí)現(xiàn)代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-12-12
Android RecycleView 實(shí)現(xiàn)左滑上下分層示例代碼(自定義功能)
這篇文章主要介紹了Android RecycleView 實(shí)現(xiàn)左滑上下分層示例代碼(自定義功能),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2017-03-03
Android判斷用戶是否允許了攝像頭權(quán)限實(shí)例代碼
本篇文章主要介紹了Android判斷用戶是否允許了攝像頭權(quán)限實(shí)例代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-04-04
Android小程序?qū)崿F(xiàn)選項(xiàng)菜單
這篇文章主要為大家詳細(xì)介紹了Android小程序?qū)崿F(xiàn)選項(xiàng)菜單,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-05-05
Android編程實(shí)現(xiàn)簡(jiǎn)單設(shè)置按鈕顏色的方法
這篇文章主要介紹了Android編程實(shí)現(xiàn)簡(jiǎn)單設(shè)置按鈕顏色的方法,涉及Android控件布局與屬性設(shè)置相關(guān)操作技巧,需要的朋友可以參考下2017-03-03

