简体   繁体   English

在 Android MultiSelectListPreference 中至少选择一项

[英]Have at least one item selected in Android MultiSelectListPreference

I have search now for hours through the internet and have found nothing substantial so far.我现在已经通过互联网搜索了几个小时,但到目前为止没有发现任何实质性的东西。 The thing that I want to do is a multi choice preference view, that disables the last item and reenables it, if it is not alone anymore.我想要做的是一个多选首选项视图,禁用最后一个项目并重新启用它,如果它不再是单独的。

I through so far about taking the super class force read the private variables in there to write my own onPrepareDialogBuilder(AlertDialog.Builder builder) .到目前为止,我通过使用超类强制读取那里的私有变量来编写我自己的onPrepareDialogBuilder(AlertDialog.Builder builder) Which is configuring its own OnMultiChoiceClickListener that jumps in, in the moment where has only one item left.它正在配置自己的OnMultiChoiceClickListener ,在只剩下一个项目的那一刻跳入。 The problem here is, that I use a bad practice force read of a private variable and that I have so far no idea how to get the checkbox item and how to disable it.这里的问题是,我使用了一种不好的做法来强制读取私有变量,到目前为止我还不知道如何获取复选框项以及如何禁用它。 But I think looking even deeper into the Android SDK will solve this problem.但我认为深入研究 Android SDK 会解决这个问题。

At the end, if nothing works, solving the problem with doing an overwrite the OnPreferenceChangeListener to display a toast if the user has less than one item selected.最后,如果没有任何效果,则通过覆盖OnPreferenceChangeListener以在用户选择的项目少于一项时显示吐司来解决问题。 But user friendliness is a high value, that needs to be earned and that often isn't easy.但是用户友好性是一个很高的价值,需要获得,而且通常并不容易。

Thx.谢谢。

import android.content.Context;
import android.preference.MultiSelectListPreference;
import android.util.AttributeSet;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import georg.com.flironetest_01.Variables.Units;

/**
 * Created by Georg on 16/03/16.
 */
public class UnitMultipleSelectorPreference extends MultiSelectListPreference {

    public UnitMultipleSelectorPreference(Context context, AttributeSet attrs) {
        super(context, attrs);

        List<CharSequence> humanU = new ArrayList<>();
        List<CharSequence> machineU = new ArrayList<>();

        Units[] all = Units.values(); // Units is a enum with a rewriten to string statement.
        for (Units elem : all) {
            humanU.add(elem.toString());
            machineU.add(elem.name());
        }

        setEntries(humanU.toArray(new CharSequence[humanU.size()]));
        setEntryValues(machineU.toArray(new CharSequence[machineU.size()]));

        Set<String> mU = new HashSet<>();
        mU.add(Units.C.name());
        mU.add(Units.K.name());

        setDefaultValue(mU);
    }
}

Okay.好的。 To answer my own question here after the motto "self is the man": I ended up with programming my own preference panel.在座右铭“自我就是男人”之后在这里回答我自己的问题:我最终编写了自己的偏好面板。 Below is the code.下面是代码。 If somebody likes to look over it and give some times how to make it even more stable: feel free.如果有人喜欢查看它并给出一些时间如何使其更加稳定:请随意。

But to sum up what I did: I created my own ArrayAdapter.但总结一下我所做的:我创建了自己的 ArrayAdapter。 But DialogPreference didn't allowed me to create my own multi selector.但是DialogPreference不允许我创建自己的多重选择器。 You need to change the final dialog fragment to create a working multi selector list (see here: https://stackoverflow.com/a/17907379/5759814 ).您需要更改最终对话框片段以创建一个有效的多重选择器列表(请参阅此处: https : //stackoverflow.com/a/17907379/5759814 )。 That is not an easy task if you work with the DialogPreferences.如果您使用 DialogPreferences,这不是一件容易的事。 The reason is these few amounts of code:原因是这些少量的代码:

/**
 * Shows the dialog associated with this Preference. This is normally initiated
 * automatically on clicking on the preference. Call this method if you need to
 * show the dialog on some other event.
 * 
 * @param state Optional instance state to restore on the dialog
 */
protected void showDialog(Bundle state) {
    Context context = getContext();

    mWhichButtonClicked = DialogInterface.BUTTON_NEGATIVE;

    mBuilder = new AlertDialog.Builder(context)
        .setTitle(mDialogTitle)
        .setIcon(mDialogIcon)
        .setPositiveButton(mPositiveButtonText, this)
        .setNegativeButton(mNegativeButtonText, this);

    View contentView = onCreateDialogView();
    if (contentView != null) {
        onBindDialogView(contentView);
        mBuilder.setView(contentView);
    } else {
        mBuilder.setMessage(mDialogMessage);
    }

    onPrepareDialogBuilder(mBuilder);

    getPreferenceManager().registerOnActivityDestroyListener(this);

    // Create the dialog
    final Dialog dialog = mDialog = mBuilder.create();
    if (state != null) {
        dialog.onRestoreInstanceState(state);
    }
    if (needInputMethod()) {
        requestInputMethod(dialog);
    }
    dialog.setOnDismissListener(this);
    dialog.show();
}

As you can see here is a method triggered to change my dialog builder with onPrepareDialogBuilder , but it doesn't seem like that there is any other function triggered afterwards, that would allow me to change the dialog directly after its creation.正如您所看到的,这是一个触发方法以使用onPrepareDialogBuilder更改我的对话框构建器,但之后似乎没有触发任何其他函数,这将允许我在创建对话框后直接更改对话框。 And the second idea of changing the onPrepareDialogBuilder so that I can init everything there, doesn't really help, because I end up with displayed dialog windows.更改onPrepareDialogBuilder以便我可以在那里初始化所有内容的第二个想法并没有真正帮助,因为我最终显示了对话框窗口。 That lead me to my decision of creating my completely own Preference class.这导致我决定创建完全属于自己的 Preference 类。 With that decision I loose all those nice prepared functions like onRestoreInstanceState and Co, but I now have an application with a much more persistent flow, that doesn't do any stupid things when I select zero units for my thermal view.有了这个决定,我onRestoreInstanceState所有那些准备好的函数,比如onRestoreInstanceState和 Co,但我现在有一个具有更持久流的应用程序,当我为我的热视图选择零单位时,它不会做任何愚蠢的事情。

