2 votes

Android : Spinner et ListView avec la même ArrayList

J'essaie de mettre en place une activité de sélection pour une liste donnée d'éléments. Chaque élément est vérifiable, j'ai donc un élément avec une TextView et une CheckBox. J'ai implémenté une ListView pour afficher toutes les options et un Spinner pour n'afficher que les dix premiers choix, en tant que sous-ensemble de la même liste. Pour l'instant, j'affiche tous les éléments à la fois dans la ListView et dans le Spinner.

Je veux que les éléments de la ListView soient mis à jour lorsque l'utilisateur sélectionne un élément dans le Spinner (Note : Le chemin inverse fonctionne bien, car le Spinner récupère les éléments mis à jour). Liste de tableaux à chaque fois qu'il descend). J'ai essayé de mettre en œuvre setOnItemSelectedListener pour mon Spinner, et d'appeler notifyOnDataSetChanged() pour mon ListViewAdapter à l'intérieur du Listener. Mais le Listener n'est appelé qu'en cas d'effondrement et j'obtiens un message d'avertissement bizarre (peut-être sans rapport avec le sujet).

L'écouteur onItemSelectedListener du Spinner ne s'exécute que lorsque le Spinner est réduit. Mais notifyOnDataSetChanged() semble ignorer le statut coché des éléments en tant que changement. Comment puis-je faire en sorte que la première option s'exécute à chaque fois que je vérifie un élément et que le changement soit correctement reçu par le ListAdapter ?

Voici le code de Activity.java :

public class TriageReasonActivity extends BaseActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_triage_reason);

    final String[] select_qualification = {
            "Select Qualification", "10th / Below", "12th", "Diploma", "UG",
            "PG", "Phd"};
    Spinner spinner = (Spinner) findViewById(R.id.top_reasons_spinner);
    ListView symptoms_list  = (ListView) findViewById(R.id.view_list_symptoms);
    ArrayList<Symptoms> listVOs = new ArrayList<>();

    for (int i = 0; i < select_qualification.length; i++) {
        Symptoms reason = new Symptoms();
        reason.setTitle(select_qualification[i]);
        reason.setSelected(false);
        listVOs.add(reason);
    }
    SymptomsListAdapter mListAdapter = new SymptomsListAdapter(this, 0,
            listVOs);
    SymptomsSpinnerAdapter mSpinnerAdapter = new SymptomsSpinnerAdapter(this, 0,
            listVOs);

    symptoms_list.setAdapter(mListAdapter);
    spinner.setAdapter(mSpinnerAdapter);

    spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
        @Override
        public void onItemSelected(AdapterView<?> parentView, View selectedItemView, int position, long id) {
            Log.i("Item selected", "but not cahnged");
            symptoms_list.invalidateViews();
            mListAdapter.notifyDataSetInvalidated();
        }

        @Override
        public void onNothingSelected(AdapterView<?> parentView) {
            Log.i("Not item selected", "but actually it did");
        }

    });
}

Le code de l'adaptateur SpinnerCustom :

public class SymptomsSpinnerAdapter extends ArrayAdapter<Symptoms>{
private Context mContext;
private ArrayList<Symptoms> listState;
private SymptomsSpinnerAdapter myAdapter;
private boolean isFromView = false;

/*@Override
public void notifyDataSetChanged() {
    super.notifyDataSetChanged();
    //mNotifyOnChange = true;
}*/

public SymptomsSpinnerAdapter(Context context, int resource, List<Symptoms> objects) {
    super(context, resource, objects);
    this.mContext = context;
    this.listState = (ArrayList<Symptoms>) objects;
    this.myAdapter = this;
}

@Override
public View getDropDownView(int position, View convertView,
                            ViewGroup parent) {
    return getCustomView(position, convertView, parent);
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    return getCustomView(position, convertView, parent);
}

public View getCustomView(final int position, View convertView,
                          ViewGroup parent) {

    final ViewHolder holder;
    if (convertView == null) {
        LayoutInflater layoutInflator = LayoutInflater.from(mContext);
        convertView = layoutInflator.inflate(R.layout.item_reasons, null);
        holder = new ViewHolder();
        holder.mTextView = (TextView) convertView.findViewById(R.id.text);
        holder.mCheckBox = (CheckBox) convertView.findViewById(R.id.checkbox);
        convertView.setTag(holder);
    } else {
        holder = (ViewHolder) convertView.getTag();
    }

    holder.mTextView.setText(listState.get(position).getTitle());

    // To check weather checked event fire from getview() or user input
    isFromView = true;
    holder.mCheckBox.setChecked(listState.get(position).isSelected());
    isFromView = false;

    if ((position == 0)) {
        holder.mCheckBox.setVisibility(View.INVISIBLE);
    } else {
        holder.mCheckBox.setVisibility(View.VISIBLE);
    }
    holder.mCheckBox.setTag(position);
    holder.mCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {

        @Override
        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
            int getPosition = (Integer) buttonView.getTag();

            if (!isFromView) {
                listState.get(position).setSelected(isChecked);
            }
        }
    });
    return convertView;
}

@Override
public int getCount() {
    return listState.size();
}

@Override
public Symptoms getItem(int position) {
    if( position < 1 ) {
        return null;
    }
    else {
        return listState.get(position-1);
    }
}

@Override
public long getItemId(int position) {
    return 0;
}

private class ViewHolder {
    private TextView mTextView;
    private CheckBox mCheckBox;
}
}

Voici le ListAdapter (presque identique) :

public class SymptomsListAdapter extends BaseAdapter implements ListAdapter {
private Context mContext;
private ArrayList<Symptoms> listState;
private boolean isFromView = false;

public SymptomsListAdapter(Context context, int resource, List<Symptoms> objects) {
    this.mContext = context;
    this.listState = (ArrayList<Symptoms>) objects;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    return getCustomView(position, convertView, parent);
}

public View getCustomView(final int position, View convertView,
                          ViewGroup parent) {

    ViewHolder holder;
    if (convertView == null) {
        LayoutInflater layoutInflator = LayoutInflater.from(mContext);
        convertView = layoutInflator.inflate(R.layout.item_reasons, null);
        holder = new SymptomsListAdapter.ViewHolder();
        holder.mTextView = (TextView) convertView.findViewById(R.id.text);
        holder.mCheckBox = (CheckBox) convertView.findViewById(R.id.checkbox);
        convertView.setTag(holder);
    } else {
        holder = (SymptomsListAdapter.ViewHolder) convertView.getTag();
    }

    holder.mTextView.setText(listState.get(position).getTitle());

    // To check weather checked event fire from getview() or user input
    isFromView = true;
    holder.mCheckBox.setChecked(listState.get(position).isSelected());
    isFromView = false;

    if ((position == 0)) {
        holder.mCheckBox.setVisibility(View.INVISIBLE);
    } else {
        holder.mCheckBox.setVisibility(View.VISIBLE);
    }
    holder.mCheckBox.setTag(position);
    holder.mCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {

        @Override
        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
            int getPosition = (Integer) buttonView.getTag();

            if (!isFromView) {
                listState.get(position).setSelected(isChecked);
            }
        }
    });
    return convertView;
}

@Override
public int getCount() {
    return listState.size();
}

@Override
public Symptoms getItem(int position) {
    if( position < 1 ) {
        return null;
    }
    else {
        return listState.get(position-1);
    }
}

@Override
public long getItemId(int position) {
    return 0;
}

private class ViewHolder {
    public TextView mTextView;
    public CheckBox mCheckBox;
}
}

Et voici l'avertissement que je reçois :

W/art: Before Android 4.1, method int android.support.v7.widget.DropDownListView.lookForSelectablePosition(int, boolean) would have incorrectly overridden the package-private method in android.widget.ListView

EDIT : Ajout des layouts et de la classe de modèle au cas où ils causeraient un problème : Disposition de l'activité :

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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"
tools:context="demo.hb.activity.visit.TriageReasonActivity">

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textFontWeight="6dp"
        android:textSize="30sp"
        android:layout_margin="20dp"
        android:textAlignment="center"
        android:textColor="#000000"
        android:text="What is the reason for your visit?" />

    <Spinner
        android:id="@+id/top_reasons_spinner"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@android:drawable/btn_dropdown"
        android:spinnerMode="dropdown"/>

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:layout_gravity="end">

        <ListView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:id="@+id/view_list_symptoms"
            android:layout_above="@+id/next_btn"
            android:layout_alignParentTop="true"/>

    </RelativeLayout>
</LinearLayout>

</FrameLayout>

Disposition de l'article :

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content">

<TextView
    android:id="@+id/text"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginStart="20dp"
    android:text="text"
    android:textAlignment="gravity" />

<CheckBox
    android:id="@+id/checkbox"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentEnd="true" />
</RelativeLayout>

Classe de modèle :

public class Symptoms {
private String title;
private boolean selected;

public String getTitle() {
    return title;
}

public void setTitle(String title) {
    this.title = title;
}

public boolean isSelected() {
    return selected;
}

public void setSelected(boolean selected) {
    this.selected = selected;
}
}

0voto

BlackHatSamurai Points 6211

Si rien ne change, c'est parce que vous n'avez pas mis en œuvre la méthode permettant de gérer les modifications de l'ensemble des données. Vous devez gérer la façon dont les données sont rechargées dans votre adaptateur :

public class SymptomsListAdapter extends BaseAdapter implements ListAdapter {
  ...
  public void refreshData(ArrayList<Symptoms> objects){
    this.listState = (ArrayList<Symptoms>) objects;
    notifyDataSetChanged();
  }
  ...
}

Este enlace explique très bien comment le notifyDataSetInvalidated() fonctionne (ou, dans votre cas, pourquoi il ne fonctionne pas).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X