Android 通用 Dialog

1. 概述

  • github 地址 : https://github.com/luoshenzhi/UniversalDialog.git

1.1 特点说明

  1. 代码简洁, 不需要其他资源文件, 使用方便, 可定制性高
  2. 基于 DialogFragment 实现, 避免 Activity 销毁了对话框还存在
  3. 基于 AppCompatDialogFragment 实现, 确保 Android 各版本样式兼容
  4. 支持设置圆角、横坚屏切换、显示动画、位置、大小等
  5. 支持横坚屏切换保存控件状态, 如 EditText 数据

1.2 注意事项

  • 默认使用 AppCompatDialogFragment, 必须添加 AppCompat 依赖并设置主题, 如果不想使用, 让 UniversalDialog 继承 DialogFragment 即可
// 1. 添加依赖 appcompat
dependencies {
    implementation 'com.android.support:appcompat-v7:28.0.0'
}

// 2. 使用 AppCompat 的主题
<resources>
    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
       ..........
    style>
resources>

2. 使用方式

2.1 MainActivity.java

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Button button = new Button(this);
        button.setOnClickListener(this);
        button.setText("Hello");
        button.setLayoutParams(new ViewGroup.LayoutParams(200, 100));
        setContentView(button);
    }

    @Override
    public void onClick(View v) {

        UniversalDialog.Builder
                .setView(R.layout.dialog_layout)                        // 设置布局文件
                .setWidth(800)                                          // 设置宽
                .setHeight(500)                                         // 设置高
                .setGravity(Gravity.CENTER)                             // 设置位置
                .setRoundRadius(15)                                     // 设置圆角
                .setAnimation(android.R.style.Animation_Translucent)    // 设置动画
                .setDimAmount(0.5f)                                     // 设置背景透明度
                .setCancelable(false)                                   // 设置为强制对话框
                .setOnBindViewListener((dialog, holder) -> {            // 设置布局
                    holder.setOnClickListener(R.id.no_tv, (view) -> dialog.dismiss());
                    holder.setOnClickListener(R.id.yes_tv, (view) -> dialog.dismiss());
                })
                .setOnSaveStateListener((outState, holder, resume) -> { // 保存 View 状态

                    // 恢复数据
                    final String key = "password_et";
                    if (resume) {
                        String password = outState.getString(key);
                        holder.setText(R.id.password_et, password);
                        return;
                    }

                    // 保存数据
                    String password = holder.getView(R.id.password_et).toString().trim();
                    if (!TextUtils.isEmpty(password)) {
                        outState.putString(key, password);
                    }
                })
                .create()                                                // 创建对话框
                .show(getSupportFragmentManager(), "TAG");
    }
}

2.2 dialog_layout.xml


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/holo_blue_dark"
    android:orientation="vertical"
    android:padding="15dp">

    <TextView
        android:id="@+id/title_tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="提示" />

    <TextView
        android:id="@+id/message_tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="请输入您的密码" />

    <EditText
        android:id="@+id/password_et"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="15dp"
        android:gravity="right|end"
        android:orientation="horizontal">

        <TextView
            android:id="@+id/no_tv"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="No" />

        <TextView
            android:id="@+id/yes_tv"
            android:layout_width="wrap_content"
            android:layout_marginLeft="20dp"
            android:layout_height="wrap_content"
            android:text="Yes" />
    LinearLayout>

LinearLayout>

2.3 UniversalDialog.java

package com.ken.dialog;

import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.GradientDrawable;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.LayoutRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.StyleRes;
import android.support.v4.app.DialogFragment;
import android.support.v7.app.AppCompatDialogFragment;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;


/**
 * 通用对话框
 * @author Ken Luo
 * @version 1.0
 */
public class UniversalDialog extends AppCompatDialogFragment {

