欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android自定義View實現(xiàn)圓形漸變多點的加載框效果

 更新時間:2025年09月14日 14:35:23   作者:夏沫瑯琊  
自定義View是Android開發(fā)中的一項重要技術,允許開發(fā)者創(chuàng)建滿足特定需求的圖形用戶界面,這篇文章主要介紹了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ù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • Kotlin協(xié)程概念原理與使用萬字梳理

    Kotlin協(xié)程概念原理與使用萬字梳理

    協(xié)程的作用是什么?協(xié)程是一種輕量級的線程,解決異步編程的復雜性,異步的代碼使用協(xié)程可以用順序進行表達,文中通過示例代碼介紹詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習吧
    2022-08-08
  • Android畢業(yè)設計備忘錄APP

    Android畢業(yè)設計備忘錄APP

    這篇文章主要介紹了一個Android畢業(yè)設計備忘錄APP,它很小,但是功能很全,可實現(xiàn)添加、刪除、修改、查看的功能,使用Java語言開發(fā),風格簡練
    2021-08-08
  • Android ToolBar 修改邊距的實現(xiàn)方法

    Android ToolBar 修改邊距的實現(xiàn)方法

    這篇文章主要介紹了Android ToolBar 修改邊距的實現(xiàn)方法的相關資料,通過此文希望能幫助到大家,需要的朋友可以參考下
    2017-08-08
  • Android 中View.onDraw(Canvas canvas)的使用方法

    Android 中View.onDraw(Canvas canvas)的使用方法

    這篇文章主要介紹了Android 中View.onDraw(Canvas canvas)的使用方法的相關資料,希望通過本文能幫助到大家,需要的朋友可以參考下
    2017-09-09
  • android屏蔽按鈕連續(xù)點擊的示例代碼

    android屏蔽按鈕連續(xù)點擊的示例代碼

    這篇文章主要介紹了android屏蔽按鈕連續(xù)點擊的示例代碼,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-12-12
  • Android實用編程技巧代碼總結(jié)

    Android實用編程技巧代碼總結(jié)

    這篇文章主要介紹了Android實用編程技巧代碼,總結(jié)分析了Android編程中常見的實用代碼段,包括圖片、文本、控件等常用操作技巧,需要的朋友可以參考下
    2016-10-10
  • Android生存指南之:開發(fā)中的注意事項

    Android生存指南之:開發(fā)中的注意事項

    本篇文章是對在Android開發(fā)中的一些注意事項,需要的朋友可以參考下
    2013-05-05
  • DataBinding onClick的七種點擊方式

    DataBinding onClick的七種點擊方式

    這篇文章主要給大家介紹了關于DataBinding onClick的七種點擊方式,文中通過示例代碼介紹的非常詳細,對各位Android開發(fā)者們具有一定的參考學習價值,需要的朋友們下面來一起學習學習吧
    2019-07-07
  • Android開源庫自定義相機模塊

    Android開源庫自定義相機模塊

    這篇文章主要為大家詳細介紹了Android開源庫自定義相機模塊,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-05-05
  • Android截取指定View為圖片的實現(xiàn)方法

    Android截取指定View為圖片的實現(xiàn)方法

    這篇文章主要為大家詳細介紹了Android截取指定View為圖片的實現(xiàn)方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-06-06

最新評論