RecyclerView 中 Android Spinners 的问题
Problems with Android Spinners in a RecyclerView
我有一个 RecyclerView
,其中包含复杂的项目,由一个 class 表示,其中包含一个 TextView
小部件、一个 Spinner
小部件和一个 EditText
小部件. (想一想任务列表,其中包含任务名称、指向 select 处理任务已达到哪个阶段的旋转器,以及该处理阶段的完成日期。)
此列表的数据来自一个文件,对列表中的项目所做的更改将被写回该文件。当用户稍后返回应用程序时,列表应反映他之前看到的数据。
目前,我的应用程序显示项目列表,只是我不知道如何将 Spinner 的初始值设置为从文件中读取的值。 如何为列表中的每个微调器设置不同的初始 selection?
当前,当用户从Spinner
生成selection时,列表消失,selection不会作为用户的选择出现。我从另一个 post - Android: Spinner not showing the selected value - that I should call setSelection()
(inherited by Spinner
class from AbsSpinner). But that did nothing. In the XML layout, my Spinner has: android:textColor="@color/colorPrimaryDark"
so I don't think it's a problem of the text being there in a color that matches the background. See this List of Tasks 看到了。 如何才能让用户的select离子得以保留?
在 OnItemSelected()
中,如何告诉 activity 记录 selected 值,以便将其保存回文件? 我在网上找到的示例代码通常只使用 Toast 来显示 selection 已注册。我想我需要知道 RecyclerView
中的哪个项目包含这个 Spinner...
这是详细信息...
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 class 以保存将与该子任务步骤关联的 Spinner 视图。
这是更新后的 DetailItem class 定义:
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; }
}
我修改了适配器以将显示/selects 的微调器与任务步骤一起存储。我还移动了代码以将 Spinner 的侦听器从 ViewHolder 移动到 Adapter。
这是更新后的 SubtaskDetailAdapter class 定义及其扩展的 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 class 中的代码以在容器视图上设置 OnClickListener,因为我不想响应对 RecyclerView 项目的点击,而只对其中的各个视图进行响应。需要一个空的 OnClick,因为 holder 被声明为实现 OnClickListener 接口(我不知道是否需要)。
这是更新的(更简单的)ViewHolder class :
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 和 #2 仍未解决,这使得我的应用程序目前无法使用...有人想要解决这个问题吗???
我不知道你的代码将如何实现,但我会尝试通过示例回答你的问题来帮助你。
1) 如何为列表中的每个微调器设置不同的初始选择?
如果你想在 Spinner 中存储一个对象列表。
Class
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 变量作为 class Activity 中的属性。您可以创建一个变量作为属性并调用 spItemSelected 来存储所选元素(在我的代码中将 SpinnerItem itemSelected
替换为 spItemSelected
)。
3) 我如何告诉 activity 记录所选值以便将其保存回文件?
如果您在 class Activity 中选择元素作为属性,则更容易从任何函数调用它以将其存储在文件中。我不确定你什么时候想要保存文件。
非常感谢我的朋友 Sheldon 解决了这个问题!
问题 #1 和 #2 现在都已解决。问题只是文字颜色的问题!从 Spinner 中选择的项目在下拉列表中显示为白色,这很好,因为下拉列表的背景是黑色的。但是删除下拉菜单后文本颜色仍然是白色,而我的 activity 有一个灰白色背景和白色文本。
这是包含相关更改的更新代码:
@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
}
});
}
我有一个 RecyclerView
,其中包含复杂的项目,由一个 class 表示,其中包含一个 TextView
小部件、一个 Spinner
小部件和一个 EditText
小部件. (想一想任务列表,其中包含任务名称、指向 select 处理任务已达到哪个阶段的旋转器,以及该处理阶段的完成日期。)
此列表的数据来自一个文件,对列表中的项目所做的更改将被写回该文件。当用户稍后返回应用程序时,列表应反映他之前看到的数据。
目前,我的应用程序显示项目列表,只是我不知道如何将 Spinner 的初始值设置为从文件中读取的值。 如何为列表中的每个微调器设置不同的初始 selection?
当前,当用户从
Spinner
生成selection时,列表消失,selection不会作为用户的选择出现。我从另一个 post - Android: Spinner not showing the selected value - that I should callsetSelection()
(inherited bySpinner
class from AbsSpinner). But that did nothing. In the XML layout, my Spinner has:android:textColor="@color/colorPrimaryDark"
so I don't think it's a problem of the text being there in a color that matches the background. See this List of Tasks 看到了。 如何才能让用户的select离子得以保留?在
OnItemSelected()
中,如何告诉 activity 记录 selected 值,以便将其保存回文件? 我在网上找到的示例代码通常只使用 Toast 来显示 selection 已注册。我想我需要知道RecyclerView
中的哪个项目包含这个 Spinner...
这是详细信息...
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 class 以保存将与该子任务步骤关联的 Spinner 视图。
这是更新后的 DetailItem class 定义:
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; }
}
我修改了适配器以将显示/selects 的微调器与任务步骤一起存储。我还移动了代码以将 Spinner 的侦听器从 ViewHolder 移动到 Adapter。
这是更新后的 SubtaskDetailAdapter class 定义及其扩展的 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 class 中的代码以在容器视图上设置 OnClickListener,因为我不想响应对 RecyclerView 项目的点击,而只对其中的各个视图进行响应。需要一个空的 OnClick,因为 holder 被声明为实现 OnClickListener 接口(我不知道是否需要)。
这是更新的(更简单的)ViewHolder class :
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 和 #2 仍未解决,这使得我的应用程序目前无法使用...有人想要解决这个问题吗???
我不知道你的代码将如何实现,但我会尝试通过示例回答你的问题来帮助你。
1) 如何为列表中的每个微调器设置不同的初始选择?
如果你想在 Spinner 中存储一个对象列表。
Class
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 变量作为 class Activity 中的属性。您可以创建一个变量作为属性并调用 spItemSelected 来存储所选元素(在我的代码中将 SpinnerItem itemSelected
替换为 spItemSelected
)。
3) 我如何告诉 activity 记录所选值以便将其保存回文件?
如果您在 class Activity 中选择元素作为属性,则更容易从任何函数调用它以将其存储在文件中。我不确定你什么时候想要保存文件。
非常感谢我的朋友 Sheldon 解决了这个问题!
问题 #1 和 #2 现在都已解决。问题只是文字颜色的问题!从 Spinner 中选择的项目在下拉列表中显示为白色,这很好,因为下拉列表的背景是黑色的。但是删除下拉菜单后文本颜色仍然是白色,而我的 activity 有一个灰白色背景和白色文本。
这是包含相关更改的更新代码:
@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
}
});
}