【问题标题】:Android Spinner data view not updating on item re-selectedAndroid Spinner 数据视图未在重新选择的项目上更新
【发布时间】:2013-06-21 20:04:07
【问题描述】:

我正在尝试将 Spinner 自定义为多选。我已经成功地将它设置为多选,但是当重新选择一个项目时,微调器内部的视图没有得到更新。

问题

当在Spinner 中重新选择一个项目时,我的SpinnerAdaptergetView() 被调用,产生我想要的View,但是View 不知何故不会显示。我已经逐步完成了调试器中的所有内容,但我找不到选择不同项目或重新选择相同项目之间的任何差异。我已经浏览了 Spinner AbsSpinnerAdapterView 中的所有代码,但我找不到可能导致这种情况的原因。

举例

微调器包含 6 个项目:选项 1-6。微调器起源于所有未选择的项目。 选择了选项 1、2 和 4。微调器内的视图正确显示“选择 1、选择 2、选择 4”。

选项 4 未选中(就 Spinner 而言,重新选择)。选项 4 已从列表中正确取消选中,但微调器内的视图不会更新。 它仍然显示“选择 1,选择 2,选择 4”。通过调试器,我的 SpinnerAdapter 上的 getView() 被调用,一切都被调用。由于某种原因,视图实际上并没有显示出来。

选项 2 未选中(这不是 Spinner 的“重新选择”)。在这里,一切都按预期运行。 Spinner 内的列表项和视图都会更新。

一些代码

多选微调器

public class MultiSelectSpinner extends Spinner {

    private OnItemSelectedListener listener;

    public MultiSelectSpinner(Context context) {

        super( context );
    }

    public MultiSelectSpinner(Context context, AttributeSet attrs) {

        super( context, attrs );
    }

    public MultiSelectSpinner(Context context, AttributeSet attrs, int defStyle) {

        super( context, attrs, defStyle );
    }

    @Override
    public void setSelection( int position ) {

        super.setSelection( position );
        if ( listener != null ) {

            listener.onItemSelected( this, getSelectedView(), position, getAdapter().getItemId( position ) );
        }
    }

    public void setOnItemSelectedEvenIfUnchangedListener( OnItemSelectedListener listener ) {

        this.listener = listener;
    }
}

微调器适配器

这里只发布重要的部分。省略 getter/setter 等。

public class FormChoiceSpinnerAdapter implements SpinnerAdapter, OnItemSelectedListener {

    private Choice[] choices;
    private String title;
    private final DataSetObservable dataSetObservable = new DataSetObservable();

    public FormChoiceSpinnerAdapter(String[] choices, String title) {

        setChoices( new Choice[choices.length] );
        for (int i = 0; i < choices.length; i++) {

            getChoices()[i] = new Choice( choices[i] );
        }
    }

    @Override
    public View getView( int position, View convertView, ViewGroup parent ) {

        Context context = parent.getContext();
        if ( convertView == null ) {

            convertView = LayoutInflater.from( context ).inflate( android.R.layout.simple_spinner_item, parent, false );
        }

        String displayString = "";

        for (int i = 0; i < getChoices().length; i++) {

            Choice choice = getChoices()[i];

            if ( choice.isSelected() ) {

                displayString += choice.getLabel() + ", ";
            }
        }

        if ( displayString.length() > 0 ) {

            displayString = displayString.trim().substring( 0, displayString.length() - 2 );
        }
        else {

            displayString = getTitle() + "...";
        }

        ( (TextView) convertView ).setText( displayString );

        return convertView;
    }

    @Override
    public View getDropDownView( int position, View convertView, ViewGroup parent ) {

        Context context = parent.getContext();
        if ( convertView == null ) {

            convertView = LayoutInflater.from( context ).inflate( R.layout.simple_dropdown_item, parent, false );
        }

        Choice choice = getChoices()[position];
        TextView text = (TextView) convertView.findViewById( android.R.id.text1 );
        text.setText( choice.getLabel() );

        ImageView selectedImage = (ImageView) convertView.findViewById( R.id.image_selected );
        int visibility = choice.isSelected() ? View.VISIBLE : View.GONE;
        selectedImage.setVisibility( visibility );

        return convertView;
    }

    @Override
    public void onItemSelected( AdapterView<?> parent, View view, int position, long id ) {

        Choice choice = getChoices()[position];

        choice.setSelected( !choice.isSelected() );

        ImageView imageView = (ImageView) view.findViewById( R.id.image_selected );

        if ( imageView != null ) {
            imageView.setVisibility( choice.isSelected() ? View.VISIBLE : View.GONE );
        }    
    }

    @Override
    public void onNothingSelected( AdapterView<?> parent ) {

        //No op
    }

    public static class Choice {

        private boolean selected;
        private String label;

        public Choice(String label) {

            this.label = label;
            selected = false;
        }
    }
}

【问题讨论】:

  • 你好,伙计!我试图通过使用Spinner 来达到几乎相同的结果,但目前没有运气。您能否发布此元素的完整代码并分享知识:)?
  • 不幸的是,我不再访问此代码。我从来没有找到解决方案,所以值得您花时间复制/修改Spinner 来做您想做的事。

标签: android android-spinner


【解决方案1】:

我知道这已经很晚了,但我找到了解决我遇到的类似问题的方法。尝试将 adapter.notifyDataSetChanged() 添加到您的 onItemSelected(AdapterView&lt;?&gt; parent, View view, int pos, long id) 覆盖方法中。

我的微调器应该显示默认消息,直到用户实际从中选择一个值。除非用户选择下拉列表中的第一项,否则它工作正常,然后微调器不会自行更新。添加adapter.notifyDataSetChanged() 有效。

我还查看了一些源代码,但我无法准确找到发生了什么,但我有一个想法。由于Spinner 在选择已选择的值时不会调用OnItemSelectedListener(除非您扩展Spinner,这是我在this SO answer 的帮助下完成的),我的猜测是,如果您尝试取消选择您最近选择的值,Spinner 不会将其识别为取消选择,因此不会重绘视图。对您而言,按顺序选择 1、2、4,然后尝试取消选择 4 不会触发重绘响应,但随后取消选择 2 会导致重绘。

我可能是错的,但我知道我们的问题是相似的,这对我有用。

【讨论】:

    【解决方案2】:

    您需要从 UI 线程更新视图。从另一个与您的问题类似的问题中查看此答案,该问题详细说明了使用 AsyncTask 更新 UI 线程:

    https://stackoverflow.com/a/4370785/793150

    我遇到了同样的问题,视图不更新片段,我相信这个解决方案也适用于这个问题。

    希望这能解决您的问题!

    【讨论】:

    • 我不相信它是。就像我说的,我在更新片段时遇到了同样的问题。如果您有更新视图的方法,则需要从 AsyncTask 调用 UI 线程。这将更新与主 ui 线程分开并独立更新。这样,您无需等待 android 回收并重建此视图,而是自己主动更新它。话虽如此,我并非万无一失。有可能我错了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-26
    相关资源
    最近更新 更多