Ich habe ein RecyclerView
die komplexe Elemente enthält, durch eine Klasse repräsentiert ein TextView
Widget enthält, ein Spinner
Widget & ein EditText
Widget. (Denken Sie an eine Aufgabenliste mit Aufgabenname, einem Drehfeld, um auszuwählen, welche Stufe der Verarbeitung der Aufgabe erreicht wurde, und einem Abschlussdatum für diese Verarbeitungsstufe.)Probleme mit Android Spinners in einem RecyclerView
Die Daten für diese Liste stammen aus einer Datei, und Änderungen an Elementen in der Liste werden in die Datei zurückgeschrieben. Wenn der Benutzer später zur App zurückkehrt, sollte die Liste die Daten so wiedergeben, wie er sie zuvor gesehen hat.
Derzeit meine App zeigt die Liste der Elemente, mit der Ausnahme, dass ich weiß nicht, wie man den Wert den Anfangswert des Spinners gesetzt aus der Datei gelesen. Wie kann ich eine andere anfängliche Auswahl für jeden Spinner in der Liste festlegen?
Wenn der Benutzer eine Auswahl aus
Spinner
trifft, verschwindet die Liste zur Zeit und die Auswahl erscheint nicht als die Wahl des Benutzers. Ich sah von einem anderen Beitrag - Android: Spinner not showing the selected value - dass ichsetSelection()
anrufen sollte (geerbt vonSpinner
Klasse von AbsSpinner). Aber das hat nichts getan. Im XML-Layout hat mein Spinner:android:textColor="@color/colorPrimaryDark"
, also glaube ich nicht, dass es ein Problem ist, dass der Text in einer Farbe ist, die mit dem Hintergrund übereinstimmt. Siehe hierzu List of Tasks. Wie kann ich die Auswahl des Benutzers beibehalten?In
OnItemSelected()
, wie kann ich feststellen, die Aktivität den gewählten Wert zu beachten, damit es wieder gespeichert werden kann einreichen? Der Beispielcode, den ich online gefunden habe, verwendet normalerweise nur einen Toast, um anzuzeigen, dass die Auswahl registriert wurde. Ich denke, was ich wissen muss, welches Element in derRecyclerView
diese Spinner enthält ...
Hier sind die Details ...
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
}
}
}
}
Danke für jede Hilfe, die Sie anbieten können, während ich versuche, dieses Zeug zu lernen!
NEUE INFORMATIONEN
Ich habe eine Lösung für meine Frage # 3 gefunden, obwohl es wie eine kluge fühlt. Es mag einen besseren Weg geben, aber da niemand mich dazu erleuchtet hat, bleibt mir das übrig.
Mein Datenfeld, das im RecyclerView angezeigt werden soll, ist ein Array von DetailItem-Objekten. Ich habe der DetailItem-Klasse eine weitere Instanzvariable hinzugefügt, die die Spinner-Ansicht enthält, die diesem Teilaufgabenschritt zugeordnet wird.
Hier ist die aktualisierte DetailItem Klassendefinition:
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; }
}
ich den Adapter mit der Aufgabe Schritt die Spinner speichern geändert, die anzeigt,/wählt es. Ich habe auch den Code verschoben, um den Listener für den Spinner vom ViewHolder zum Adapter zu setzen.
Hier ist die aktualisierte SubtaskDetailAdapter Klassendefinition mit seiner erweiterten onBindViewHolder Methode:
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
}
});
}
ich den Code in der ViewHolder Klasse entfernt, um einen OnClickListener auf dem Behälter Ansicht zu setzen, da ich auf Tippen nicht reagiert werden soll auf dem RecyclerView-Element, aber nur auf einzelnen Ansichten darin. Ein leeres OnClick ist erforderlich, da der Inhaber für die Implementierung der OnClickListener-Schnittstelle deklariert ist (ich weiß nicht, ob dies erforderlich ist).
Hier ist die aktualisierte (einfacher) ViewHolder Klasse:
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
}
}
Meine Fragen # 1 & # 2 sind nach wie vor ungelöst, die meine app ziemlich unbrauchbar zur Zeit macht ... Wer Kredit wollen für die Lösung Dies???
Post-Code. – Adithya