最初在列表中不可见时未选中 MultiSelectListPreference 复选框 (API 23)

MultiSelectListPreference checkboxes not checked when not initially visible in list (API 23)

给定一个仅包含 MultiSelectListPreference 及其数组资源中的条目、值和默认值的 SettingsActivity,尽管 Android 知道它们应该被选中,但某些复选框并未绘制为已选中。单击未选中的项目时,该项目保持未选中状态(因为 Android 认为它正在取消选中已选中的项目)。

Here's a video of this happening

此处提供项目:https://github.com/ImmersibleElf/MSLPBug

它似乎在 API 21 和 22 中工作正常,但在 23 中不行。这可能是视图回收中的错误?或者可能是什么原因?

SettingsActivity.java

package com.immersibleelf.mslpbug;

import android.os.Bundle;
import android.preference.PreferenceFragment;
import android.support.v7.app.AppCompatActivity;

public class SettingsActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Display the fragment as the main content.
        getFragmentManager().beginTransaction()
                .replace(android.R.id.content, new SettingsFragment())
                .commit();
    }

    @Override
    public void onPause() {
        super.onPause();
    }

    public static class SettingsFragment extends PreferenceFragment {
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);

            // Load the settings from an XML resource
            addPreferencesFromResource(R.xml.settings);
        }
    }
}

settings.xml

<?xml version="1.0" encoding="utf-8"?>

<PreferenceScreen
    xmlns:android="http://schemas.android.com/apk/res/android">

        <MultiSelectListPreference
            android:key="mslp_key"
            android:title="MultiSelectListPreference"
            android:entries="@array/mslp_entries"
            android:entryValues="@array/mslp_entry_values"
            android:defaultValue="@array/mslp_default_value"
            android:persistent="true"
            />
</PreferenceScreen>

arrays.xml

<?xml version="1.0" encoding="utf-8"?>

<resources>

    <string-array name="mslp_entries">
        <item>Entry 01</item>
        <item>Entry 02</item>
        <item>Entry 03</item>
        <item>Entry 04</item>
        <item>Entry 05</item>
        <item>Entry 06</item>
        <item>Entry 07</item>
        <item>Entry 08</item>
        <item>Entry 09</item>
        <item>Entry 10</item>
        <item>Entry 11</item>
        <item>Entry 12</item>
        <item>Entry 13</item>
        <item>Entry 14</item>
        <item>Entry 15</item>
    </string-array>

    <string-array name="mslp_entry_values">
        <item>1</item>
        <item>2</item>
        <item>3</item>
        <item>4</item>
        <item>5</item>
        <item>6</item>
        <item>7</item>
        <item>8</item>
        <item>9</item>
        <item>10</item>
        <item>11</item>
        <item>12</item>
        <item>13</item>
        <item>14</item>
        <item>15</item>
    </string-array>

    <string-array name="mslp_default_value">
        <item>1</item>
        <item>2</item>
        <item>3</item>
        <item>4</item>
        <item>5</item>
        <item>6</item>
        <item>7</item>
        <item>8</item>
        <item>9</item>
        <item>10</item>
        <item>11</item>
        <item>12</item>
        <item>13</item>
        <item>14</item>
        <item>15</item>
    </string-array>

</resources>

解决方法是创建 MultiSelectListPreference 的子 class 并像这样覆盖方法 showDialog:

@Override
protected void showDialog(Bundle state) {
    super.showDialog(state);

    AlertDialog dialog = (AlertDialog)getDialog();
    if (dialog == null)
        return;

    if (Build.VERSION.SDK_INT >= 23) {
        ListView listView = dialog.getListView();

        listView.setOnScrollListener(new AbsListView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(AbsListView view, int scrollState) {
                int size = view.getChildCount();
                for (int i=0; i<size; i++) {
                    View v = view.getChildAt(i);
                    if (v instanceof CheckedTextView)
                        ((CheckedTextView)v).refreshDrawableState();
                }
            }

            @Override
            public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
                int size = view.getChildCount();
                for (int i=0; i<size; i++) {
                    View v = view.getChildAt(i);
                    if (v instanceof CheckedTextView)
                        ((CheckedTextView)v).refreshDrawableState();
                }
            }
        });
    }
}

非常好。此代码的工作方式与 mdiener 提到的一样:

import android.app.AlertDialog;
import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import android.preference.MultiSelectListPreference;
import android.util.AttributeSet;
import android.view.View;
import android.widget.AbsListView;
import android.widget.CheckedTextView;
import android.widget.ListView;

/**
 * Android bug: https://code.google.com/p/android/issues/detail?id=205487
 */
public class MultiSelectWrapper extends MultiSelectListPreference {

public MultiSelectWrapper(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
    super(context, attrs, defStyleAttr, defStyleRes);
}

public MultiSelectWrapper(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
}

public MultiSelectWrapper(Context context, AttributeSet attrs) {
    super(context, attrs);
}

public MultiSelectWrapper(Context context) {
    super(context);
}

@Override
protected void showDialog(Bundle state) {
    super.showDialog(state);

    AlertDialog dialog = (AlertDialog)getDialog();
    if (dialog == null)
        return;

    if (Build.VERSION.SDK_INT >= 23) {
        ListView listView = dialog.getListView();

        listView.setOnScrollListener(new AbsListView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(AbsListView view, int scrollState) {
                int size = view.getChildCount();
                for (int i=0; i<size; i++) {
                    View v = view.getChildAt(i);
                    if (v instanceof CheckedTextView)
                        ((CheckedTextView)v).refreshDrawableState();
                }
            }

            @Override
            public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
                int size = view.getChildCount();
                for (int i=0; i<size; i++) {
                    View v = view.getChildAt(i);
                    if (v instanceof CheckedTextView)
                        ((CheckedTextView)v).refreshDrawableState();
                }
            }
        });
    }
}
}