我们应该都知道任何一个app的UI都会遵循一个统一的样式,比如我们的Dialog、Log、Toast等,统一管理好自己的组件库,对自己或后来人都会有很大地帮助的。
记得自己刚开始接手某个项目时,发现这个项目什么规范都没有,命名啥的也不规范,虽然有一些统一管理的工具类,但是写地真是烂,唉,说多了都是泪。。。。
接下来讲解下小球项目里Dialog,我们项目中Dialog居中显示,属性有标题、内容、按钮(1或2个),如我们的布局文件dialog_common_view.xml
<?xml version="1.0" encoding="utf-8"?>
<com.flyco.roundview.RoundLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:orientation="vertical"
android:paddingBottom="10dp"
app:rv_backgroundColor="@color/white"
app:rv_cornerRadius="6dp">
<com.flyco.roundview.RoundTextView
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:gravity="center"
android:id="@+id/tv_dialog_title"
android:minHeight="50dp"
android:paddingBottom="4dp"
android:paddingEnd="15dp"
android:paddingStart="15dp"
android:paddingTop="4dp"
android:text="标题"
android:textColor="@color/dialog_title_text"
android:textSize="17sp"
android:textStyle="bold"
android:visibility="visible"
app:rv_backgroundColor="@color/dialog_title_bg"
app:rv_cornerRadius_TL="6dp"
app:rv_cornerRadius_TR="6dp" />
<TextView
android:layout_height="wrap_content"
android:layout_marginBottom="25dp"
android:layout_marginLeft="15dp"
android:layout_marginRight="15dp"
android:layout_marginTop="25dp"
android:layout_width="match_parent"
android:gravity="center"
android:id="@+id/tv_dialog_content"
android:lineSpacingExtra="2dp"
android:maxHeight="400dp"
android:scrollbars="vertical"
android:text="弹框内容"
android:textColor="@color/dialog_content_text"
android:textSize="14sp" />
<LinearLayout
android:layout_height="wrap_content"
android:layout_marginLeft="15dp"
android:layout_marginRight="15dp"
android:layout_width="match_parent"
android:id="@+id/ll_bottom_operation"
android:orientation="horizontal">
<com.flyco.roundview.RoundTextView
android:layout_height="40dp"
android:layout_marginEnd="4dp"
android:layout_weight="1"
android:layout_width="0dp"
android:gravity="center"
android:id="@+id/rtv_left"
android:text="@string/dialog_btn_cancel"
android:textColor="@color/dialog_btn_cancel_text"
android:textSize="14sp"
android:visibility="visible"
app:rv_backgroundColor="@color/dialog_btn_cancel_bg"
app:rv_backgroundPressColor="@color/dialog_btn_cancel_bg_checked"
app:rv_cornerRadius="6dp"
app:rv_strokeColor="@color/dialog_btn_cancel_stroke"
app:rv_strokePressColor="@color/dialog_btn_cancel_stroke_checked"
app:rv_strokeWidth="1dp" />
<com.flyco.roundview.RoundTextView
android:layout_height="40dp"
android:layout_marginStart="4dp"
android:layout_weight="1"
android:layout_width="0dp"
android:gravity="center"
android:id="@+id/rtv_right"
android:text="@string/dialog_btn_confirm"
android:textColor="@color/dialog_btn_confirm_text"
android:textSize="14sp"
android:visibility="visible"
app:rv_backgroundColor="@color/dialog_btn_confirm_bg"
app:rv_backgroundPressColor="@color/dialog_btn_confirm_bg_checked"
app:rv_cornerRadius="6dp" />
</LinearLayout>
</com.flyco.roundview.RoundLinearLayout>
用到的依赖
implementation 'com.blankj:utilcodex:1.30.6'
implementation 'com.flyco.roundview:FlycoRoundView_Lib:1.1.4@aar'
颜色值
<!--弹框相关颜色-->
<color name="dialog_title_text">#101a3e</color>
<color name="dialog_title_bg">#F9FAFB</color>
<color name="dialog_content_text">#221715</color>
<color name="dialog_btn_cancel_text">#221715</color>
<color name="dialog_btn_cancel_stroke">#221715</color>
<color name="dialog_btn_cancel_stroke_checked">#221715</color>
<color name="dialog_btn_cancel_bg">#FFFFFF</color>
<color name="dialog_btn_cancel_bg_checked">#F0F0F0</color>
<color name="dialog_btn_confirm_text">#221715</color>
<color name="dialog_btn_confirm_stroke">#221715</color>
<color name="dialog_btn_confirm_stroke_checked">#221715</color>
<color name="dialog_btn_confirm_bg">#4996F3</color>
<color name="dialog_btn_confirm_bg_checked">#4996F3</color>
<color name="dialog_btn_confirm_text_enable">#80221715</color>
<color name="dialog_btn_confirm_bg_enable">#384996F3</color>
字符值
<string name="dialog_title">温馨提示</string>
<string name="dialog_permission_allow">允许</string>
<string name="dialog_permission_refuse">拒绝</string>
<string name="dialog_permission_title">申请权限</string>
<string name="dialog_btn_cancel">取消</string>
<string name="dialog_btn_confirm">确认</string>
<string name="dialog_btn_setting">设置</string>
以前项目中Dialog这里写一个,那边又写一个,所以自己就重构了整个项目的Dialog,先是定义了个简单基类BaseDialog
package com.littlejerk.permissiondemo;
import android.app.Activity;
import android.app.Dialog;
import android.content.Context;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.Window;
import android.view.WindowManager;
import com.blankj.utilcode.util.ActivityUtils;
import androidx.annotation.NonNull;
public class BaseDialog extends Dialog {
private Context context;
public BaseDialog(@NonNull Context context) {
super(context, R.style.DialogTheme);
this.context = context;
}
public void show(View view) {
Window window = getWindow();
if (window == null) return;
window.setContentView(view);
WindowManager.LayoutParams pl = window.getAttributes();
pl.gravity = Gravity.CENTER;
pl.height = WindowManager.LayoutParams.WRAP_CONTENT;
int width = window.getDecorView().getResources().getDisplayMetrics().widthPixels;
pl.width = (int) (width * 0.9);
window.setAttributes(pl);
show();
}
@Override
public void show() {
Activity activity = ActivityUtils.getActivityByContext(context);
if (activity != null && !activity.isFinishing()) {
super.show();
}
}
@Override
public void dismiss() {
Activity activity = ActivityUtils.getActivityByContext(context);
if (activity != null && !activity.isFinishing()) {
super.dismiss();
}
}
public boolean isOutOfBounds(Context context, MotionEvent event) {
final int x = (int) event.getX();
final int y = (int) event.getY();
final int slop = ViewConfiguration.get(context).getScaledWindowTouchSlop();
final Window window = getWindow();
if (window == null) return false;
final View decorView = getWindow().getDecorView();
return (x < -slop) || (y < -slop) || (x > (decorView.getWidth() + slop)) || (y > (decorView.getHeight() + slop));
}
}
然后使用单例模式AppDialogManager来管理Dialog,如下是完整的代码
package com.littlejerk.permissiondemo;
import android.app.Activity;
import android.app.Dialog;
import android.content.DialogInterface;
import android.text.method.ScrollingMovementMethod;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.blankj.utilcode.util.SizeUtils;
import com.blankj.utilcode.util.StringUtils;
import com.flyco.roundview.RoundTextView;
public class AppDialogManager {
private AppDialogManager() {
}
public static AppDialogManager getInstance() {
return Holder.instance;
}
private static class Holder {
private static AppDialogManager instance = new AppDialogManager();
}
private BaseDialog mDialog = null;
public interface DialogClickCallback {
void onClick(int index);
}
public void showPermissionRemindDialog(Activity activity, String content,
DialogInterface.OnClickListener negativeLister, DialogInterface.OnClickListener positiveLister) {
showCommonDialog(activity,
activity.getString(R.string.dialog_permission_title), content,
activity.getString(R.string.dialog_permission_refuse), negativeLister,
activity.getString(R.string.dialog_permission_allow), positiveLister);
}
public void showPermissionSettingRemind(Activity activity, String content,
DialogInterface.OnClickListener negativeLister, DialogInterface.OnClickListener positiveLister) {
showCommonDialog(activity,
activity.getString(R.string.dialog_permission_title), content,
activity.getString(R.string.dialog_btn_cancel), negativeLister,
activity.getString(R.string.dialog_btn_setting), positiveLister);
}
public void showPositiveDialog(Activity activity, String title, String content,
String positive, DialogInterface.OnClickListener positiveLister) {
showCommonDialog(activity, title, content, null, null, positive, positiveLister);
}
public void showCommonDialog(Activity activity, String content, DialogInterface.OnClickListener positiveLister) {
showCommonDialog(activity, null, content,
activity.getString(R.string.dialog_btn_cancel), null,
activity.getString(R.string.dialog_btn_confirm), positiveLister);
}
public void showCommonDialog(Activity activity, String title, String content,
String negative, DialogInterface.OnClickListener negativeLister,
String positive, DialogInterface.OnClickListener positiveLister) {
showCommonDialog(activity, title, content, negative, negativeLister, positive, positiveLister, true);
}
public void showCommonDialog(Activity activity, String title, String content,
String negative, DialogInterface.OnClickListener negativeLister,
String positive, DialogInterface.OnClickListener positiveLister, boolean isClickDismiss) {
showDialog(activity, title, content, negative, negativeLister, positive, positiveLister,
false, false, null,
true, isClickDismiss);
}
private void showDialog(Activity activity,
String title,
String content,
String negative, final DialogInterface.OnClickListener negativeLister,
String positive, final DialogInterface.OnClickListener positiveLister,
boolean isCanceledOnTouchOutside,
boolean isCancelable,
final DialogInterface.OnDismissListener dismissListener,
boolean isMultiDialog,
final boolean isClickDismiss) {
View view = LayoutInflater.from(activity).inflate(R.layout.dialog_common_view, null);
TextView tvTitle = view.findViewById(R.id.tv_dialog_title);
TextView tvContent = view.findViewById(R.id.tv_dialog_content);
tvContent.setMovementMethod(ScrollingMovementMethod.getInstance());
RoundTextView rtvLeft = view.findViewById(R.id.rtv_left);
RoundTextView rtvRight = view.findViewById(R.id.rtv_right);
if (!StringUtils.isEmpty(title)) {
tvTitle.setText(title);
tvTitle.setVisibility(View.VISIBLE);
} else {
tvTitle.setVisibility(View.GONE);
}
if (StringUtils.isEmpty(content)) {
return;
}
tvContent.setText(content);
tvContent.setVisibility(View.VISIBLE);
if (StringUtils.isEmpty(title)) {
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) tvContent.getLayoutParams();
params.setMargins(
SizeUtils.dp2px(15), SizeUtils.dp2px(30),
SizeUtils.dp2px(15), SizeUtils.dp2px(30));
tvContent.setLayoutParams(params);
}
if (StringUtils.isEmpty(title) && StringUtils.isEmpty(negative) && !StringUtils.isEmpty(positive)) {
tvContent.setTextSize(12);
}
if (!StringUtils.isEmpty(negative)) {
rtvLeft.setText(negative);
rtvLeft.setVisibility(View.VISIBLE);
} else {
rtvLeft.setVisibility(View.GONE);
}
if (!StringUtils.isEmpty(positive)) {
rtvRight.setText(positive);
rtvRight.setVisibility(View.VISIBLE);
} else {
rtvRight.setVisibility(View.GONE);
}
if (isMultiDialog) {
final BaseDialog dialog = new BaseDialog(activity);
dialog.setCanceledOnTouchOutside(isCanceledOnTouchOutside);
dialog.setCancelable(isCancelable);
dialog.setOnDismissListener(dialog1 -> {
if (dismissListener != null) dismissListener.onDismiss(dialog1);
});
rtvLeft.setOnClickListener(v -> {
if (isClickDismiss) {
dialog.dismiss();
}
if (negativeLister != null) {
negativeLister.onClick(dialog, DialogInterface.BUTTON_NEGATIVE);
}
});
rtvRight.setOnClickListener(v -> {
if (isClickDismiss) {
dialog.dismiss();
}
if (positiveLister != null) {
positiveLister.onClick(dialog, DialogInterface.BUTTON_POSITIVE);
}
});
dialog.show(view);
} else {
resetLatestDialog(mDialog);
mDialog = new BaseDialog(activity);
mDialog.setCanceledOnTouchOutside(isCanceledOnTouchOutside);
mDialog.setCancelable(isCancelable);
mDialog.setOnDismissListener(dialog -> {
if (dismissListener != null) dismissListener.onDismiss(dialog);
});
rtvLeft.setOnClickListener(v -> {
if (isClickDismiss) {
mDialog.dismiss();
}
if (negativeLister != null) {
negativeLister.onClick(mDialog, DialogInterface.BUTTON_NEGATIVE);
}
});
rtvRight.setOnClickListener(v -> {
if (isClickDismiss) {
mDialog.dismiss();
}
if (positiveLister != null) {
positiveLister.onClick(mDialog, DialogInterface.BUTTON_POSITIVE);
}
});
mDialog.show(view);
}
}
private void resetLatestDialog(Dialog dialog) {
if (dialog != null && dialog.isShowing()) {
dialog.dismiss();
dialog = null;
}
}
}
这里我们提供了一个最终调用方法,不同样式可通过传入不同参数控制
private void showDialog(Activity activity,
String title,
String content,
String negative, final DialogInterface.OnClickListener negativeLister,
String positive, final DialogInterface.OnClickListener positiveLister,
boolean isCanceledOnTouchOutside,
boolean isCancelable,
final DialogInterface.OnDismissListener dismissListener,
boolean isMultiDialog,
final boolean isClickDismiss);
方法参数和说明如下
activity | 上下文 |
---|
title | 标题 | content | 内容 | negative | 左边按钮(取消) | negativeLister | 左边按钮的回调 | positive | 右边按钮(确认) | positiveLister | 右边按钮的回调 | isCanceledOnTouchOutside | 点击非Dialog内容部分是否允许dismiss | isCancelable | 点击后退键是否允许dismiss | dismissListener | dialog消失的监听事件 | isMultiDialog | 是否允许多个dialog同时存在 | isClickDismiss | 点击按钮是否允许 dismiss dialog |
可实现的功能
- 可任意修改按钮文案
- 设置是否有标题
- 控制按钮的数量
- 按钮点击事件回调
- 任意控制dialog的消失(如强制更新按钮点击不允许dialog消失、back键等)
要想实现这些功能,直接重载上面这个showDialog()方法。
文章主要讲解了小球项目中dialog的样式管理,我们可以根据代码修改符合自己项目的dialog,如dialog的位置显示、圆角控制、左右边距、按钮的显示样式等等,这里只是提供一个思路,以后应该会整合一些常用的dialog,欢迎大家来支持关注一波。最后,文章代码在 PermissionDemo里,有需要可以去看看!
APP合规检查系列文章: Android 组件导出风险及防范 Android Activity防劫持方案 Android 申请权限前简单封装弹框阐述申请理由工具类,应付app合规检查
|