Android 的自定义等待对话框

简介: 版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u010046908/article/details/50614383       最近,看了好多的APP的等待对话框,发现自己的太lower,于是就研究了一番,最后经过苦心努力,实现一个。
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u010046908/article/details/50614383


      最近,看了好多的APP的等待对话框,发现自己的太lower,于是就研究了一番,最后经过苦心努力,实现一个。

  1. 自定义一个LoadingIndicatorView(extends View )类
  2. 编写values/attrs.xml,在其中编写styleable和item等标签元素
  3. 在布局文件中LoadingIndicatorView使用自定义的属性(注意namespace)
  4. 在LoadingIndicatorView的构造方法中通过TypedArray获取
   描述就提供这些,一下是代码的展示,非常的详细。
  • 自定义属性的声明文件
<declare-styleable name="AVLoadingIndicatorView">
        <attr name="indicator">
            <flag name="BallSpinFadeLoader" value="22"/>
        </attr>
        <attr name="indicator_color" format="color"/>
    </declare-styleable>

<pre name="code" class="html">LoadingIndicatorView.java
 
  

import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Build;
import android.support.annotation.IntDef;
import android.util.AttributeSet;
import android.view.View;

import com.chni.lidong.androidtestdemo.R;


/**
 * Created by lidongon 2016/1/31
 *
 .BallSpinFadeLoader,
 *
 */
public class LoadingIndicatorView extends View {


    //indicators 指示器
    public static final int BallSpinFadeLoader=22;

    @IntDef(flag = true,
            value = {
                    BallSpinFadeLoader,
            })
    public @interface Indicator{}

    //Sizes (with defaults in DP)
    public static final int DEFAULT_SIZE=45;

    //attrs
    int mIndicatorId;
    int mIndicatorColor;

    Paint mPaint;

    BaseIndicatorController mIndicatorController;

    private boolean mHasAnimation;


    public LoadingIndicatorView(Context context) {
        super(context);
        init(null, 0);
    }

    public LoadingIndicatorView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(attrs, 0);
    }

    public LoadingIndicatorView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(attrs, defStyleAttr);
    }


    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public LoadingIndicatorView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init(attrs, defStyleAttr);
    }

    private void init(AttributeSet attrs, int defStyle) {
        /**
         *获取TypedArray(属性的集合)
         */
        TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.AVLoadingIndicatorView);
        mIndicatorId=a.getInt(R.styleable.AVLoadingIndicatorView_indicator, BallSpinFadeLoader);//获取编号属性
        mIndicatorColor=a.getColor(R.styleable.AVLoadingIndicatorView_indicator_color, Color.WHITE);//获取颜色属性
        a.recycle();//回收属性的集合
        mPaint=new Paint();
        mPaint.setColor(mIndicatorColor);//设置画笔的颜色
        mPaint.setStyle(Paint.Style.FILL);//设置画笔的样式为填充
        mPaint.setAntiAlias(true);//去锯齿
        applyIndicator();//
    }

    private void applyIndicator(){
        switch (mIndicatorId){
            case BallSpinFadeLoader:
                mIndicatorController=new BallSpinFadeLoaderIndicator();
                break;
        }
        mIndicatorController.setTarget(this);//将控件设置到当前View
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int width  = measureDimension(dp2px(DEFAULT_SIZE), widthMeasureSpec);//获取View的宽度
        int height = measureDimension(dp2px(DEFAULT_SIZE), heightMeasureSpec);//获取View的高度
        setMeasuredDimension(width, height);//
    }

    /**
     *测量的 维度
     * @param defaultSize 默认大小
     * @param measureSpec {@see widthMeasureSpec,heightMeasureSpec}
     * @return 返回测量的结果
     */
    private int measureDimension(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);//取最小值
        } else {
            result = defaultSize;
        }
        return result;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        drawIndicator(canvas);
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        if (!mHasAnimation){
            mHasAnimation=true;
            applyAnimation();
        }
    }

    void drawIndicator(Canvas canvas){
        mIndicatorController.draw(canvas,mPaint);
    }

    void applyAnimation(){
        mIndicatorController.createAnimation();
    }

    private int dp2px(int dpValue) {
        return (int) getContext().getResources().getDisplayMetrics().density * dpValue;
    }
BaseIndicatorController.java 
package com.chni.lidong.androidtestdemo.loading;

import android.graphics.Canvas;
import android.graphics.Paint;
import android.view.View;

/**
 * Created by lidongon 2016/1/31
 */
public abstract class BaseIndicatorController {

    private View mTarget;


    public void setTarget(View target){
        this.mTarget=target;
    }

    public View getTarget(){
        return mTarget;
    }

    /**
     * 得到View的宽度
     * @return
     */
    public int getWidth(){
        return mTarget.getWidth();
    }

    /**
     * 得到view的高度
     * @return
     */
    public int getHeight(){
        return mTarget.getHeight();
    }

    /**
     * 刷新view
     */
    public void postInvalidate(){
        mTarget.postInvalidate();
    }

    /**
     * draw indicator what ever
     * you want to draw
     * 绘制indicate
     * @param canvas
     * @param paint
     */
    public abstract void draw(Canvas canvas,Paint paint);

    /**
     * create animation or animations
     * ,and add to your indicator.
     * 创建动画或者动画集合,添加到indcator
     */
    public abstract void createAnimation();


}

 BallSpinFadeLoaderIndicator.java

package com.chni.lidong.androidtestdemo.loading;

import android.graphics.Canvas;
import android.graphics.Paint;

import com.nineoldandroids.animation.ValueAnimator;

/**
 * Created by lidongon 2016/1/31
 */
public class BallSpinFadeLoaderIndicator extends BaseIndicatorController {

    public static final float SCALE=1.0f;

    public static final int ALPHA=255;
    /**
     * 圆点的比例
     */
    float[] scaleFloats=new float[]{SCALE,
            SCALE,
            SCALE,
            SCALE,
            SCALE,
            SCALE,
            SCALE,
            SCALE};
    /**
     * 圆点的透明度集合
     */
    int[] alphas=new int[]{ALPHA,
            ALPHA,
            ALPHA,
            ALPHA,
            ALPHA,
            ALPHA,
            ALPHA,
            ALPHA};


    @Override
    public void draw(Canvas canvas, Paint paint) {
        float radius=getWidth()/10;
        for (int i = 0; i < 8; i++) {
            canvas.save();
            Point point=circleAt(getWidth(),getHeight(),getWidth()/2-radius,i*(Math.PI/4));
            canvas.translate(point.x,point.y);
            canvas.scale(scaleFloats[i],scaleFloats[i]);
            paint.setAlpha(alphas[i]);
            canvas.drawCircle(0,0,radius,paint);
            canvas.restore();
        }
    }

    /**
     * 圆O的圆心为(a,b),半径为R,点A与到X轴的为角α.
     *则点A的坐标为(a+R*cosα,b+R*sinα)
     * @param width
     * @param height
     * @param radius
     * @param angle
     * @return
     */
    Point circleAt(int width,int height,float radius,double angle){
        float x= (float) (width/2+radius*(Math.cos(angle)));
        float y= (float) (height/2+radius*(Math.sin(angle)));
        return new Point(x,y);
    }

    @Override
    public void createAnimation() {
        int[] delays= {0, 120, 240, 360, 480, 600, 720, 780, 840};
        for (int i = 0; i < 8; i++) {
            final int index=i;
            ValueAnimator scaleAnim=ValueAnimator.ofFloat(1,0.4f,1);//创建ValueAnimator对象
            scaleAnim.setDuration(1000);//设置动画的持续时间
            scaleAnim.setRepeatCount(-1);//设置动画是否重复
            scaleAnim.setStartDelay(delays[i]);//延迟启动动画
            scaleAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {//ValueAnimator只负责第一次的内容,因此必须通过监听来实现对象的相关属性的更新
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    scaleFloats[index] = (float) animation.getAnimatedValue();//获取当前帧的值
                    postInvalidate();
                }
            });
            scaleAnim.start();//启动属性动画

