gregkorossy / Android-Support-Preference-V7-Fix

Android androidx.preference support library has some issues, this lib tries to fix them.
https://discord.gg/87NVsSK
Apache License 2.0
497 stars 46 forks source link

Feedback problem #92

Closed tcqq closed 7 years ago

tcqq commented 7 years ago

material design specification the dividing line should be added below the grouping,how to add the division line below the group?

Like this: https://material.io/guidelines/patterns/settings.html#settings-usage

gregkorossy commented 7 years ago

Use the DIVIDER_CATEGORY_BEFORE_FIRST | DIVIDER_CATEGORY_BETWEEN flags to achieve the desired behavior.

A complete example for your fragment extending PreferenceFragmentCompatDividers:

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, @Nullable Bundle savedInstanceState) {
    try {
        return super.onCreateView(inflater, container, savedInstanceState);
    } finally {
        setDividerPreferences(DIVIDER_CATEGORY_BEFORE_FIRST | DIVIDER_CATEGORY_BETWEEN);
    }
}

Note that you can also use DIVIDER_PREFERENCE_AFTER_LAST instead of DIVIDER_CATEGORY_BEFORE_FIRST, it should depend on your needs.


As for the independent lines between certain "groups" without the group titles (e.g. in your example the line between the "Do not disturb" and "Phone ringtone" preferences), it's not possible because if you don't specify the title for the PreferenceCategory, it will still use the same layout without the title being there, which makes it look like a huge empty space. Is this something you want to also include or you just wanted to know the config for the divider flags?

tcqq commented 7 years ago

Ok thanks

tcqq commented 7 years ago

But when I check the CheckBox, then I turn the phone into a horizontal screen, CheckBox status will be changed to not checked

gregkorossy commented 7 years ago

You probably set the persistent attribute to false for the CheckBoxPreference in your XML.

tcqq commented 7 years ago

No, the Activity will be reset when the phone screen rotates

tcqq commented 7 years ago

When the Activity is reset, the state of the control will not be saved

tcqq commented 7 years ago

Such as the screen before the rotation of the control state is true, and then I click to false, and then when the screen rotation, the control state will become true

gregkorossy commented 7 years ago

If you have the persistent attribute set to true (default), the value will be saved immediately. Note, however, that you need to specify the key for the preference in order to be saved.

tcqq commented 7 years ago

i dont understand

gregkorossy commented 7 years ago

Please, post your preferences XML. You probably didn't setup the XML well.

tcqq commented 7 years ago

I set persistent to true , and when the Activity is reset, the control state will not be saved

tcqq commented 7 years ago

<?xml version="1.0" encoding="utf-8"?> <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto">

<PreferenceCategory android:title="PreferenceCategory using preference_fallback_accent_color below API 21">

</PreferenceCategory>
<PreferenceCategory android:title="Another category">
    <EditTextPreference
        android:dialogMessage="This is the dialog\'s message"
        android:inputType="phone"
        android:key="edit_text_test"
        android:persistent="true"
        android:summary="It should be a phone input (it is now!)"
        android:title="EditTextPreference" />
    <ListPreference
        android:dialogTitle="ListPreference dialog"
        android:entries="@array/pref_list_entries"
        android:entryValues="@array/pref_list_values"
        android:key="pref_syncConnectionType"
        android:persistent="true"
        android:summary="%s"
        android:title="ListPreference" />
    <DropDownPreference
        android:dialogTitle="DropDownPreference dialog"
        android:entries="@array/pref_list_entries"
        android:entryValues="@array/pref_list_values"
        android:persistent="true"
        android:summary="%s"
        android:title="DropDownPreference" />
    <PreferenceScreen
        android:key="preference_screen_test"
        android:persistent="true"
        android:title="PreferenceScreen test">
        <Preference
            android:persistent="true"
            android:title="A preference" />
        <SwitchPreferenceCompat
            android:persistent="true"
            android:summary="But this is a SwitchPreferenceCompat"
            android:title="Another one" />
    </PreferenceScreen>
