Android自定義View實現(xiàn)圓形漸變多點的加載框效果
本文主要記錄創(chuàng)建一個 Android 自定義加載彈窗,實現(xiàn)指定個數(shù)且從小到大的實心圓周期性旋轉(zhuǎn)的效果。
附上效果如下:

一: 自定義圓形loadingView
1.1 核心屬性定義
//默認參數(shù)
private int circleCount = 8; // 圓圈數(shù)量
private int minCircleRadius = 5; // 最小圓圈半徑
private int maxCircleRadius = 15; // 最大圓圈半徑
private int circleColor = Color.parseColor("#3F51B5"); //默認顏色
這些屬性控制了加載動畫的外觀:圓的數(shù)量、大小范圍、顏色以及動畫實例。
1.2 構(gòu)造方法
public CircleLoadingView(Context context) {
super(context);
init(null);
}
public CircleLoadingView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(attrs);
}
public CircleLoadingView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(attrs);
}
- 實現(xiàn)了 3 個構(gòu)造方法,覆蓋了代碼創(chuàng)建和 XML 布局引用兩種場景
- 所有構(gòu)造方法最終都調(diào)用
init()方法完成初始化,符合 Android 自定義 View 的最佳實踐
1.3 初始化方法init()
private void init(AttributeSet attrs) {
if (attrs != null){
TypedArray ta = getContext().obtainStyledAttributes(attrs, R.styleable.CircleLoadingView);
circleCount = ta.getInt(R.styleable.CircleLoadingView_circleCount, circleCount);
minCircleRadius = ta.getInt(R.styleable.CircleLoadingView_minCircleRadius, minCircleRadius);
maxCircleRadius = ta.getInt(R.styleable.CircleLoadingView_maxCircleRadius, maxCircleRadius);
circleColor = ta.getColor(R.styleable.CircleLoadingView_circleColor, circleColor);
ta.recycle();
}
paint = new Paint();
paint.setColor(circleColor);//畫筆顏色
paint.setStyle(Paint.Style.FILL);//填充 繪制實心圓
paint.setAntiAlias(true);//抗鋸齒
initAnimation();
}
- 自定義屬性處理:通過
TypedArray讀取 XML 中設置的自定義屬性(如圓的數(shù)量、顏色等),如果沒設置則使用默認值 - 畫筆初始化:配置繪制圓形的畫筆,設置為實心、抗鋸齒
- 動畫初始化:調(diào)用
initAnimation()方法創(chuàng)建旋轉(zhuǎn)動畫
1.4 動畫實現(xiàn)
// 創(chuàng)建旋轉(zhuǎn)動畫,3秒完成一圈,無限循環(huán)
private void initAnimation() {
rotateAnimation = new RotateAnimation(
0, 360,
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f
);
rotateAnimation.setDuration(3000);
rotateAnimation.setRepeatCount(Animation.INFINITE);
rotateAnimation.setInterpolator(new LinearInterpolator());
startAnimation(rotateAnimation);
}
1.5 繪制
這是自定義 View 的核心方法,負責繪制所有圓形:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 獲取視圖中心坐標
int centerX = getWidth() / 2;
int centerY = getHeight() / 2;
// 計算可用的半徑(視圖最小邊的一半減去最大圓半徑)
int availableRadius = Math.min(centerX, centerY) - maxCircleRadius;
// 繪制每個圓
for (int i = 0; i < circleCount; i++) {
// 計算每個圓的角度(等分圓周,從頂部開始)
// 減去90度(Math.PI/2)使第一個點位于頂部
float angle = (float) (2 * Math.PI * i / circleCount - Math.PI / 2);
// 計算當前圓的半徑(從小到大漸變)
float radius = minCircleRadius;
if (circleCount > 1) {
radius = minCircleRadius + (maxCircleRadius - minCircleRadius) * (float) i / (circleCount - 1);
}
// 計算圓的中心點坐標(均勻分布在圓形軌跡上)
float x = centerX + (float) (availableRadius * Math.cos(angle));
float y = centerY + (float) (availableRadius * Math.sin(angle));
// 設置畫筆顏色(可選:可以根據(jù)位置設置不同的透明度)
paint.setAlpha(calculateAlpha(i));
// 繪制圓
canvas.drawCircle(x, y, radius, paint);
}
}
/**
* 根據(jù)圓點位置計算透明度,創(chuàng)建漸變效果
* @param index 圓點索引
* @return 透明度值(0-255)
*/
private int calculateAlpha(int index) {
// 可以根據(jù)需要調(diào)整透明度計算邏輯
// 這里創(chuàng)建一個漸變效果,第一個點最暗,最后一個點較亮
if (circleCount <= 1) return 255;
// 計算透明度,
return 155 + (index * 100 / (circleCount - 1));
}
1.6: 尺寸測量
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// 設置默認大小
int defaultSize = dp2px(80);
int width = measureSize(defaultSize, widthMeasureSpec);
int height = measureSize(defaultSize, heightMeasureSpec);
setMeasuredDimension(width, height);
}
private int measureSize(int defaultSize, int measureSpec) {
int result = defaultSize;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
if (specMode == MeasureSpec.EXACTLY) {
result = specSize;
} else if (specMode == MeasureSpec.AT_MOST) {
result = Math.min(defaultSize, specSize);
}
return result;
}
// dp轉(zhuǎn)px
private int dp2px(float dpValue) {
final float scale = getContext().getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
- 處理 View 的尺寸測量,支持
wrap_content、match_parent和固定尺寸 - 默認大小為 80dp,通過
dp2px()方法將 dp 轉(zhuǎn)為 px,適配不同屏幕密度
1.7 : attrs自定義參數(shù)
<!-- 自定義加載視圖的屬性 -->
<declare-styleable name="CircleLoadingView">
<attr name="circleCount" format="integer" />
<attr name="minCircleRadius" format="integer" />
<attr name="maxCircleRadius" format="integer" />
<attr name="circleColor" format="color" />
</declare-styleable>
目前定義了四個屬性包括圓點的個數(shù),顏色,最大最小的半徑. 有其他需求的我們可以繼續(xù)補充.
下面是完整的自定義View的代碼:
public class CircleLoadingView extends View {
private static final String TAG = "CircleLoadingView";
//默認參數(shù)
private int circleCount = 8; // 圓圈數(shù)量
private int minCircleRadius = 5; // 最小圓圈半徑
private int maxCircleRadius = 15; // 最大圓圈半徑
private int circleColor = Color.parseColor("#3F51B5"); //默認顏色
// 旋轉(zhuǎn)動畫
private RotateAnimation rotateAnimation;
// 畫筆
private Paint paint;
public CircleLoadingView(Context context) {
super(context);
init(null);
}
public CircleLoadingView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(attrs);
}
public CircleLoadingView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(attrs);
}
private void init(AttributeSet attrs) {
if (attrs != null){
TypedArray ta = getContext().obtainStyledAttributes(attrs, R.styleable.CircleLoadingView);
circleCount = ta.getInt(R.styleable.CircleLoadingView_circleCount, circleCount);
minCircleRadius = ta.getInt(R.styleable.CircleLoadingView_minCircleRadius, minCircleRadius);
maxCircleRadius = ta.getInt(R.styleable.CircleLoadingView_maxCircleRadius, maxCircleRadius);
circleColor = ta.getColor(R.styleable.CircleLoadingView_circleColor, circleColor);
ta.recycle();
}
paint = new Paint();
paint.setColor(circleColor);//畫筆顏色
paint.setStyle(Paint.Style.FILL);//填充 繪制實心圓
paint.setAntiAlias(true);//抗鋸齒
initAnimation();
}
// 創(chuàng)建旋轉(zhuǎn)動畫,3秒完成一圈,無限循環(huán)
private void initAnimation() {
rotateAnimation = new RotateAnimation(
0, 360,
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f
);
rotateAnimation.setDuration(3000);
rotateAnimation.setRepeatCount(Animation.INFINITE);
rotateAnimation.setInterpolator(new LinearInterpolator());
startAnimation(rotateAnimation);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 獲取視圖中心坐標
int centerX = getWidth() / 2;
int centerY = getHeight() / 2;
// 計算可用的半徑(視圖最小邊的一半減去最大圓半徑)
int availableRadius = Math.min(centerX, centerY) - maxCircleRadius;
// 繪制每個圓
for (int i = 0; i < circleCount; i++) {
// 計算每個圓的角度(等分圓周,從頂部開始)
// 減去90度(Math.PI/2)使第一個點位于頂部
float angle = (float) (2 * Math.PI * i / circleCount - Math.PI / 2);
// 計算當前圓的半徑(從小到大漸變)
float radius = minCircleRadius;
if (circleCount > 1) {
radius = minCircleRadius + (maxCircleRadius - minCircleRadius) * (float) i / (circleCount - 1);
}
// 計算圓的中心點坐標(均勻分布在圓形軌跡上)
float x = centerX + (float) (availableRadius * Math.cos(angle));
float y = centerY + (float) (availableRadius * Math.sin(angle));
// 設置畫筆顏色(可選:可以根據(jù)位置設置不同的透明度)
paint.setAlpha(calculateAlpha(i));
// 繪制圓
canvas.drawCircle(x, y, radius, paint);
}
}
/**
* 根據(jù)圓點位置計算透明度,創(chuàng)建漸變效果
* @param index 圓點索引
* @return 透明度值(0-255)
*/
private int calculateAlpha(int index) {
// 可以根據(jù)需要調(diào)整透明度計算邏輯
// 這里創(chuàng)建一個漸變效果,第一個點最暗,最后一個點較亮
if (circleCount <= 1) return 255;
// 計算透明度,
return 155 + (index * 100 / (circleCount - 1));
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// 設置默認大小
int defaultSize = dp2px(80);
int width = measureSize(defaultSize, widthMeasureSpec);
int height = measureSize(defaultSize, heightMeasureSpec);
setMeasuredDimension(width, height);
}
private int measureSize(int defaultSize, int measureSpec) {
int result = defaultSize;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
if (specMode == MeasureSpec.EXACTLY) {
result = specSize;
} else if (specMode == MeasureSpec.AT_MOST) {
result = Math.min(defaultSize, specSize);
}
return result;
}
// dp轉(zhuǎn)px
private int dp2px(float dpValue) {
final float scale = getContext().getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
// 開始動畫
public void startLoading() {
if (rotateAnimation != null && rotateAnimation.hasEnded()) {
startAnimation(rotateAnimation);
}
}
// 停止動畫
public void stopLoading() {
if (rotateAnimation != null) {
clearAnimation();
}
}
// 更新圓點的顏色
public void setCircleColor(int color) {
this.circleColor = color;
paint.setColor(color);
invalidate();
}
}
二: 自定義Dialog.
2.1 代碼
這里我寫的很簡單, 直接集成DIalog. 界面也只有CircleLoadingView.
public class CircleDialog extends Dialog {
private static final String TAG = "CircleDialog";
private CircleLoadingView loadingView;
public CircleDialog(@NonNull Context context) {
super(context, R.style.LoadingDialogStyle);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_circle_dialog);
loadingView = findViewById(R.id.loading_view);
}
private void initWindow() {
Window window = getWindow();
if (window != null) {
// 設置窗口屬性
WindowManager.LayoutParams params = window.getAttributes();
// 設置窗口居中
params.gravity = Gravity.CENTER;
// 設置窗口寬高為包裹內(nèi)容
params.width = WindowManager.LayoutParams.WRAP_CONTENT;
params.height = WindowManager.LayoutParams.WRAP_CONTENT;
// 設置窗口背景透明度
params.dimAmount = 0.3f;
window.setAttributes(params);
// 設置窗口背景為透明
window.setBackgroundDrawableResource(android.R.color.transparent);
// 設置窗口是否有遮罩層
window.addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
}
// 設置點擊外部是否可取消
setCanceledOnTouchOutside(false);
}
// 設置加載圓圈的顏色
public void setCircleColor(int color) {
if (loadingView != null) {
loadingView.setCircleColor(color);
}
}
@Override
public void show() {
super.show();
if (loadingView != null) {
loadingView.startLoading();
}
}
@Override
public void dismiss() {
super.dismiss();
if (loadingView != null) {
loadingView.stopLoading();
}
}
}
2.2: layout布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical">
<com.test.accessbilitytest.view.CircleLoadingView
android:layout_width="80dp"
android:layout_height="80dp"
android:id="@+id/loading_view"
android:layout_gravity="center_horizontal"
app:circleCount="8"
app:minCircleRadius="10"
app:maxCircleRadius="16"
app:circleColor="#fffdbe32"
/>
</LinearLayout>
2.3: dialog的樣式
<!-- 加載彈窗樣式 -->
<style name="LoadingDialogStyle" parent="@android:style/Theme.Dialog">
<!-- 不顯示標題欄 -->
<item name="android:windowNoTitle">true</item>
<!-- 背景透明 -->
<item name="android:windowBackground">@android:color/transparent</item>
<!-- 窗口進入退出動畫 -->
<item name="android:windowAnimationStyle">@android:style/Animation.Dialog</item>
</style>
三: 彈框的特點.
1. 核心是CircleLoadingView自定義視圖,它負責繪制 8個從小到大的實心圓,并通過旋轉(zhuǎn)動畫實現(xiàn)周期性旋轉(zhuǎn)效果。
2. 通過自定義屬性,您可以輕松調(diào)整:
圓的數(shù)量(默認 8個)
最小圓半徑
最大圓半徑
圓的顏色
3. LoadingDialog封裝了彈窗的顯示邏輯,包括設置彈窗樣式、背景透明度等。
4. 使用方法簡單:
創(chuàng)建LoadingDialog實例
調(diào)用show()方法顯示
調(diào)用dismiss()方法隱藏
總結(jié)
到此這篇關于Android自定義View實現(xiàn)圓形漸變多點的加載框效果的文章就介紹到這了,更多相關Android自定義加載彈窗內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Android ToolBar 修改邊距的實現(xiàn)方法
這篇文章主要介紹了Android ToolBar 修改邊距的實現(xiàn)方法的相關資料,通過此文希望能幫助到大家,需要的朋友可以參考下2017-08-08
Android 中View.onDraw(Canvas canvas)的使用方法
這篇文章主要介紹了Android 中View.onDraw(Canvas canvas)的使用方法的相關資料,希望通過本文能幫助到大家,需要的朋友可以參考下2017-09-09

