RecyclerView中的Android Spinners问题

时间:2017-07-11 13:29:13

标签: android android-recyclerview android-spinner

我有RecyclerView,其中包含复杂项目,由包含TextView窗口小部件,Spinner窗口小部件&一个EditText小部件。 (想想一个任务列表,包括任务名称,一个微调器,用于选择处理任务的哪个阶段,以及该处理阶段的完成日期。)

此列表的数据来自文件,对列表中项目的更改将写回文件。当用户稍后返回应用程序时,列表应该反映之前看到的数据。

  1. 目前,我的应用程序显示项目列表,但我不知道如何将Spinner的初始值设置为从文件读取的值。 如何为列表中的每个微调器设置不同的初始选择?

  2. 目前,当用户从Spinner进行选择时,列表会消失,并且选择不会显示为用户的选择。我在另一篇文章中看到了Android: Spinner not showing the selected value - 我应该调用setSelection()(由AbsSpinner的Spinner类继承)。但那没有做任何事。在XML布局中,我的Spinner有:android:textColor="@color/colorPrimaryDark"所以我不认为文本存在的问题是与背景相匹配的颜色。见List of Tasks如何保留用户的选择?

  3. OnItemSelected()如何告诉活动记下所选值,以便将其保存回文件?我在网上找到的示例代码通常只使用Toast来显示选择已注册。我想我需要知道RecyclerView中哪个项目包含此Spinner ...

  4. 以下是详细信息......

    activity_task.xml:

    <?xml version="1.0" encoding="utf-8"?>
    <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/colorBG"
        tools:context="org.myorg.myapp.DetailActivity">
    
        ...
    
        <android.support.v7.widget.RecyclerView
            android:id="@+id/rvChapList"
            android:layout_width="wrap_content"
            android:layout_height="0dp"
            app:layout_constraintTop_toBottomOf="@+id/txtTaskLabel"
            android:layout_marginTop="8dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toRightOf="@+id/imgBorder"
            app:layout_constraintRight_toRightOf="parent"
            />
    
    </android.support.constraint.ConstraintLayout>
    

    subtask_detail.xml:

    <?xml version="1.0" encoding="utf-8"?>
    <android.support.constraint.ConstraintLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="24dp"
        android:background="@color/colorBG"
        android:id="@+id/subtask_detail"
        >
    
        <android.support.constraint.Guideline
            android:id="@+id/LGuideLine2"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:orientation="vertical"
            app:layout_constraintGuide_begin="40dp"
            tools:layout_editor_absoluteY="0dp"
            tools:layout_editor_absoluteX="40dp" />
    
        <android.support.constraint.Guideline
            android:id="@+id/RGuideLine2"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:orientation="vertical"
            app:layout_constraintGuide_begin="280dp"
            tools:layout_editor_absoluteY="0dp"
            tools:layout_editor_absoluteX="280dp" />
    
        <TextView
            android:id="@+id/txtChapNum"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:paddingTop="6dp"
            tools:text="150"
            android:textAppearance="@style/TextAppearance.AppCompat"
            android:textColor="@color/colorPrimaryDark"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toLeftOf="@+id/LGuideLine2"
            />
    
        <Spinner
            android:id="@+id/spnSteps"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:spinnerMode="dialog"
            tools:text="Quality Check"
            android:prompt="@string/step_prompt"
            style="@android:style/Widget.Holo.Light.Spinner"
            android:entries="@array/step_array"
            android:textColor="@color/colorPrimaryDark"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintLeft_toRightOf="@+id/LGuideLine2"
            app:layout_constraintRight_toLeftOf="@+id/RGuideLine2"
            />
    
        <EditText
            android:id="@+id/edtDate"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:inputType="date"
            android:paddingTop="0dp"
            android:paddingBottom="0dp"
            android:paddingStart="0dp"
            android:paddingEnd="0dp"
            tools:text="12-30-2020"
            android:textSize="14sp"
            android:textColor="@color/colorPrimaryDark"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintLeft_toRightOf="@+id/RGuideLine2"
            app:layout_constraintRight_toRightOf="parent"
            />
    
    </android.support.constraint.ConstraintLayout>
    

    DetailActivity.java:

    package org.myorg.myapp;
    
    import android.content.Intent;
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.support.v7.widget.LinearLayoutManager;
    import android.support.v7.widget.RecyclerView;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.AdapterView;
    import android.widget.EditText;
    import android.widget.Spinner;
    import android.widget.TextView;
    
    import java.util.ArrayList;
    
    public class DetailActivity extends AppCompatActivity {
        private RecyclerView _rv;
        private LayoutInflater _li;
        private SubtaskDetailAdapter _adapter;
        private ArrayList _alEntries;
        private ArrayList _alTaskEntries;
        private String _sTaskName = null;
        private static final String EXTRA_TASK = "EXTRA_TASK";
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_task);
    
            Intent i = getIntent();
            _sTaskName = i.getStringExtra(EXTRA_TASK);
            TextView tvTaskName = (TextView) findViewById(R.id.txtTaskName2);
            tvTaskName.setText(_sTaskName);
    
            // create item detail array & populate it based on task item array
            _alEntries = Globals.getArrayList();
            _alTaskEntries = new ArrayList();
            PopulateTaskDetailList();
    
            _rv = (RecyclerView) findViewById(R.id.rvChapList);
            _li = getLayoutInflater();
            _rv.setLayoutManager(new LinearLayoutManager(this));
    
            _adapter = new SubtaskDetailAdapter();
            _rv.setAdapter(_adapter);
        }
    
        public void PopulateTaskDetailList() {
            int iNumEntries = _alEntries.size();
            String sSubtask = "";
            TaskItem tiEntry = null;
            DetailItem diEntry = null;
    
            // extract subtasks for indicated task
            for (int i = 0; i < iNumEntries; i++) {
                tiEntry = (TaskItem) _alEntries.get(i);
    
                // if this task entry has the indicated Task name, save it's data
                if (tiEntry.get_TaskName().equals(_sTaskName)) {
                    diEntry = new DetailItem(tiEntry.get_Subtask(),
                                             tiEntry.get_StepCompleted(),
                                             tiEntry.get_DateCompleted());
    
                    _alTaskEntries.add(diEntry);
                }
            }
        }
    
        private class DetailItem {
            private String _sSubTaskName = "";
            private String _sStep = "";
            private String _sDate = "";
    
            private DetailItem(String sSubTaskName, String sStep, String sDate) {
                _sSubTaskName = sSubTaskName;
                _sStep = sStep;
                _sDate = sDate;
            }
    
            private String get_Subtask() { return _sSubTaskName; }
    
            public void set_Subtask(String sTaskName) { _sSubTaskName = sTaskName; }
    
            private String get_Step() { return _sStep; }
    
            public void set_Step(String sStep) { _sStep = sStep; }
    
            private String get_Date() { return _sDate; }
    
            public void set_Date(String sDate) { _sDate = sDate; }
        }
    
        private class SubtaskDetailAdapter extends RecyclerView.Adapter<SubtaskDetailAdapter.DetailViewHolder> {
    
            /**
             * Inflates (creates & fills) a new subtask_detail View, and then creates/returns a new
             * DetailViewHolder object for that view.
             * @param parent Unfortunately the docs currently don't explain this at all :(
             * @param viewType Unfortunately the docs currently don't explain this at all :(
             * @return
             */
            @Override
            public SubtaskDetailAdapter.DetailViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
                // "inflate" (create & fill) a new holder / container view based on the task_item
                // layout, without "attaching" it to the parent view
                View v = _li.inflate(R.layout.subtask_detail, parent, false);
    
                // create an instance of DetailViewHolder based on this "inflated" "holder" view
                return new SubtaskDetailAdapter.DetailViewHolder(v);
            }
    
            /**
             * This method "Binds" or assigns Data (from _alTaskEntries) to each SubtaskDetail (ViewHolder).
             * @param holder The SubtaskDetail instance at a given position in the list
             * @param position The current position of the SubtaskDetail we are Binding to, based upon
             *                 our (listOfData). So for the second ViewHolder we create, we'll bind data
             *                 from the second Item in listOfData.
             */
            @Override
            public void onBindViewHolder(SubtaskDetailAdapter.DetailViewHolder holder, int position) {
                // the ViewHolder data
                DetailItem currentItem = (DetailItem) _alTaskEntries.get(position);
    
                holder._tvSubtask.setText(currentItem.get_Subtask());
                holder._etDate.setText(currentItem.get_Date());
                holder._spSteps.setSelection(position, true);
            }
    
            /**
             * This method helps our Adapter determine how many ViewHolders it needs to create,
             * based on the size of the Dataset (List) it is working with.
             * Returning 0 here would tell our Adapter not to make any Items.
             *
             * @return the size of the dataset to be represented in the RecyclerView
             **/
            @Override
            public int getItemCount() { return _alTaskEntries.size(); }
    
            /**
             * A ViewHolder is a container for a set of Views we want to populate with Data
             **/
            class DetailViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
    
                // view holders for views to bind in the layout
                private TextView  _tvSubtask;
                private EditText  _etDate;
                private Spinner   _spSteps;
                private ViewGroup _vgContainer;
    
                private DetailViewHolder(View itemView) {
                    super(itemView);
    
                    // use itemView with findViewByID, because we are looking for an ID in
                    // the SubtaskDetail view container we created / inflated above
                    _tvSubtask = (TextView) itemView.findViewById(R.id.txtChapNum);
                    _spSteps = (Spinner) itemView.findViewById(R.id.spnSteps);
                    _etDate = (EditText) itemView.findViewById(R.id.edtDate);
    
                    _vgContainer = (ViewGroup) itemView.findViewById(R.id.subtask_detail);
    
                    _spSteps.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
                        @Override
                        public void onItemSelected(AdapterView<?> adapter, View v,
                                                   int position, long id) {
                            // On selecting a spinner item
                            String sStep = adapter.getItemAtPosition(position).toString();
    
                            _spSteps.setSelection(position);
                        }
    
                        @Override
                        public void onNothingSelected(AdapterView<?> arg0) {
                            // TODO Auto‐generated method stub
                        }
                    });
    
                    /* We can pass "this" as an Argument, because DetailViewHolder implements the
                       View.OnClickListener interface. */
                    _vgContainer.setOnClickListener(this);
                }
    
                @Override
                public void onClick(View v) {
                    // currently unused
                }
            }
        }
    }
    

    感谢您在我尝试学习这些内容时提供的任何帮助!

    新信息

    我已经为我的问题#3找到了一个解决方案,尽管它感觉像是一个混乱。可能有一种更好的方式,但由于没有人对我有所启发,我就离开了。

    我在RecyclerView中显示的数据数组是一个DetailItem对象数组。我已经在DetailItem类中添加了另一个实例变量来保存Spinner视图,该视图将与该子任务步骤相关联。

    这里是更新的DetailItem类定义:

    private class DetailItem {
        private String   _sSubTaskName = "";
        private String   _sStep = "";
        private Spinner  _spSteps;
        private String   _sDate = "";
    
        private DetailItem(String sSubTaskName, String sStep, String sDate) {
        _sSubTaskName = sSubTaskName;
        _sStep = sStep;
        _sDate = sDate;
        }
    
        private String get_Subtask() { return _sSubTaskName; }
        public void set_Subtask(String sTaskName) { _sSubTaskName = sTaskName; }
        private String get_Step() { return _sStep; }
        public void set_Step(String sStep) { _sStep = sStep; }
        private Spinner get_Spin() { return _spSteps; }
        public void set_Spin(Spinner spSteps) { _spSteps = spSteps; }
        private String get_Date() { return _sDate; }
        public void set_Date(String sDate) { _sDate = sDate; }
    }
    

    我修改了适配器,以便在任务步骤中存储显示/选择它的Spinner。我还移动了代码,将Spinner的Listener从ViewHolder设置为Adapter。

    这里是更新的SubtaskDetailAdapter类定义及其扩展的onBindViewHolder方法:

    private class SubtaskDetailAdapter extends RecyclerView.Adapter<SubtaskDetailAdapter.DetailViewHolder> {
        /** no changes */
        @Override
        public SubtaskDetailAdapter.DetailViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            // ...
        }
    
        @Override
        public void onBindViewHolder(SubtaskDetailAdapter.DetailViewHolder holder, int position) {
            // the ViewHolder data
            DetailItem currentItem = (DetailItem) _alTaskEntries.get(position);
    
            holder._tvSubtask.setText(currentItem.get_Subtask());
            holder._etDate.setText(currentItem.get_Date());
    
            // store the spinner in the DetailItem object
            currentItem.set_Spin(holder._spSteps);
    
            // store DetailItem object in the array
            _alTaskEntries.set(position, currentItem);
    
            // look for Spinner step matching this entry's step
            String sStep = currentItem.get_Step();
            int iSel = 0;
            while (iSel < sSteps.length && !sSteps[iSel].equals(sStep))
                iSel++;
    
            // if matching step is found, set Spinner to show it
            if (iSel < sSteps.length) holder._spSteps.setSelection(iSel, true);
    
            // if matching step isn't found, show error message
            else Toast.makeText(getApplicationContext(),
                                "Unrecognized Step: " + sStep,
                                Toast.LENGTH_SHORT).show();
    
            // set listener for spinner selections
            holder._spSteps.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
                @Override
                public void onItemSelected(AdapterView<?> adapter, View v,
                                           int position,           long id) {
                    // get selected step
                    String sStep = adapter.getItemAtPosition(position).toString();
    
                    DetailItem currentItem = null;
                    int iNumDetails = _alTaskEntries.size();
                    int iDetail = 0;
                    // fast-forward to array entry for this adapter (spinner)
                    while (iDetail < iNumDetails) {
                        currentItem = (DetailItem) _alTaskEntries.get(iDetail);
                        if (currentItem.get_Spin().equals(adapter))
                            break;
                        else
                            iDetail++;
                    }
    
                    // if found, save it in the array of detail items
                    if ((iDetail < iNumDetails) && (currentItem != null)) {
                        currentItem.set_Step(sStep);
                        _alTaskEntries.set(iDetail, currentItem);
                    }
    
                    adapter.setSelection(position);
                }
    
                @Override
                public void onNothingSelected(AdapterView<?> arg0) {
                    // TODO Auto-generated method stub
                }
            });
        }
    

    我删除了ViewHolder类中的代码,以便在容器视图上设置OnClickListener,因为我不想响应RecyclerView项目上的点击,而只是响应其中的各个视图。需要一个空的OnClick,因为声明持有者实现了OnClickListener接口(我不知道是否需要这样做)。

    这是更新的(更简单的)ViewHolder类:

    class DetailViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
        // view holders for views to bind in the layout
        private TextView  _tvSubtask;
        private EditText  _etDate;
        private Spinner   _spSteps;
    
        private DetailViewHolder(View itemView) {
            super(itemView);
    
            // use itemView with findViewByID, because we are looking for an ID in
            // the SubtaskDetail view container we created / inflated above
            _tvSubtask = (TextView) itemView.findViewById(R.id.txtChapNum);
            _spSteps = (Spinner) itemView.findViewById(R.id.spnSteps);
            _etDate = (EditText) itemView.findViewById(R.id.edtDate);
        }
    
        @Override
        public void onClick(View v) {
            // currently unused
        }
    }
    

    我的问题#1&amp; #2仍未解决,这使我的应用程序目前无法使用...任何人都想要解决这个问题?