</PreferenceCategory>
<PreferenceCategory
    android:key="pref_extras"
    android:title="Extra preferences (available separately)">
    <RingtonePreference
        android:dialogTitle="@string/ringtone_picker_title"
        android:key="pref_ringtone"
        android:persistent="true"
        android:ringtoneType="ringtone"
        android:showDefault="true"
        android:showSilent="true"
        android:summary="Select a nice ringtone"
        android:title="RingtonePreference"
        app:showAdd="true"
        app:summaryHasRingtone="The best ringtone: %s" />
    <TimePickerPreference
        android:key="pref_time"
        android:persistent="true"
        android:summary="Select the perfect time"
        android:title="TimePickerPreference"
        app:hourFormat="auto"
        app:pickerTime="19:35"
        app:summaryHasTime="Selected time: %s" />
    <DatePickerPreference
        android:key="pref_date"
        android:persistent="true"
        android:summary="Select a date"
        android:title="DatePickerPreference"
        app:minDate="08/31/2017"
        app:summaryHasDate="Selected date: %s" />
    <ColorPickerPreference
        android:dialogTitle="@string/color_picker_default_title"
        android:key="pref_color"
        android:persistent="true"
        android:summary="Very nice color"
        android:title="ColorPickerPreference"
        app:size="small" />
</PreferenceCategory>
<PreferenceCategory
    android:key="pref_categ"
    android:title="Dynamic category">
    <Preference
        android:key="pref_add"
        android:persistent="true"
        android:summary="Add preference"
        android:title="Preference" />
</PreferenceCategory>

gregkorossy commented 7 years ago

I don't see the CheckBoxPreference in your code, but I see that, for example, the SwitchPreferenceCompat has no key attribute. This means that the preference cannot be saved as there is no key associated with it, which means that when you rotate the phone, there's no persisted value that could be loaded.

tcqq commented 7 years ago

ok,i understand,add key will successful,thank you

tcqq commented 7 years ago

Does this library support dynamic addition of data? Because the data returned by the server is dynamic

gregkorossy commented 7 years ago

Here's an example:

<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
    <CheckBoxPreference
        android:key="test_checkbox_01"
        android:summary="This will be saved properly"
        android:title="CheckBoxPreference 1" />
    <CheckBoxPreference
        android:key="test_checkbox_02"
        android:persistent="false"
        android:summary="This will not be persisted (persistent = false), but survives rotation (has key)"
        android:title="CheckBoxPreference 2" />
    <CheckBoxPreference
        android:summary="This will not be saved, and does not survive rotation (missing key)"
        android:title="CheckBoxPreference 3" />
</PreferenceScreen>

As you can see, CheckBoxPreference 1 has a key attribute set to "test_checkbox_01" and the persistent is not set to anything (it's default value is true), so the value of the checkbox will be saved to the SharedPreferences.

CheckBoxPreference 2 has a key attribute ("test_checkbox_02") but its persistent attribute is set to false, so it will survive the configuration change (rotation) of the device, but it will not be saved to the SharedPreferences. If you leave the app (press the back button) and open it again, the checkbox won't be checked because of this.

CheckBoxPreference 3 is missing the key attribute entirely, so it won't be saved to the SharedPreferences nor will it be restored when you rotate the device because the system cannot save the value of it to an instance state due to the missing key.


As for the other question: what do you mean by dynamic addition of data? The preferences can be added dynamically to the preference screen, the sample app has an example of this in the "Dynamic category" where if you press the Preference (Add preference), it will add a new preference to that category.

tcqq commented 7 years ago

The server returns json, and the Preference is displayed according to the json data, like facebook more activity

gregkorossy commented 7 years ago

Check out the testDynamicPrefs() method that shows you how to add preferences dynamically to a category. It's the same when you add the preference to the preference screen which you can get by calling [getPreferenceScreen()](https://developer.android.com/reference/android/support/v7/preference/PreferenceFragmentCompat.html#getPreferenceScreen()). If you build the entire preference screen dynamically, just create an almost empty XML with only the PreferenceScreen tag in it that will be your root preference. Load it like you do now, then use getPreferenceScreen() to get a reference to it, then just add the new preferences by calling addPreference(...).

tcqq commented 7 years ago

Ok thank you,I check now

tcqq commented 7 years ago

Now "SeekBarPreference" the "seekBarValue" control is displayed on the right side of the progress bar, allowing "seekBarValue" to appear above the progress bar, can you? qq 20170922094729

gregkorossy commented 7 years ago

No, you need a custom layout XML to allow the value holder TextView to be moved to a different place. It'd be best to copy and modify the preference_widget_seekbar_material.xml file(s).

Note that those files are probably not needed anymore as it seems like the Google guys fixed the layout of the seekbar, but I haven't tested it yet.

tcqq commented 7 years ago

ok, i saw it, but "seekBarValue" is not real-time, can make "seekBarValue" with "SeekBar" drag the number of real-time changes value?

gregkorossy commented 7 years ago

No, that would require some heavy reflection usage throughout (or the complete rewrite of) the whole SeekBarPreference class.

tcqq commented 7 years ago

But adjust the volume is generally real-time

gregkorossy commented 7 years ago

Well, tell this to the Google devs 😄 But IMO, it's not a huge problem if you don't display the exact value because no one cares about it, the bar itself represents the volume and probably all users can understand the meaning of it without seeing the actual value.

tcqq commented 7 years ago

I changed the layout (layout-v21, layout-v14) layout, deleted (layout-v17), I think now looks much better than before ibin f7 1 nvhp_8g4 ao3

layout.zip

gregkorossy commented 7 years ago

I won't change it though, since no one has had problems with it so far and anyone can easily override the layout files (just like you did) if they have special needs.

tcqq commented 7 years ago

Yes, honestly now the layout looks better than before, so do not need to add the code to the project, directly use the gradle link can be used directly, more convenient, you can compare

gregkorossy commented 7 years ago

By the way, the no real-time-update probably has practical reasons: if it updated the value real-time, the SharedPreferences would get a ton of updates, stressing the device unnecessarily. Updating only the TextView would do no good probably since, as you said, you wanted to listen to the changes which only happen before the value gets persisted. If the user moved the thumb from 100 to 0, it would send 100 change notifications, again, stressing the device. It might even make the UI become laggy.

Of course, it would be possible to update the value on the UI and create a separate interface for listening for updates without persisting the value, but that's really some serious effort just to display and use the value in real-time, yet it is probably not even needed most of the time.

tcqq commented 7 years ago

Now there is a library that implements real-time updates and you can look at his code

https://github.com/consp1racy/android-support-preference#dividers

gregkorossy commented 7 years ago

I know how to do it, the only problem with the SeekBarPreference is that everything is private so simply extending the class and overriding a few things is not possible. Instead, one would have to basically copy the whole class then make the necessary changes. This makes the updating process time consuming, since the new class should reflect every change from the official class, which is kind of a diffing between two classes.

As you can see, the creator of that lib also recreated every single preference type which means a bigger library and slower updates. That library uses 23.x.x dependencies (according to this page) while this one is and always has been on the latest version (26.1.0 at the time of writing this) within days (or sometimes just hours) since there's usually no need to rewrite complex classes to make it work when a new support lib version is released (which happens usually once or twice a month; major releases happen yearly).

TL;DR: Re-implementing every preference to make them fit the custom needs results in bigger lib size and slower updates.

tcqq commented 7 years ago

I think you're right, you thinking is very comprehensive

You have time can look at i in above send the layout, I tested

tcqq commented 7 years ago

If use svn as an icon, can dynamically change icon color in the code?

Like this:https://github.com/AAkira/CompoundIconTextView

gregkorossy commented 7 years ago

You cannot use SVGs in Android but vector drawables (they are not the same), just to be clear.

However, the official Preference implementation does not work with vector drawables on devices with API version lower than 21 (so pre-lollipop) because it uses ContextCompat for inflating drawables and that's basically just a wrapper around getting them on different API levels. If you want to use vector drawables as icons, you have a few choices.

The next 3 uses vectorDrawables.useSupportLibrary = true so no PNGs will be generated.

Method 1 Use AppCompatResources.getDrawable(...) to inflate the icons and set the returned drawables as the appropriate preferences' icons using setIcon(...). The drawback is that you need to setup all of the preferences programmatically, which can be cumbersome.

Method 2 Use VectorDrawableCompat.create(...) to create the vector drawable. The difference between this and method 1 is that you cannot use this to inflate regular (i.e. non-vector) drawables. If you mix them, use method 1 instead.

Method 3 Use AppCompatDelegate.setCompatVectorFromResourcesEnabled(...) to enable vector drawable inflation automatically for every drawable container.

static {
    AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
}

Note, however, that this solution can introduce memory leaks and other problems to your app as the documentations states it:

This feature defaults to disabled, since enabling it can cause issues with memory usage, and problems updating Configuration instances. If you update the configuration manually, then you probably do not want to enable this. You have been warned.


Method * Use vectorDrawables.useSupportLibrary = false in your gradle. What this does is if your min SDK is set to less than 21, PNGs will be generated from the vector drawables for pre-lollipop devices. This means, of course, that you cannot use the tint attribute or theme related resources (like ?colorControlNormal) in the vector drawable because they are not known at compile time. If you want to tint the icons, you'll need to set the fillColor attribute to the desired value so they will be generated accordingly.

gregkorossy commented 7 years ago

I opened an issue to try to convince the devs to change ContextCompat to AppCompatResources in order to enable vector drawable loading with only the vectorDrawables.useSupportLibrary = true present in the gradle build file. If you want to, you can star it to show interest in the problem.

tcqq commented 7 years ago

Ok, thank you i will to try

tcqq commented 7 years ago

I'm not add this library:compile 'com.takisoft.fix:preference-v7-colorpicker:26.1.0.0',I would like to use the official "ColorPickerPreference", but app error,what reason?

Code:

<?xml version="1.0" encoding="utf-8"?> <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto">

<PreferenceCategory android:title="First category">

    <ColorPickerPreference
        android:key="pref_color"
        android:persistent="false"
        android:summary="Very nice color" />

</PreferenceCategory>

Error Log:

java.lang.NullPointerException: Attempt to invoke virtual method 'float android.view.View.getY()' on a null object reference at com.takisoft.fix.support.v7.preference.PreferenceFragmentCompatDividers$DividerItemDecoration.onDrawOver(PreferenceFragmentCompatDividers.java:288) at android.support.v7.widget.RecyclerView.draw(RecyclerView.java:3991) at android.view.View.updateDisplayListIfDirty(View.java:18069) at android.view.View.draw(View.java:18847) at android.view.ViewGroup.drawChild(ViewGroup.java:4214) at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4000) at android.view.View.updateDisplayListIfDirty(View.java:18060)

tcqq commented 7 years ago

I would like to use the official "ColorPickerPreference", what should I do?

gregkorossy commented 7 years ago

What Android version and device is this? Can you reproduce it with the sample app too?

tcqq commented 7 years ago

Pixel XL 8.0

gregkorossy commented 7 years ago

Can you reproduce it with the sample app too?

tcqq commented 7 years ago

Wait

tcqq commented 7 years ago

Done

tcqq commented 7 years ago

Ready

gregkorossy commented 7 years ago

I'm asking if you could reproduce it on your device using the sample app. Clone the project and install the sample app and check if you see the problem happening there. If not, there's probably something wrong with your app, otherwise there's a problem with the lib. I cannot reproduce it using the sample app, it works both on my device (7.0) and in the emulator (8.0).

tcqq commented 7 years ago

Yes i tested

tcqq commented 7 years ago

Please wait, uploading

gregkorossy commented 7 years ago

Why do you need to upload something for this? Just tell me if the exception was thrown or not.

tcqq commented 7 years ago

MyApplication.zip

tcqq commented 7 years ago

Yes