Android 自定义属性以及主题

前言

前段时间偶然看到了鸿洋大神公共账号的一篇关于自定义属性的推文,当时只是粗略的看了一下,似懂非懂,后来在自己的项目中有用到attr 自定义属性,结果出了Caused by: java.lang.UnsupportedOperationException: Failed to resolve attrib,关于这类问题可以看UnsupportedOperationException这篇博客,讲的很详细。后来我又遇到了其他的问题,真是不懂什么就会来什么问题。作为一个程序猿来讲,遇到问题当然要刨根问题,迎难而上,这边博客也跟着问题讲一下自定义属性以及构造函数的写法。

报错 log

D/AndroidRuntime(  819): Shutting down VM
W/dalvikvm(  819): threadid=1: thread exiting with uncaught exception (group=0x416aaba8)
E/AndroidRuntime(  819): FATAL EXCEPTION: main
E/AndroidRuntime(  819): Process: com.xxx.update, PID: 819
E/AndroidRuntime(  819): android.view.InflateException: Binary XML file line #0: Error inflating class android.widget.RelativeLayout
E/AndroidRuntime(  819): 	at android.view.LayoutInflater.createView(LayoutInflater.java:620)
E/AndroidRuntime(  819): 	at com.android.internal.policy.impl.PhoneLayoutInflater.onCreateView(PhoneLayoutInflater.java:56)
E/AndroidRuntime(  819): 	at android.view.LayoutInflater.onCreateView(LayoutInflater.java:669)
E/AndroidRuntime(  819): 	at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:694)
E/AndroidRuntime(  819): 	at android.view.LayoutInflater.inflate(LayoutInflater.java:469)
E/AndroidRuntime(  819): 	at android.view.LayoutInflater.inflate(LayoutInflater.java:397)
E/AndroidRuntime(  819): 	at android.view.LayoutInflater.inflate(LayoutInflater.java:353)
E/AndroidRuntime(  819): 	at com.xxx.update.utils.EnforceUpdateDialog.onCreate(EnforceUpdateDialog.java:155)
E/AndroidRuntime(  819): 	at android.app.Dialog.dispatchOnCreate(Dialog.java:373)
E/AndroidRuntime(  819): 	at android.app.Dialog.show(Dialog.java:269)
E/AndroidRuntime(  819): 	at com.xxx.update.utils.EnforceUpdateDialog.show(EnforceUpdateDialog.java:263)
E/AndroidRuntime(  819): 	at com.xxx.update.utils.UpdateService.createDialog(UpdateService.java:476)
E/AndroidRuntime(  819): 	at com.xxx.update.utils.UpdateService.access$700(UpdateService.java:48)
E/AndroidRuntime(  819): 	at com.xxx.update.utils.UpdateService$3.handleMessage(UpdateService.java:151)
E/AndroidRuntime(  819): 	at android.os.Handler.dispatchMessage(Handler.java:102)
E/AndroidRuntime(  819): 	at android.os.Looper.loop(Looper.java:136)
E/AndroidRuntime(  819): 	at android.app.ActivityThread.main(ActivityThread.java:5017)
E/AndroidRuntime(  819): 	at java.lang.reflect.Method.invokeNative(Native Method)
E/AndroidRuntime(  819): 	at java.lang.reflect.Method.invoke(Method.java:515)
E/AndroidRuntime(  819): 	at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:788)
E/AndroidRuntime(  819): 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:604)
E/AndroidRuntime(  819): 	at dalvik.system.NativeStart.main(Native Method)
E/AndroidRuntime(  819): Caused by: java.lang.reflect.InvocationTargetException
E/AndroidRuntime(  819): 	at java.lang.reflect.Constructor.constructNative(Native Method)
E/AndroidRuntime(  819): 	at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
E/AndroidRuntime(  819): 	at android.view.LayoutInflater.createView(LayoutInflater.java:594)
E/AndroidRuntime(  819): 	... 21 more
E/AndroidRuntime(  819): Caused by: android.content.res.Resources$NotFoundException: Resource is not a Drawable (color or path): TypedValue{t=0x2/d=0x7f010004 a=-1}
E/AndroidRuntime(  819): 	at android.content.res.Resources.loadDrawable(Resources.java:2068)
E/AndroidRuntime(  819): 	at android.content.res.TypedArray.getDrawable(TypedArray.java:602)
E/AndroidRuntime(  819): 	at android.view.View.<init>(View.java:3554)
E/AndroidRuntime(  819): 	at android.view.View.<init>(View.java:3484)
E/AndroidRuntime(  819): 	at android.view.ViewGroup.<init>(ViewGroup.java:464)
E/AndroidRuntime(  819): 	at android.widget.RelativeLayout.<init>(RelativeLayout.java:236)
E/AndroidRuntime(  819): 	... 24 more
I/Process (  819): Sending signal. PID: 819 SIG: 9
I/ActivityManager(  500): Process com.xxx.update (pid 819) has died.

随着博客的深入,这个问题后面会有讲到。

遇到的问题

1. dialog 无法全屏
2. 填充布局的几种写法
3. dialog的样式主题以及构造函数的写法

Dialog 设置全屏的写法

private void createDialog() {
CustomDialog dialog=new CustomDialog(this);
dialog.show();
}
-----------------------------
public class CustomDialog extends Dialog {
private View view;
public CustomDialog(@NonNull Context context) {
super(context);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
view = getLayoutInflater().inflate(R.layout.dialog_layout_dark, null);
setContentView(view);
}
}