Below the non commented code.在未注释的代码下方。 I'm sorry, but I think its simple enough for everybody who landing here.我很抱歉,但我认为这对每个登陆这里的人来说都很简单。

import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.preference.Preference;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import georg.com.flironetest_01.Variables.Units;

/**
 * Created by Georg on 16/03/16.
 */
public class UnitMultipleSelectorPreference extends Preference implements DialogInterface.OnClickListener, Preference.OnPreferenceClickListener {

    String[] human_entries = null;
    String[] machine_entries = null;



    public SharedPreferences prev;

    public UnitMultipleSelectorPreference(Context context, AttributeSet attrs) {
        super(context, attrs);


        prev = getSharedPreferences();

        List<String> humanU = new ArrayList<>();
        List<String> machineU = new ArrayList<>();

        Units[] all = Units.values();
        for (Units elem : all) {
            humanU.add(elem.toString());
            machineU.add(elem.name());
        }

        human_entries = humanU.toArray(new String[humanU.size()]);
        machine_entries = machineU.toArray(new String[machineU.size()]);

        Set<String> mU = new HashSet<>();
        mU.add(Units.C.name());
        mU.add(Units.K.name());

        setDefaultValue(mU);

        setOnPreferenceClickListener(this);
    }

    boolean[] selected = new boolean[0];

    protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
        if (prev == null)
            return;

        if (human_entries == null || machine_entries == null || human_entries.length != machine_entries.length ) {
            throw new IllegalStateException(
                    "ListPreference requires an entries array and an entryValues array which are both the same length");
        }

        selected = new boolean[human_entries.length];
        for (int i = 0; i < human_entries.length; i++)
            selected[i] = prefSet.contains(machine_entries[i]);


        String[] stringObj = new String[human_entries.length];
        int i = 0;
        for(CharSequence ch : human_entries)
            stringObj[i++] = ch.toString();


        builder.setAdapter(new MyAdapter(getContext(), android.R.layout.simple_list_item_multiple_choice, stringObj), new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
            }
        });

        AlertDialog mDialog = builder.create();
        mDialog.getListView().setChoiceMode(AbsListView.CHOICE_MODE_MULTIPLE);
        mDialog.getListView().setItemsCanFocus(false);
        mDialog.getListView().setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view,
                                    int position, long id) {
                // Manage selected items here

                ListView mParent = (ListView)parent;


                if (mParent.getCheckedItemCount() >= 1)
                    selected[position] = mParent.isItemChecked(position);
                if (mParent.getCheckedItemCount() == 0)
                    mParent.setItemChecked(position, true);
            }
        });


        mDialog.show();



        i = 0;
        for (boolean select : selected)
            mDialog.getListView().setItemChecked(i++, select);
    }

    @Override
    public boolean onPreferenceClick(Preference preference) {
        AlertDialog.Builder mBuilder = new AlertDialog.Builder(getContext());
        mBuilder.setTitle(getTitle())
                .setPositiveButton(android.R.string.ok, this)
                .setNegativeButton(android.R.string.cancel, this);
        onPrepareDialogBuilder(mBuilder);

        return true;
    }



    @Override
    public void onClick(DialogInterface dialog, int which) {
        Toast.makeText(getContext(), "W:"+which + " | " + Arrays.toString(selected), Toast.LENGTH_SHORT).show();

        switch (which) {
            case -1:
                if (isPersistent()) {
                    prefSet = new HashSet<>();

                    for (int i = 0; i < selected.length; i++) {
                        if (selected[i])
                            prefSet.add(machine_entries[i]);
                    }
                    getEditor().putStringSet(getKey(), prefSet).apply();

                    Toast.makeText(getContext(), "W:"+which + " | " + getSharedPreferences().getStringSet(getKey(),null).toString(), Toast.LENGTH_SHORT).show();

                }
                return;
        }
    }



    public class MyAdapter extends ArrayAdapter<String> {

        public MyAdapter(Context context, int textViewResourceId, String[] objects) {
            super(context, textViewResourceId, objects);
        }

        @Override
        public boolean isEnabled(int n) {
            return true;
        }
    }

    Set<String> prefSet;

    @Override
    protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue) {
        super.onSetInitialValue(restorePersistedValue, defaultValue);
        prev = getSharedPreferences();

        if(restorePersistedValue) {
            prefSet = prev.getStringSet(getKey(), new HashSet<String>());
        } else {
            try {
                prefSet = (Set<String>)defaultValue;
                if(isPersistent())
                    getEditor().putStringSet(getKey(), prefSet);
            } catch (ClassCastException e) {
                Log.e("ERROR_CAST", "Error casting the default value to Set<String>.");
            }
        }
    }
}

A really simple solution is to set a setOnPreferenceChangeListener and just return false if the new value would be empty.一个非常简单的解决方案是设置一个 setOnPreferenceChangeListener 并在新值为空时返回 false 。
All of the code is put into onCreatePreferences.所有代码都放在 onCreatePreferences 中。

MultiSelectListPreference infoPreference = findPreference("information");

infoPreference.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
            @Override
            public boolean onPreferenceChange(Preference preference, Object newValue) {
                if (size(newValue) == 0){
                    return false;
                }
                return true;
            }
        });

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM