Android 通用 Dialog
1. 概述
- github 地址 : https://github.com/luoshenzhi/UniversalDialog.git
- 代码简洁, 不需要其他资源文件, 使用方便, 可定制性高
- 基于 DialogFragment 实现, 避免 Activity 销毁了对话框还存在
- 基于 AppCompatDialogFragment 实现, 确保 Android 各版本样式兼容
- 支持设置圆角、横坚屏切换、显示动画、位置、大小等
- 支持横坚屏切换保存控件状态, 如 EditText 数据
- 默认使用 AppCompatDialogFragment, 必须添加 AppCompat 依赖并设置主题, 如果不想使用, 让 UniversalDialog 继承 DialogFragment 即可
// 1. 添加依赖 appcompat
dependencies {
implementation 'com.android.support:appcompat-v7:28.0.0'
}// 2. 使用 AppCompat 的主题
name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
..........
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
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(@LayoutResint layoutResId) {
P = new DialogParams();
P.layoutResId = layoutResId;
}/**
* 设置对话框布局
* @param layoutResId : 布局文件
* @return 对话框构建器
*/
public static Builder setView(@LayoutResint 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 对话框构建器
*
*
*
*- @anim/translucent_enter
*- @anim/translucent_exit
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
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 CREATOR = new Creator() {
@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第三方框架(五)ButterKnife
- Android中的AES加密-下
- 带有Hilt的Android上的依赖注入
- android|android studio中ndk的使用
- Android事件传递源码分析
- RxJava|RxJava 在Android项目中的使用(一)
- Android7.0|Android7.0 第三方应用无法访问私有库
- 深入理解|深入理解 Android 9.0 Crash 机制(二)
- android防止连续点击的简单实现(kotlin)
- Android|Android install 多个设备时指定设备