            ValueAnimator alphaAnim=ValueAnimator.ofInt(255, 77, 255);//透明度动画
            alphaAnim.setDuration(1000);//
            alphaAnim.setRepeatCount(-1);
            alphaAnim.setStartDelay(delays[i]);
            alphaAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    alphas[index] = (int) animation.getAnimatedValue();
                    postInvalidate();
                }
            });
            alphaAnim.start();
        }
    }

    final class Point{
        public float x;
        public float y;

        public Point(float x, float y){
            this.x=x;
            this.y=y;
        }
    }


}

UIHelp.java
package com.chni.lidong.androidtestdemo.utils;

import android.app.Activity;
import android.app.Dialog;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;

import com.chni.lidong.androidtestdemo.R;

/**
 * 对话框的实现
 * @author 李东
 * @Date 2014-11-23
 */
public class UIHelper {
	
	/** 加载数据对话框 */
	private static Dialog mLoadingDialog;
	
	
	/**
	 * 显示加载对话框
	 * @param context 上下文
	 * @param msg 对话框显示内容
	 * @param cancelable 对话框是否可以取消
	 */
	public static void showDialogForLoading(Activity context, String msg, boolean cancelable) {
		View view = LayoutInflater.from(context).inflate(R.layout.layout_loading_dialog, null);
		TextView loadingText = (TextView)view.findViewById(R.id.id_tv_loading_dialog_text);
		loadingText.setText(msg);
		
		mLoadingDialog = new Dialog(context, R.style.loading_dialog_style);
		mLoadingDialog.setCancelable(cancelable);
		mLoadingDialog.setContentView(view, new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT));
		mLoadingDialog.show();		
	}
	
	/**
	 * 关闭加载对话框
	 */
	public static void hideDialogForLoading() {
		if(mLoadingDialog != null && mLoadingDialog.isShowing()) {
			mLoadingDialog.cancel();
		}
	}

}

对话框的布局:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="@drawable/bg_loading_dialog_shape"
    android:gravity="center"
    android:minHeight="60dp"
    android:minWidth="180dp"
    android:orientation="vertical"
    android:padding="@dimen/padding_10" >

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_weight="1"
        android:gravity="center"
        android:layout_height="wrap_content">

        <com.chni.lidong.androidtestdemo.loading.AVLoadingIndicatorView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:indicator="BallSpinFadeLoader"
            app:indicator_color="@color/green"
            />

    </LinearLayout>

    <TextView
        android:id="@+id/id_tv_loading_dialog_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="@dimen/padding_5"
        android:text="正在登录…"
        android:textColor="@color/content"
        android:textSize="14sp" />

</LinearLayout>

对话框的样式:

<!-- 自定义Loading Dialog -->
<style name="loading_dialog_style" parent="@android:style/Theme.Dialog">
    <item name="android:windowFrame">@null</item>
    <item name="android:windowNoTitle">true</item>
    <item name="android:windowBackground">@color/transparent</item>
    <item name="android:windowIsFloating">true</item>
    <item name="android:windowContentOverlay">@null</item>
</style>


MainActivity.java

public class Main7Activity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main7);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                        .setAction("Action", null).show();
            }
        });
        UIHelper.showDialogForLoading(this, "正在加载...", true);
        Handler handler = new Handler();
        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                UIHelper.hideDialogForLoading();
            }
        },10000);
    }

}


最终的截图:



相关文章
|
24天前
|
存储 Shell Android开发
基于Android P,自定义Android开机动画的方法
本文详细介绍了基于Android P系统自定义开机动画的步骤,包括动画文件结构、脚本编写、ZIP打包方法以及如何将自定义动画集成到AOSP源码中。
42 2
基于Android P,自定义Android开机动画的方法
|
22天前
|
供应链 物联网 区块链
未来触手可及:探索新兴技术的趋势与应用安卓开发中的自定义视图:从基础到进阶
【8月更文挑战第30天】随着科技的飞速发展,新兴技术如区块链、物联网和虚拟现实正在重塑我们的世界。本文将深入探讨这些技术的发展趋势和应用场景,带你领略未来的可能性。
|
23天前
|
测试技术 Android开发 Python
探索软件测试的艺术:从基础到高级安卓应用开发中的自定义视图
【8月更文挑战第29天】在软件开发的世界中,测试是不可或缺的一环。它如同艺术一般,需要精细的技巧和深厚的知识。本文旨在通过浅显易懂的语言,引领读者从软件测试的基础出发,逐步深入到更复杂的测试策略和工具的使用,最终达到能够独立进行高效测试的水平。我们将一起探索如何通过不同的测试方法来确保软件的质量和性能,就像艺术家通过不同的色彩和笔触来完成一幅画作一样。
|
1天前
|
Android开发 开发者
安卓开发中的自定义视图:从入门到精通
【9月更文挑战第19天】在安卓开发的广阔天地中,自定义视图是一块充满魔力的土地。它不仅仅是代码的堆砌,更是艺术与科技的完美结合。通过掌握自定义视图,开发者能够打破常规,创造出独一无二的用户界面。本文将带你走进自定义视图的世界,从基础概念到实战应用,一步步展示如何用代码绘出心中的蓝图。无论你是初学者还是有经验的开发者,这篇文章都将为你打开一扇通往创意和效率的大门。让我们一起探索自定义视图的秘密,将你的应用打造成一件艺术品吧!
18 10
|
6天前
|
XML 编解码 Android开发
安卓开发中的自定义视图控件
【9月更文挑战第14天】在安卓开发中,自定义视图控件是一种高级技巧,它可以让开发者根据项目需求创建出独特的用户界面元素。本文将通过一个简单示例,引导你了解如何在安卓项目中实现自定义视图控件,包括创建自定义控件类、处理绘制逻辑以及响应用户交互。无论你是初学者还是有经验的开发者,这篇文章都会为你提供有价值的见解和技巧。
14 3
|
7天前
|
前端开发 Android开发 开发者
安卓应用开发中的自定义视图基础
【9月更文挑战第13天】在安卓开发的广阔天地中,自定义视图是一块神奇的画布,它允许开发者将想象力转化为用户界面的创新元素。本文将带你一探究竟,了解如何从零开始构建自定义视图,包括绘图基础、触摸事件处理,以及性能优化的实用技巧。无论你是想提升应用的视觉吸引力,还是追求更流畅的交互体验,这里都有你需要的金钥匙。
|
10天前
|
缓存 搜索推荐 Android开发
安卓应用开发中的自定义View组件实践
【9月更文挑战第10天】在安卓开发领域,自定义View是提升用户体验和实现界面个性化的重要手段。本文将通过一个实际案例,展示如何在安卓项目中创建和使用自定义View组件,包括设计思路、实现步骤以及可能遇到的问题和解决方案。文章不仅提供了代码示例,还深入探讨了自定义View的性能优化技巧,旨在帮助开发者更好地掌握这一技能。
|
22天前
|
XML 搜索推荐 Android开发
安卓开发中的自定义View组件实践
【8月更文挑战第30天】探索Android世界,自定义View是提升应用界面的关键。本文以简洁的语言带你了解如何创建自定义View,从基础到高级技巧,一步步打造个性化的UI组件。
|
24天前
|
Android开发
Android在rootdir根目录创建自定义目录和挂载点的方法
本文介绍了在Android高通平台的根目录下创建自定义目录和挂载点的方法,通过修改Android.mk文件并使用`LOCAL_POST_INSTALL_CMD`变量在编译过程中添加目录,最终在ramdisk.img的系统根路径下成功创建了`/factory/bin`目录。
49 1
|
14天前
|
前端开发 搜索推荐 Android开发
探索安卓开发中的自定义视图##
【9月更文挑战第6天】 在安卓应用开发的世界里,自定义视图如同绘画艺术中的色彩,它们为界面设计增添了无限可能。通过掌握自定义视图的绘制技巧,开发者能够创造出既符合品牌形象又提升用户体验的独特界面元素。本文将深入浅出地介绍如何从零开始构建一个自定义视图,包括基础框架搭建、关键绘图方法实现、事件处理机制以及性能优化策略。准备好让你的安卓应用与众不同了吗?让我们开始吧! ##