如果你是直接这么创建的dialog 没有传入任何的主题,你会发现dialog是这样的。
dialog无法全屏
根据源码里面的构造函数调用,会默认用R.attr.dialogTheme主题,然后在show()方法中加载窗口。

    public void show() {
        if (mShowing) {
            if (mDecor != null) {
                if (mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) {
                    mWindow.invalidatePanelMenu(Window.FEATURE_ACTION_BAR);
                }
                mDecor.setVisibility(View.VISIBLE);
            }
            return;
        }

        mCanceled = false;

        if (!mCreated) {
            dispatchOnCreate(null);
        } else {
            // Fill the DecorView in on any configuration changes that
            // may have occured while it was removed from the WindowManager.
            final Configuration config = mContext.getResources().getConfiguration();
            mWindow.getDecorView().dispatchConfigurationChanged(config);
        }

        onStart();
        mDecor = mWindow.getDecorView();

        if (mActionBar == null && mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) {
            final ApplicationInfo info = mContext.getApplicationInfo();
            mWindow.setDefaultIcon(info.icon);
            mWindow.setDefaultLogo(info.logo);
            mActionBar = new WindowDecorActionBar(this);
        }

        WindowManager.LayoutParams l = mWindow.getAttributes();
        if ((l.softInputMode
                & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) == 0) {
            WindowManager.LayoutParams nl = new WindowManager.LayoutParams();
            nl.copyFrom(l);
            nl.softInputMode |=
                    WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
            l = nl;
        }

        mWindowManager.addView(mDecor, l);
        mShowing = true;

        sendShowMessage();
    }

我查了一下网上的设置主题全屏的方法,有两种:
1、重写onShow 方法

    @Override
    public void show() {
        super.show();
        //set the dialog fullscreen
        /**
         * 设置宽度全屏,要设置在show的后面
         */
        WindowManager.LayoutParams layoutParams = getWindow().getAttributes();
        layoutParams.gravity = Gravity.CENTER;
        layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT;
        layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT;
        getWindow().getDecorView().setPadding(0, 0, 0, 0);
        getWindow().setAttributes(layoutParams);

    }

2、代码里面设置全屏

getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);

注意这个设置窗口的全屏一定要在setContentView之后,否则会报错,因为要先加载出窗口才能设置窗口的大小。

但是在博主写的demo中这两种方法都没有效果,最后发现是我Dialog中的构造函数没有传入Style 主题。于是我继续写。
1、首先在Style.xml中新建一个

<style name="DialogStyle" parent="Base.Theme.AppCompat.Dialog">
        <item name="windowNoTitle">true</item>
        <item name="android:windowFullscreen">true</item>
    </style>

2、将Dialog 的构造函数改成这样

public CustomDialog(@NonNull Context context) {
        super(context,R.style.Dialog);
    }

以为万事大吉的时候,呵呵还是不能全屏,在dialog的四周总是有几dp的间隔。
毫无头绪的时候我想起来了以前写的代码设置成功的全屏的dialog的写法,然后对比了一下发现将dialog继承的主题换成这个就OK了。

<style name="Dialog" parent="Animation.AppCompat.Dialog">
        <item name="android:windowFullscreen">true</item>
        <item name="android:windowCloseOnTouchOutside">true</item>
    </style>

本着刨根问底的精神去跟代码对比Animation.AppCompat.DialogBase.Theme.AppCompat.Dialog结果一层层parentd的主题跟下去发现Base.Theme.AppCompat.Dialog最终的主题属性里面有

<item name="listPreferredItemPaddingLeft">10dip</item>
<item name="listPreferredItemPaddingRight">10dip</item> 
<item name="listPreferredItemPaddingStart">10dip</item>
<item name="listPreferredItemPaddingEnd">10dip</item>

Animation.AppCompat.Dialog是没有的,问题到这里应该已经水落石出了,但是我之前我其实知道Dialog 不能全屏可能是主题里面有这些设置padding 的属性,但是理论上我上面的那种重写Dialog 的Show(),在里面设置
getWindow().getDecorView().setPadding(0, 0, 0, 0); 就可以,但是我还没搞明白为啥设置的setPadding无效,如果有读者知道的话麻烦一定评论告知一下本菜逼。
关于这个Dialog 全屏的问题,如果不是写博客总结的话我也没有发现会有这么多问题,平时只知道这样写就OK了,但是不知道为什么,果然还是太菜了,另外,这个Style 继承的parent 是不能乱写的,否则就可能会出现设置属性依然不能全屏的问题。

利用布局填充器填充布局的几种方式

这里做一个笔记总结,直接贴出来:

一、第一种方式
LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(R.layout.test_layout, null);
setContent(view);

二、第二种方式
LayoutInflater inflater = LayoutInflater.from(MainActivity.this); 
View view = inflater.inflate(R.layout.test_layout, null);
setContent(view);

三、第三种方式
View view = View.inflate(MainActivity.this, R.layout.test_layout, null);
setContent(view);

创建一个自定义列表

Markdown
Text-to-HTML conversion tool
Authors
John
Luke
  • 关于 Flowchart流程图 语法,参考 这儿.

导出与导入

导出

如果你想尝试使用此编辑器, 你可以在此篇文章任意编辑。当你完成了一篇文章的写作, 在上方工具栏找到 文章导出 ,生成一个.md文件或者.html文件进行本地保存。

导入

如果你想加载一篇你写过的.md文件或者.html文件,在上方工具栏可以选择导入功能进行对应扩展名的文件导入,
继续你的创作。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Mrsongs的心情杂货铺

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值