support包中preference下自定义原生alertdialog解决方案

场景

最近在开发UI控件库中需要对perference自定义样式,而EditTextPreference以及ListPreference等均使用到alertdialog,则需要对alertdialog的样式进行自定义修改

首先从demo出发,Android  Support包源码中sample并没有对perference作demo,网罗的一些demo,其中不乏两种方式使用support-preference

  1. preferences-demo
  2. platform_packages_apps_settings

preferences-demo采用的是AppCompatActivity – PreferenceFragmentCompat的搭配,这个时候你要使用appcompat的theme

platform_packages_apps_settings(android8.0 下settings app源码)采用的是Activity-PreferenceFragment的搭配(这个需要仔细去阅读android8.0 settings的源码,在这里不做详述),可以在activity下使用

使用这两种方式去展示时,会发现Activity-PreferenceFragment的搭配是不能通过在theme下重写alertdialogstyle的样式布局去改变perference控件(例如android.support.v7.preference.EditTextPreference、android.support.v7.preference.ListPreference)弹出的alertdialog。而AppCompatActivity – PreferenceFragmentCompat是可以的

那么为什么会这样?我们从两者的源码出发去探索

android.support.v7.preference. PreferenceFragmentCompat 源码:

@Override
public void onDisplayPreferenceDialog(Preference preference) {
    .....
    final DialogFragment f;
    if (preference instanceof EditTextPreference) {
        f = EditTextPreferenceDialogFragmentCompat.newInstance(preference.getKey());
    } else if (preference instanceof ListPreference) {
        f = ListPreferenceDialogFragmentCompat.newInstance(preference.getKey());
    } else if (preference instanceof AbstractMultiSelectListPreference) {
        f = MultiSelectListPreferenceDialogFragmentCompat.newInstance(preference.getKey());
    } else {
        throw new IllegalArgumentException("Tried to display dialog for unknown " +
            "preference type. Did you forget to override onDisplayPreferenceDialog()?");
    }
    .....
}

android.support.v14.preference.PreferenceFragment源码:

@Override
public void onDisplayPreferenceDialog(Preference preference) {
    ....
    final DialogFragment f;
    if (preference instanceof EditTextPreference) {
   	f = EditTextPreferenceDialogFragment.newInstance(preference.getKey());
    } else if (preference instanceof ListPreference) {
   	f = ListPreferenceDialogFragment.newInstance(preference.getKey());
    } else if (preference instanceof MultiSelectListPreference) {
    	f = MultiSelectListPreferenceDialogFragment.newInstance(preference.getKey());
    } else {
    	throw new IllegalArgumentException("Tried to display dialog for unknown " +
            "preference type. Did you forget to override onDisplayPreferenceDialog()?");
    }
    ....
}

即v14包中在PreferenceFragment下使用了EditTextPreferenceDialogFragment、ListPreferenceDialogFragment、MultiSelectListPreferenceDialogFragment,而他们继承了PreferenceDialogFragment

同样的 v7包中在PreferenceFragmentCompat下使用了EditTextPreferenceDialogFragmentCompat、ListPreferenceDialogFragmentCompat、MultiSelectListPreferenceDialogFragmentCompat,而他们继承了PreferenceDialogFragmentCompat。

那么我们接下来看一下 PreferenceDialogFragment 和 PreferenceDialogFragmentCompat 的源码:

android.support.v14.preference. PreferenceDialogFragment 源码:

@Override
public @NonNull Dialog onCreateDialog(Bundle savedInstanceState) {
    final Context context = getActivity();
    mWhichButtonClicked = DialogInterface.BUTTON_NEGATIVE;

    final android.app.AlertDialog.Builder builder = new AlertDialog.Builder(context)
            .setTitle(mDialogTitle)
            .setIcon(mDialogIcon)
            .setPositiveButton(mPositiveButtonText, this)
            .setNegativeButton(mNegativeButtonText, this);
	//...省略
    return dialog;
}

 

android.support.v7.preference.PreferenceDialogFragmentCompat源码:

