Was PreferenceFragment intentionally excluded from

2019-01-03 07:42发布

I'm looking to write preferences that can be applied to both 3.0 and pre-3.0 devices. Discovering that PreferenceActivity contains deprecated methods (although these are used in the accompanying sample code), I looked at PreferenceFragement and the compatibility package to solve my woes.

It appears, though, that PreferenceFragment isn't in the compatibility package. Can anyone tell me whether this was intentional? If so, can I easily target a range of devices (i.e. < 3.0 and >=3.0) or will I have to jump through hoops? If it wasn't intentionally excluded, can we expect a new release of the compatibility package? Or is there another workaround that is safe to use?

Cheers

James

8条回答
地球回转人心会变
2楼-- · 2019-01-03 08:07

I needed integrate Preferences into application's design and keep support for 2.3 android. So I still needed PreferencesFragment.

After some search I found android-support-v4-preferencefragment lib. This lib save a lot of time for copying and refactoring original PreferencesFragment as Tenacious said. Works fine and users enjoy preferences.

查看更多
兄弟一词,经得起流年.
3楼-- · 2019-01-03 08:11

Tenacious's answer is correct, but here are some more details.

The reason you can't "create a normal layout and bind the view components to the sharedprefs manually" is that there are some surprising omissions in the android.preferences API. PreferenceActivity and PreferenceFragment both have access to critical non-public PreferenceManager methods, without which you can't implement a preference UI of your own.

In particular, to construct a Preference hierarchy from an XML file you need to use a PreferenceManager, but all of PreferenceManager's constructors are either package-private or hidden. The method of attaching the Preference onClick listeners to your activity is also package-private.

And you can't work around this by sneakily putting your implementation in the android.preferences package, because non-public methods in Android APIs are actually omitted from the SDK. With a bit of creativity involving reflection and dynamic proxies, you can still get at them. The only alternative, as Tenacious says, is to fork the entire android.preference package, including at least 15 classes, 5 layouts, and a similar number of style.xml and attrs.xml elements.

So to answer the original question, the reason Google didn't include PreferenceFragment in the compatibility package is that they would have had exactly the same difficulty as Tenacious and myself. Even Google can't go back in time and make those methods public in the old platforms (though I hope they do that in future releases).

查看更多
Bombasti
4楼-- · 2019-01-03 08:12

See PreferenceFragment-Compat from Machinarius. It was easy to drop in with gradle and I forget that it's even there.

compile 'com.github.machinarius:preferencefragment:0.1.1'

Important Update: The latest revision of the v7 support library now has a native PreferenceFragmentCompat.

查看更多
Bombasti
5楼-- · 2019-01-03 08:20

The subtle implication of the answer from @CommonsWare is that - your app must choose between the compatibility API or the built-in fragment API (since SDK 11 or so). In fact that's what the "easily" recommendation has done. In other words, if you want to use PreferenceFragment your app needs to use the built-in fragment API and deal with the deprecated methods on PreferenceActivity. Conversely, if it's important that your app use the compat. API you will be faced with not having a PreferenceFragment class at all. Thus, targeting devices is not a problem, but the hoop-jumping happens when you have to choose one or the other API and thus submit your design to unforeseen workarounds. I need the compat. API so I'm going to create my own PreferenceFragment class and see how that works. In the worst case scenario I'll just create a normal (fragment) layout and bind the view components to the sharedprefs manually...ugh.

EDIT: After trying and looking at the code at http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.0.1_r1/android/preference/PreferenceFragment.java?av=h -- creating my own PreferenceFragment isn't going to happen. It appears the liberal use of package-private in PreferenceManager instead of 'protected' is the main blocker. It really doesn't look like there's any security or really good motivation to have done that and it isn't great for unit-testing but oh well...less typing I guess...

EDIT v2: Actually it did happen and it worked. It was definitely a headache to make the code work with the Compatibility API JAR. I had to copy about 70% the com.android.preference package from the SDK to my app and then wrestle with typically mediocre-quality Java code in Android. I used v14 of the SDK. It would have been much easier for a Goog engineer to do what I did, contrary to what I've heard some lead Android engineers say about this topic.

BTW - did I say "targeting devices is not a problem"? It totally is...if you use com.android.preference you are not going to be able to swap out with the Compatibility API without major refactoring. Fun log!

查看更多
萌系小妹纸
6楼-- · 2019-01-03 08:25

My app target is API +14 but due to using support library for some fancy navigation, I couldn't use the android.app.Fragment and had to use android.support.v4.app.Fragment, but I also needed to have PreferenceFragment in place without large changes to code behind.

So my easy fix for having both worlds of support library and PreferenceFragment:

private android.support.v4.app.Fragment fragment;
private android.app.Fragment nativeFragment = null;

private void selectItem(int position) {
    fragment = null;
    boolean useNativeFragment = false;
    switch (position) {
    case 0:
        fragment = new SampleSupprtFragment1();
        break;
    case 1:
        fragment = new SampleSupprtFragment2();
        break;
    case 2:
        nativeFragment = new SettingsFragment();
        useNativeFragment = true;
        break;
    }
    if (useNativeFragment) {
        android.app.FragmentManager fragmentManager = getFragmentManager();
        fragmentManager.beginTransaction()
            .replace(R.id.content_frame, nativeFragment).commit();
    } else {
        if (nativeFragment != null) {
            getFragmentManager().beginTransaction().remove(nativeFragment)
                .commit();
            nativeFragment = null;
        }
        FragmentManager fragmentManager = getSupportFragmentManager();
        fragmentManager.beginTransaction()
            .replace(R.id.content_frame, fragment).commit();
    }
}
查看更多
甜甜的少女心
7楼-- · 2019-01-03 08:26

Building upon CommonsWare's answer as well as Tenacious' observations, I have come up with a single descendant class solution capable of targeting all current Android API versions with minimal fuss and no code or resource duplication. Please see my answer to the related question over here: PreferenceActivity Android 4.0 and earlier

or on my blog: http://www.blackmoonit.com/2012/07/all_api_prefsactivity/

Tested on two tablets running 4.0.3 and 4.0.4 as well as a phone running 4.0.4 and 2.3.3 and also an emulator running 1.6.

查看更多
登录 后发表回答