2 个答案:

答案 0 :(得分:0)

我不知道您的代码将如何实施,但我会尝试通过示例回答您的问题来帮助您。

1)如何为列表中的每个微调器设置不同的初始选择?

如果要在Spinner中存储对象列表。

public class SpinnerItem {

    private String name;
    private int position;

    public SpinnerItem(String name, int position) {
        this.name = name;
        this.position = position;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getPosition() {
        return position;
    }

    public void setPosition(int position) {
        this.position = position;
    }

    @Override
    public String toString() {
        return this.name;
    }
}

微调

ArrayList<SpinnerItem> spinnerItems = new ArrayList<>();
Spinner spinner = (Spinner) findViewById(R.id.spinner);

// Creating the items
for(int i=0; i<20; i++){
    spinnerItems.add(new SpinnerItem("A"+(i+1), i));
}

ArrayAdapter adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, spinnerItems);

spinner.setAdapter(adapter);
spinner.setSelection(10); // To select A11

2)如何保留用户的选择?

要获取所选项目,您必须使用setOnItemSelectedListener

spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView<?> adapterView, View view, int position, long id) {
                SpinnerItem itemSelected = (SpinnerItem) adapterView.getItemAtPosition(position);
                Toast.makeText(view.getContext(), itemSelected.toString(), Toast.LENGTH_SHORT).show();
            }

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

            }
        });

