7

我需要创建一个自定义 ListPreference 对话框,以便可以在列表 (ListView) 上方添加一些标题文本 (TextView)。我创建了 MyListPreference 类,它扩展了 ListPreference 并覆盖了 onCreateDialogView():

@Override
protected View onCreateDialogView() {
    LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    View v = (View) inflater.inflate(R.layout.dialog_preference_list, null);
    return v;
}

我的 XML 布局 dialog_preference_list.xml 包含:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="vertical" >

    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="TextView" />

    <ListView
        android:id="@android:id/list"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:drawSelectorOnTop="false"
        android:scrollbarAlwaysDrawVerticalTrack="true" />

</LinearLayout>

问题:TextView 显示在 ListView 下方而不是上方。我需要 TextView 在上面。我已经尝试过使用 LinearLayout 和 RelativeLayout(使用“下方”或“上方”属性)但没有成功:我找不到将 TextView 放在 ListView 上方的方法......布局非常简单,我看不到为什么列表保持在上面...另外,请注意问题出现在真实设备(Nexus 4、Android 4.2.2)和模拟器上。但是,当查看 Eclipse 的图形布局中呈现的布局时,布局是正确的!见附图。

关于如何解决这个问题的任何想法?

设备上呈现的布局(不正确):

设备上呈现的布局(不正确)

在 Eclipse 上呈现的布局(正确):

在 Eclipse 上呈现的布局(正确)

使用解决方案 10.07.2013 进行编辑

正如接受的答案所建议的那样,问题来自在 ListPreference 的 onPrepareDialogBu​​ilder() 中使用 builder.setSingleChoiceItems()。我通过扩展 ListPreference 并覆盖 onCreateDialogView() 来修复它以在没有构建器的情况下构建对话框,以便我可以创建一个自定义视图,显示列表项上方的标题文本。

GPListPreference.java:

public class GPListPreference extends ListPreference {
    ...

    @Override
    protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
        builder.setNegativeButton(null, null);
        builder.setPositiveButton(null, null);
    }

    private int getValueIndex() {
        return findIndexOfValue(getValue());
    }

    @Override
    protected View onCreateDialogView() {
        LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        ListView lv = (ListView) inflater.inflate(R.layout.dialog_preference_list, null);

        TextView header = (TextView) inflater.inflate(R.layout.dialog_preference_list_header, null);
        header.setText(getDialogMessage()); // you should set the header text as android:dialogMessage in the preference XML
        lv.addHeaderView(header);

        ArrayAdapter<CharSequence> adapter = new ArrayAdapter<CharSequence>(getContext(), R.layout.dialog_preference_list_singlechoice, getEntries());
        lv.setAdapter(adapter);

        lv.setClickable(true);
        lv.setEnabled(true);
        lv.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
        lv.setItemChecked(getValueIndex() + 1, true);
        lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {

            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                setValueIndex(position - 1);
                getDialog().dismiss();
            }
        });

        return lv;
    }
}

dialog_preference_list.xml:

<?xml version="1.0" encoding="utf-8"?>
<ListView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@android:id/list"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:drawSelectorOnTop="false"
    android:scrollbarAlwaysDrawVerticalTrack="true" />

dialog_preference_list_singlechoice.xml

<?xml version="1.0" encoding="utf-8"?>
<CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@android:id/text1"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:checkMark="?android:attr/listChoiceIndicatorSingle"
    android:ellipsize="marquee"
    android:gravity="center_vertical"
    android:minHeight="?android:attr/listPreferredItemHeight"
    android:paddingBottom="2dip"
    android:paddingLeft="10dip"
    android:paddingRight="10dip"
    android:paddingTop="2dip"
    android:textAppearance="?android:attr/textAppearanceMedium" />

dialog_preference_list_header.xml

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:padding="10dip"
    android:textAppearance="?android:attr/textAppearanceSmall">

</TextView>
4

4 回答 4

4

我认为问题在于 ListPreference 的工作方式。ListPreference 使用 Builder.setSingleChoiceItems() 来创建带有 RadioButtons 的行,并且它优先于您尝试添加的自定义布局(在您的情况下是 TextView 和 LinearLayout 内的 ListView。解决方案是扩展 DialogPreference。这里是一个指向 GitHub 的链接,我在其中创建了一个自定义 DialogPreference 来满足您的需求。我没有编写 RadioButton 逻辑。

于 2013-07-08T21:15:30.503 回答
2

我想这是一个主题问题。尝试在构造函数中更改对话框的主题,使其类似于setStyle(STYLE_NO_TITLE, R.style.AppTheme). 具有 no_title 样式的基本应用程序主题。

如果这不是问题,则可能与ListPreference课程本身有关。它可能会覆盖您的布局,以使首选项视图的主题保持一致。但是,我以前没有使用ListPreference过,所以它只是一个猜测。

您可以通过在 XML 图形布局预览中使用主题来重现相同的结果吗?

于 2013-07-08T20:23:23.133 回答
1

您可以尝试的另一个选项是将TextView作为标题添加到ListView这样的:

TextView textView = new TextView(getActivity());
ListView listView = new ListView(getActivity());
listView.addHeaderView(textView);

addHeaderView需要 aView所以理论上你有任何你想成为标题的东西,但我只使用了a TextView

于 2013-07-10T14:15:28.260 回答
0

上面的链接坏了。在这个解决方案中,想法是覆盖 ListPreference,并使用 ListPreference 上定义的数据来膨胀您自己的列表视图。

@Override
protected View onCreateDialogView() {

    LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    ListView lv = new ListView(getContext());

    // Inflate the view into the header only if a message was set
    if (getDialogMessage() != null && ! getDialogMessage().equals("") ) {

        TextView header = (TextView) inflater.inflate(R.layout.dialog_preference_list_header, null);
        header.setText(getDialogMessage());
        lv.addHeaderView(header, null, false);

    }

    // Create a new adapter and a list view and feed it with the ListPreference entries
    ArrayAdapter<CharSequence> adapter = new ArrayAdapter<CharSequence>(getContext(),
            R.layout.custom_dialog_single_choice_list_adapter, getEntries());

    lv.setAdapter(adapter);
    lv.setClickable(true);
    lv.setEnabled(true);
    lv.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
    lv.setItemChecked(getValueIndex() + 1, true);
    lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {

        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
            setValueIndex(position - 1);
            getDialog().dismiss();
        }
    });
    return lv;
}

另一个重要的事情是调用 onPrepareDialogBu​​ilder 而不是在其中调用 super。这将避免列表视图出现两次。

@Override
protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
    // Not calling super, to avoid having 2 listviews

    // Set the positive button as null
    builder.setPositiveButton(null, null);
}

private int getValueIndex() {
    return findIndexOfValue(getValue());
}

在我的例子中,dialog_preference_list_header 只是一个 TestView,但它可能是一个更复杂的视图,而 custom_dialog_single_choice_list_adapter 可能是这样的:

<?xml version="1.0" encoding="utf-8"?>
<CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@android:id/text1"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:checkMark="?android:attr/listChoiceIndicatorSingle"
    android:ellipsize="marquee"
    android:gravity="center_vertical"
    android:minHeight="?android:attr/listPreferredItemHeight"
    android:paddingBottom="2dip"
    android:paddingLeft="10dip"
    android:paddingRight="10dip"
    android:paddingTop="2dip"
    android:textAppearance="?android:attr/textAppearanceMedium" />
于 2015-12-10T09:48:16.053 回答