目录
写在前面
作者在Android开发过程中,碰到过许多坑,如果没有多年的开发经验的积累,对于一个初学者来说,很难发现是什么问题,本篇文章是作者回顾先前遇到的诸多坑,记录并整理出来供初学者学习以及参考,如果有大佬发现哪里写的不对请多多包涵并斧正,不胜感激。
如果你有问题欢迎咨询我,作者联系方式QQ/WX相同:860326470
长文预警!!
一、UI
其实个人感觉坑最多的地方就是UI上的问题了,所以放在最前面。
1. UI - Java代码动态设置控件宽高等参数
如果我们想设置一个UI控件的宽度,例如修改TextView的宽度时,我们不能直接调用TextView.setWidth()方法,这是设置它的最大范围的意思,如果你要设置宽高,必须要获取它的layoutparams,代码:
// 将textView宽设为100dp
ViewGroup.LayoutParams layoutParams = textView.getLayoutParams();
layoutParams.width = UIUtils.getInstance().dp2px(MainActivity.this, 100);// 这里需要的单位是像素也就是px,而我们习惯用dp,所以要做一个转换
textView.setLayoutParams(layoutParams);
其中UIUtils工具类前往我的另一篇文章获取(那里面统一叫MyUtils工具类,你可以根据自己的用途,创建多个自己的工具类,我的习惯是创建UIUtils负责UI方面的东西,HttpUtils负责网络相关的东西,MyUtils负责其它方面的东西),文中有很多干货。
2. UI - AlertDialog自定义布局
我个人使用两种方法自定义AlertDialog布局,这两种方法功能上都能实现,区别在于,方法一单次使用方便,方法二复用方便。
方式一:简单代码自定义
(抛砖引玉开始)
如果想要自定义AlertDialog布局,最简单的方式就是在setTitle和setMessage和setPositiveButton的同时setView,下面上代码(为了方便举例,新建一个项目并将代码直接放在onCreate方法中):
LayoutInflater inflater = LayoutInflater.from(MainActivity.this);
View view = inflater.inflate(R.layout.dialog_example,null);
new AlertDialog.Builder(MainActivity.this)
.setView(view)
.setTitle("标题")
.setMessage("内容")
.setPositiveButton("确定",null)
.create().show();
其中,dialog_example是你在res/layout下创建的布局,没错,就是放activity的布局文件的那个文件夹,下面是效果图:
(抛砖引玉结束)
但是有的同学问了,我明明给根布局设置了background是黑色怎么只有中间那块是自定义的布局?如果想要自定义整个AlertDialog,有两个方法可供选择,如果你不需要复用,那么直接使用下面这种方式即可:
AlertDialog alertDialog = new AlertDialog.Builder(MainActivity.this).create();
// 必须要先show
alertDialog.show();
// 构建alertDialog显示的view布局
View view = LayoutInflater.from(this).inflate(R.layout.dialog_example, null);
// 设置布局
Window window = alertDialog.getWindow();
window.setContentView(view);
可新的问题接踵而至,我明明给布局设置了200dp的宽高和圆角,但是仍然没有正常显示,很明显高度wrap_content了,这时我们就要加一点代码了:
AlertDialog alertDialog = new AlertDialog.Builder(MainActivity.this).create();
// 必须要先show
alertDialog.show();
// 构建alertDialog显示的view布局
View view = LayoutInflater.from(this).inflate(R.layout.dialog_example, null);
// 设置布局
Window window = alertDialog.getWindow();
window.setContentView(view);
// 将显示dialog的window背景设为透明
window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
// 将显示dialog的window的宽高设为200dp
WindowManager.LayoutParams params = window.getAttributes();
params.width = UIUtils.getInstance().dp2px(MainActivity.this, 200); // 这里需要的单位是像素也就是px,而我们习惯用dp,所以要做一个转换
params.height = UIUtils.getInstance().dp2px(MainActivity.this, 200); // 这里需要的单位是像素也就是px,而我们习惯用dp,所以要做一个转换
window.setAttributes(params);
其中UIUtils工具类前往我的另一篇文章获取(那里面统一叫MyUtils工具类,你可以根据自己的用途,创建多个自己的工具类,我的习惯是创建UIUtils负责UI方面的东西,HttpUtils负责网络相关的东西,MyUtils负责其它方面的东西),文中有很多干货。
可以将Window想象成root,也就是根布局,如果你在xml布局文件中最外层写的match_parent,实际上匹配的是这个window的尺寸,当然,我这里为了凸显出不设置window带来的错误效果,给最外层写了200dp,你只需要写match_parent即可。
(细心的朋友可能会发现,如果在xml布局中角落设置几个尖尖角的控件,即使你background中的shape设置了圆角,尖尖角依旧会被暴露在外,为了让你透彻的运用,这里的小bug我不做更改,希望你通过学习下面的重写Dialog中能找到解决的办法。)
效果:
那如果我的dialog内有按钮怎么设置点击事件呢?添加两行代码,请你举一反三:
AlertDialog alertDialog = new AlertDialog.Builder(MainActivity.this).create();
// 必须要先show
alertDialog.show();
// 构建alertDialog显示的view布局
View view = LayoutInflater.from(this).inflate(R.layout.dialog_example, null);
// 设置布局
Window window = alertDialog.getWindow();
window.setContentView(view);
// 将显示dialog的window背景设为透明
window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
// 将显示dialog的window的宽高设为200dp
WindowManager.LayoutParams params = window.getAttributes();
params.width = UIUtils.getInstance().dp2px(MainActivity.this, 200); // 这里需要的单位是像素也就是px,而我们习惯用dp,所以要做一个转换
params.height = UIUtils.getInstance().dp2px(MainActivity.this, 200); // 这里需要的单位是像素也就是px,而我们习惯用dp,所以要做一个转换
window.setAttributes(params);
// 获取布局内的组件并操作
TextView textView = view.findViewById(R.id.textView_dialog);
textView.setText("改变后的文字");
效果:
好了,大功告成,通过大约十行代码,就能在Activity中自定义你的AlertDIalog样式,很简单吧。
方式二:重写Dialog
但是需求总是日益变化,如果你需要统一整个app的UI风格,那你就需要重写Dialog了,调用你自己的Dialog,而不是androidx.appcompat.app包下的(Android自带的)AlertDialog,这里列出一个我自己写的MyAlertDIalog.class:
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
public class MyAlertDialog extends Dialog implements DialogInterface {
public MyAlertDialog(@NonNull Context context) {
super(context);
}
public MyAlertDialog(@NonNull Context context, int themeResId) {
super(context, themeResId);
}
protected MyAlertDialog(@NonNull Context context, boolean cancelable, @Nullable OnCancelListener cancelListener) {
super(context, cancelable, cancelListener);
}
public static class Builder {
private View mLayout;
private TextView mTextViewTitle;
private TextView mTextViewMessage;
private Button mButtonPositive;
private Button mButtonNegative;
private View.OnClickListener mButtonPositiveClickListener;
private View.OnClickListener mButtonNegativeClickListener;
private MyAlertDialog mDialog;
public Builder(Context context) {
mDialog = new MyAlertDialog(context, R.style.Theme_MaterialComponents_Dialog);
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
//加载布局文件
mLayout = inflater.inflate(R.layout.dialog_alert, null, false);
//添加布局文件到 Dialog
mDialog.addContentView(mLayout, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
Window window = mDialog.getWindow();
WindowManager.LayoutParams lp = mDialog.getWindow().getAttributes();
lp.width = UIUtils.getInstance().dp2px(context,256);// 调整该值可以设置对话框显示的宽度
lp.height = ViewGroup.LayoutParams.WRAP_CONTENT;
window.setAttributes(lp);
window.setBackgroundDrawableResource(R.drawable.shape_dialog_background);// 答案在这里
// ↑↑↑我在这里给window的background设置了圆角,而不是给xml根组件的background设置圆角,上面一章的例子只是将window设为透明而已,所以当根组件内的某些组件超出圆角后,根组件的圆角并不能包裹住尖尖角,所以尖尖角暴露在外。
mTextViewTitle = mLayout.findViewById(R.id.textView_dialog_my_title);
mTextViewMessage = mLayout.findViewById(R.id.textView_dialog_my_message);
mButtonPositive = mLayout.findViewById(R.id.button_dialog_my_positive);
mButtonNegative = mLayout.findViewById(R.id.button_dialog_my_negative);
}
public TextView getTitle(){
return mTextViewTitle;
}
public TextView getMessage(){
return mTextViewMessage;
}
public Button getPositiveButton(){
return mButtonPositive;
}
public Button getNegativeButton(){
return mButtonNegative;
}
public Builder setTitle(@NonNull String title) {
mTextViewTitle.setText(title);
mTextViewTitle.setVisibility(View.VISIBLE);
return this;
}
public Builder setMessage(@NonNull String message) {
mTextViewMessage.setText(message);
mTextViewMessage.setVisibility(View.VISIBLE);
return this;
}
public Builder setPositiveButton(@NonNull String text, View.OnClickListener listener) {
mButtonPositive.setText(text);
mButtonPositiveClickListener = listener;
return this;
}
public Builder setNegativeButton(@NonNull String text, View.OnClickListener listener) {
mButtonNegative.setText(text);
mButtonNegativeClickListener = listener;
mButtonNegative.setVisibility(View.VISIBLE);
return this;
}
public MyAlertDialog create() {
mButtonNegative.setOnClickListener(v -> {
mDialog.dismiss();
if (mButtonNegativeClickListener != null) {
mButtonNegativeClickListener.onClick(v);
}
});
mButtonPositive.setOnClickListener(v -> {
mDialog.dismiss();
if (mButtonPositiveClickListener != null) {
mButtonPositiveClickListener.onClick(v);
}
});
mDialog.setContentView(mLayout);
mDialog.setCancelable(false); //用户点击后退键关闭 Dialog
mDialog.setCanceledOnTouchOutside(false); //用户点击外部来关闭 Dialog
return mDialog;
}
}
}
使用方式:
new MyAlertDialog.Builder(SettingsActivity.this)
.setTitle("有新版可用")
.setMessage("建议在wifi环境下进行下载,确定要下载吗?")
.setPositiveButton("确定", v -> {
// ...
})
.setNegativeButton("取消", null)
.create().show();
效果:
3. UI - Button背景background属性设置无效
这个问题在新的Android Studio中会遇到,通常是因为版本太新,AS自动将主题设为Theme.MaterialComponents.DayNight.DarkActionBar造成的,解决这个办法只需要将res/values/thems下的Theme.MaterialComponents.DayNight.DarkActionBar后加上.Bridge即可:
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Theme.Test" parent="Theme.MaterialComponents.DayNight.DarkActionBar.Bridge">
<!-- Primary brand color. -->
<item name="colorPrimary">@color/purple_500</item>
<item name="colorPrimaryVariant">@color/purple_700</item>
<item name="colorOnPrimary">@color/white</item>
<!-- Secondary brand color. -->
<item name="colorSecondary">@color/teal_200</item>
<item name="colorSecondaryVariant">@color/teal_700</item>
<item name="colorOnSecondary">@color/black</item>
<!-- Status bar color. -->
<item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
<!-- Customize your theme here. -->
</style>
</resources>