我通常将Spinner变量作为Activity类中的属性。您可以创建一个变量作为属性,并调用spItemSelected来存储所选元素(在我的代码中替换SpinnerItem itemSelected的{​​{1}}。)

3)如何告诉活动记下所选值,以便将其保存回文件?

如果在类Activity中选择了元素作为属性,则可以更容易地从任何函数调用它来将其存储在文件中。我不确定你什么时候需要保存文件。

答案 1 :(得分:0)

非常感谢我的朋友Sheldon解决这个问题!

问题#1&amp; #2现在都已解决。问题只是文字颜色问题! Spinner中的所选项目在下拉列表中显示为白色,这很好,因为下拉列表的背景为黑色。但删除下拉列表后,文本颜色仍为白色,我的活动背景为灰白色背景。

以下是包含相关更改的更新代码:

@Override
public void onBindViewHolder(SubtaskDetailAdapter.DetailViewHolder holder, int position) {
    // the ViewHolder data
    DetailItem currentItem = (DetailItem) _alTaskEntries.get(position);

    holder._tvSubtask.setText(currentItem.get_Subtask());
    holder._etDate.setText(currentItem.get_Date());

    // store the spinner in the DetailItem object
    currentItem.set_Spin(holder._spSteps);

    // store DetailItem object in the array
    _alTaskEntries.set(position, currentItem);

    // initialize spinner - 1st, set correct text color
    holder._spSteps.setSelection(0, true);
    View v = holder._spSteps.getSelectedView();
    ((TextView)v).setTextColor(ContextCompat.getColor(DetailActivity.this,
                                                 R.color.colorPrimaryDark));

    // look for Spinner step matching this entry's step
    String sStep = currentItem.get_Step();
    int iSel = 0;
    while (iSel < sSteps.length && !sSteps[iSel].equals(sStep))
        iSel++;

    // if matching step is found, set Spinner to show it
    if (iSel < sSteps.length) holder._spSteps.setSelection(iSel, true);

    // if matching step isn't found, show error message
    else Toast.makeText(getApplicationContext(),
                        "Unrecognized Step: " + sStep,
                        Toast.LENGTH_SHORT).show();

    // set listener for spinner selections
    holder._spSteps.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
        @Override
        public void onItemSelected(AdapterView<?> adapter, View v,
                                   int position,           long id) {
            // ensure selected Spinner text has correct color
            int tc = ContextCompat.getColor(BookActivity.this,
                                            R.color.colorPrimaryDark);
            ((TextView) v).setTextColor(tc);

            // get selected step
            String sStep = adapter.getItemAtPosition(position).toString();

            DetailItem currentItem = null;
            int iNumDetails = _alTaskEntries.size();
            int iDetail = 0;
            // fast-forward to array entry for this adapter (spinner)
            while (iDetail < iNumDetails) {
                currentItem = (DetailItem) _alTaskEntries.get(iDetail);
                if (currentItem.get_Spin().equals(adapter))
                    break;
                else
                    iDetail++;
            }

            // if found, save it in the array of detail items
            if ((iDetail < iNumDetails) && (currentItem != null)) {
                currentItem.set_Step(sStep);
                _alTaskEntries.set(iDetail, currentItem);
            }

            adapter.setSelection(position);
        }

        @Override
        public void onNothingSelected(AdapterView<?> arg0) {
            // TODO Auto-generated method stub
        }
    });
}