@Override
public @NonNull Dialog onCreateDialog(Bundle savedInstanceState) {
    final Context context = getActivity();
    mWhichButtonClicked = DialogInterface.BUTTON_NEGATIVE;

    final android.support.v7.app.AlertDialog.Builder builder = new AlertDialog.Builder(context)
            .setTitle(mDialogTitle)
            .setIcon(mDialogIcon)
            .setPositiveButton(mPositiveButtonText, this)
            .setNegativeButton(mNegativeButtonText, this);
    //...省略 
    return dialog;
 }

v14下的PreferenceDialogFragment 使用的是android.app.AlertDialog,而v7下的PreferenceDialogFragmentCompat使用的是android.support.v7.app.AlertDialog

那么,接下来问题便转化成:

原生AlertDialog和v7下的AlertDialog有什么不同

我们知道,AlertDialog的源码使用了建造者模式,用到了AlertController去进行控制

com.android.internal.app. AlertController源码:

protected AlertController(Context context, DialogInterface di, Window window) {
    ...
    final TypedArray a = context.obtainStyledAttributes(null, R.styleable.AlertDialog,
                com.android.internal.R.attr.alertDialogStyle, 0);
    ...
}

 

android.support.v7.app. AlertController源码:

public AlertController(Context context, AppCompatDialog di, Window window) {
	...
    final TypedArray a = context.obtainStyledAttributes(null, R.styleable.AlertDialog,
                android.support.v7.appcompat.R.attr.alertDialogStyle, 0);
	...
}

于是乎比较原生和v7下的AlertController会发现原生使用的是com.android.internal.R.styleable.AlertDialog,我们是无法通过更改alertdialogstyle去修改原生的样式的,虽然官方在官方文档中有提供如下api

AlertDialog.Builder(Context context, int themeResId)

Creates a builder for an alert dialog that uses an explicit theme resource.

但是AlertDialog在v14中是以方法的局部变量使用的,这就导致了v14 PreferenceDialogFragment下使用的AlertDialog是无法通过在theme下重写alertdialogstyle的样式布局去改变perference控件样式布局。而v7使用的AlertDialog是可以通过在theme下重写alertdialogstyle这个style改变其样式以及布局的。

 

梳理一下

也即是说,在android.support.v14.preference.PreferenceFragment使用preference控件,弹出的AlertDialog会使用原生的样式以及布局,这是无法通过调用api改变的,而PreferenceFragment继承了android.app.Fragment,可以在Activity下使用,所以如果你在Activity-PreferenceFragment这套方案下使用时,无法改变AlertDialog的样式。

在v7的PreferenceFragmentCompat使用preference控件,弹出的AlertDialog是可以通过在theme下重写alertdialogstyle的样式布局去改变的

解决方案

那么接下来,为了适配更多的方式,我需要在android.support.v14.preference.PreferenceFragment弹出的AlertDialog去改变其样式和布局,想到了两种策略:

  1. hook注入,偷梁换柱
  2. 重新写一套v14下的PreferenceFragment,包括android.app.AlertDialog,改为自己使用的样式(其实在UI库中已经将android.app.AlertDialog重写了,所以这套方案那没有想象之中的困难)

api-hook这套方案需要用反射进去对android.app.AlertDialog进行修改

hook点如果是android.app.AlertDialog那么对整个R文件需要修改,工程量很大。

退一步,假如在android.support.v14.preference.PreferenceDialogFragment 的onCreateDialog(Bundle savedInstanceState)进行修改,那么其中AlertDialog为方法的局部变量,也没有办法反射,需要直接拿到onCreateDialog下的所有变量,对整个方法进行偷梁换柱,这种方法可行,但工程量较大。

接下来想到了还有aop进行hook注入,同样工作量不小。

而且如果使用hook的话,那么假如app接入了UI库运行在各种Android手机上,各种手机会对系统源码做定制,假如修改了android.app.AlertDialog的代码,而又对他进行hook时,这样就不安全了。

于是最后的方案选择了第二个方案:重新写一套v14下的PreferenceFragment

 

这次的思路应该一路下来看源码和做修改没有大差错,好处是自己熟悉系统以及兼容包源码中preference和alertdialog下配合使用的部分,也了解hook的一些局限性和导致的后果

附,参考文章:

android preference:

Android Preference 设置偏好全攻略

hook:

Android插件化原理解析——Hook机制之动态代理