    private DialogParams params;
    private ViewHolder viewHolder;
    private static final String SAVE_STATE_KEY = "params";

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return setView(inflater, container, savedInstanceState);
    }

    @Override
    public void onStart() {
        super.onStart();
        setDialog();
    }

    @Override
    public void onSaveInstanceState(@NonNull Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putParcelable(SAVE_STATE_KEY, params);                     // 横坚屏切换时保存数据
        if (params.saveStateListener != null) {
            params.saveStateListener.onSaveInstanceState(outState, viewHolder, false);
        }
    }

    private void setParams(DialogParams params) {
        this.params = params;
    }

    private View setView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

        // 恢复横坚屏切换时保存的对话框数据
        if (params == null && savedInstanceState != null) {
            params = savedInstanceState.getParcelable(SAVE_STATE_KEY);
        }

        if (params == null || params.layoutResId <= 0)
            return null;

        View view = inflater.inflate(params.layoutResId, container, false);
        viewHolder = new ViewHolder(getContext(), view);

        // 设置布局
        if (params.bindViewListener != null) {
            params.bindViewListener.onBindView(this, viewHolder);
        }

        // 恢复 View 状态, 如 EditText 的数据
        if (params.saveStateListener != null && savedInstanceState != null) {
            params.saveStateListener.onSaveInstanceState(savedInstanceState, viewHolder, true);
        }
        return view;
    }


    private void setDialog() {

        if (params == null || getContext() == null || getDialog() == null || getDialog().getWindow() == null)
            return;

        // 圆角背景
        Window window = getDialog().getWindow();
        GradientDrawable drawable = new GradientDrawable();
        drawable.setColor(Color.TRANSPARENT);
        drawable.setCornerRadius(params.roundRadius > 0 ? params.roundRadius : dp2px(getContext(), 5));
        drawable.setStroke(0, Color.TRANSPARENT);
        window.setBackgroundDrawable(drawable);

        // 背景变暗
        window.addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
        window.setDimAmount(params.dimAmount);

        // 设置宽高和位置
        window.setLayout(params.width > 0 ? params.width : getScreenWidth(getContext()) / 4 * 3,
                         params.height > 0 ? params.height : WindowManager.LayoutParams.WRAP_CONTENT);
        window.setGravity(params.gravity);

        // 设置显示和退出动画
        if (params.animation > 0)
            window.setWindowAnimations(params.animation);

        // 是否为强制对话框
        this.setCancelable(params.cancelable);
    }

    private static int getScreenWidth(Context context) {
        return context.getResources().getDisplayMetrics().widthPixels;
    }

    private static int dp2px(Context context, int dps) {
        return Math.round(context.getResources().getDisplayMetrics().density * dps);
    }


    /**
     * 通用对话框构建器
     */
    public static class Builder {

        private final DialogParams P;

        private Builder(@LayoutRes  int layoutResId) {
            P = new DialogParams();
            P.layoutResId = layoutResId;
        }

        /**
         * 设置对话框布局
         * @param layoutResId : 布局文件
         * @return 对话框构建器
         */
        public static Builder setView(@LayoutRes  int layoutResId) {
            return new Builder(layoutResId);
        }

        /**
         * 设置对话框构宽度
         * @param width : 对话框宽度, 默认为屏幕宽度的 3/4
         * @return 对话框构建器
         */
        public Builder setWidth(int width) {
            P.width = width;
            return this;
        }

        /**
         * 设置对话框构高度
         * @param height 高度, 默认 WRAP_CONTENT
         * @return 对话框构建器
         */
        public Builder setHeight(int height) {
            P.height = height;
            return this;
        }

        /**
         * 设置对话框显示位置
         * @param gravity 显示位置, 默认 Gravity.CENTER
         * @return 对话框构建器
         */
        public Builder setGravity(int gravity) {
            P.gravity = gravity;
            return this;
        }

        /**
         * 设置对话框构圆角大小
         * @param roundRadius 圆角大小, 默认 5dip
         * @return 对话框构建器
         */
        public Builder setRoundRadius(int roundRadius) {
            P.roundRadius = roundRadius;
            return this;
        }

        /**
         * 设置显示和退出动画
         * @param resId 资源文件
         * @return 对话框构建器
         *
         * 
         *     
         *
         *      
         *      
         *          
         *          
         *      
         *
         *      
         *      
         *          
         *          
         *      
         *
         * 
*/
public Builder setAnimation(@StyleRes int resId) { P.animation = resId; return this; } /** * 设置窗口透明度 * @param dimAmount 范围 0~1.0, 0-完全透明, 1-全黑; 默认 0.5f * @return 对话框构建器 */ public Builder setDimAmount(float dimAmount) { P.dimAmount = dimAmount; return this; } /** * 设置是否为强制对话框 * @param cancelable true 非强制, false 强制; 默认为非强制 * @return 对话框构建器 */ public Builder setCancelable(boolean cancelable) { P.cancelable = cancelable; return this; } /** * 设置布局监听器 * @param listener 对话框布局设置监听器 * @return 对话框构建器 */ public Builder setOnBindViewListener(OnBindViewListener listener) { P.bindViewListener = listener; return this; } public Builder setOnSaveStateListener(OnSaveStateListener listener) { P.saveStateListener = listener; return this; } /** * 创建对话框 * @return 创建好的对话框 * @exception NullPointerException 如果布局文件 ID 小于等于 0 */ public DialogFragment create() { if (P.layoutResId <= 0) throw new NullPointerException("The dialog without layout!"); final UniversalDialog dialog = new UniversalDialog(); dialog.setParams(P); return dialog; } } private static class DialogParams implements Parcelable { private int layoutResId = 0; private int width = 0; private int height = 0; private int gravity = Gravity.CENTER; private int roundRadius = 0; private int animation = 0; private float dimAmount = 0.5f; private boolean cancelable = true; private OnBindViewListener bindViewListener; private OnSaveStateListener saveStateListener; private DialogParams() { } DialogParams(Parcel in) { layoutResId = in.readInt(); width = in.readInt(); height = in.readInt(); gravity = in.readInt(); roundRadius = in.readInt(); animation = in.readInt(); dimAmount = in.readFloat(); cancelable = in.readByte() != 0; } public static final Creator<DialogParams> CREATOR = new Creator<DialogParams>() { @Override public DialogParams createFromParcel(Parcel in) { return new DialogParams(in); } @Override public DialogParams[] newArray(int size) { return new DialogParams[size]; } }; @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(layoutResId); dest.writeInt(width); dest.writeInt(height); dest.writeInt(gravity); dest.writeInt(roundRadius); dest.writeInt(animation); dest.writeFloat(dimAmount); dest.writeByte((byte) (cancelable ? 1 : 0)); } } public interface OnBindViewListener { void onBindView(DialogFragment dialog, ViewHolder holder); } public interface OnSaveStateListener { /** * 控件状态保存回调函数 * @param bundle 保存或恢复数据 * @param holder ViewHolder * @param resume true 表示恢复数据, false 表示保存数据 */ void onSaveInstanceState(@NonNull Bundle bundle, ViewHolder holder, boolean resume); } }

你可能感兴趣的:(Android)