Android编程坑集合(更新中……)

写在前面

作者在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>


二、其它

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值