理解 Android Hook 技术以及简单实战

 


31 条评论

昵称
  1. Hi there mates, pleasant article and nice urging commented at this place, I am actually enjoying by these. Antony Petros

  2. Thank you David and Vicki. I love and appreciate you both and your service in the Kingdom of a God. Terrance Ternes

  3. There is clearly a bundle to identify about this. I believe you made certain good points in features also. Kurtis Bansal

  4. russian viagra

    But wanna input on few general things, The website design and style is perfect, the content material is real excellent : D. Marshall Wycoff

  5. Yes! Finally someone writes about digital printing. Bertram Tetter

  6. Loveme

    In the comment given above, the load current for 63.91 kW output is wrongly mentioned as 127A it is 139A. Alvin Merz

  7. russian bet

    That is a great tip particularly to those new to the blogosphere. Sherman Muray

  8. Loveme

    I recommend to you to visit a site on which there are many articles on this question. Ricky Mowry

  9. russian viagra

    Great post. I will be facing some of these issues as well.. Kent Arcadipane

  10. sikis

    You made some nice points there. I did a search on the subject and found most guys will go along with with your website. Darren Norbury

  11. liseli

    Because the admin of this web site is working, no doubt very soon it will be well-known, due to its feature contents. Luther Mariello

  12. bahis oyna

    PSN Generator, we strive to be running out of interested people. Sydney Pen

  13. bahis oyna

    TY for this article, it has been totally helpful to me! Much better written than other bloggers out there. Norbert Jabaay

  14. sikis

    Hello. This article was extremely remarkable, particularly because I was investigating for thoughts on this matter last Monday. Rory Seagers

  15. bahis oyna

    Looking forward to reading more. Great blog. Thanks Again. Really Great. Cyrus Sierzenga

  16. sex videolari

    Dead composed articles, Really enjoyed looking through. Kati Frank Shipley

  17. bahis oyna

    I view something genuinely special in this web site. Hilario Naccari

  18. porno

    I reckon something really special in this website. Normand Nogueda

  19. Gayle Edouard

    Hello, did you know that there are 241,120 internet directories in the world.

    These websites are what drive traffic to YOUR business.

    Want more traffic? Want more Sales? We can help – today.

    Your website hugeterry.cn is listed in only 14 of these directories.

    Get more traffic for your China, People?s Republic of audience.

    Our automated system adds your website to all of the directories.

    You can find it here: getlisted.directory/hugeterry.cn

    Act today, and we will expedite your listings and waive the processing charge!

    We have a special going on. Use “FRIENDS” on checkout for a 50% discount valid today.

  20. Celina McMurtry

    Convert hugeterry.cn to an app with one click!

    80% of users browse websites from their phones.

    Have the App send out push notifications without any extra marketing costs!

    MakeMySiteMobile.com

  21. Kathy Noriega

    Hello, did you know that there are 241,120 internet directories in the world.

    These websites are what drive traffic to YOUR business.

    Want more traffic? Want more Sales? We can help – today.

    Your website hugeterry.cn is listed in only 14 of these directories.

    Get more traffic for your China, People?s Republic of audience.

    Our automated system adds your website to all of the directories.

    You can find it here: getlisted.directory/hugeterry.cn

    Act today, and we will expedite your listings and waive the processing charge!

  22. Ginger Stegall

    Your go-to source for leads. We can provide business to business and business to consumer leads, custom-tailored to your needs.

    CustomDatabases.org

  23. Katherine Ziemba

    Hello, from CustomData.click we are a provider of unique databases that could help your business.

    Please visit us at CustomData.click to see if we can help you.

    Regards,
    Katherine

  24. Ricardo Lalonde

    Hello.

    It is with sad regret to inform you TopDataList.com is shutting down.

    We have made all our databases available for you for a once off fee.

    Visit us on TopDataList.com

  25. Marietta Anglin

    ZippyLeads.org is running an easter special till the 18th of April.

    Get all the leads you need for your company with our easter special.

  26. 匿名

    优秀

  27. 1

    特特牛逼

  28. 匿名

    问题不大

  29. 匿名

    写的很nice

  30. 匿名

    写的不错

  31. 爸爸

    fb(fucking boy)(face book)(face brother)