web-dev-qa-db-fra.com

Android ListView avec bouton radio en mode Choix unique et une disposition de lignes personnalisée

J'ai un ListView , qui est en mode singleChoice . Tout ce que je veux, c’est d’afficher un bouton radio sur le côté qui, une fois cliqué, est mis en surbrillance pour indiquer qu’il est sélectionné, et lorsque vous cliquez sur un bouton différent, il revient à désélectionné et le nouveau devient sélectionné. Pourquoi est-ce si difficile? Cela ne devrait pas être aussi compliqué. J'ai passéJOURSà chercher une réponse appropriée à cette question et je n'ai rien trouvé. Je pose donc la question avec espoir, de manière claire et concise.

Ma mise en page pour la listview (R.layout.view_orders) :

<?xml version="1.0" encoding="utf-8"?>
<ListView 
        Android:choiceMode="singleChoice"
        Android:id="@Android:id/list"
        Android:layout_width="fill_parent"
        Android:layout_height="fill_parent"
        Android:divider="@drawable/list_divider"
        Android:dividerHeight="1px"
        Android:focusable="false"
        Android:focusableInTouchMode="false"
        Android:cacheColorHint="#00000000">
</ListView>

Ma ligne personnalisée (R.layout.orders_row) :

<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout
    xmlns:app="http://schemas.Android.com/apk/res/com.xxx.xxxxxx"
    xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:layout_width="fill_parent"
    Android:layout_height="wrap_content"
    Android:orientation="horizontal"
    Android:padding="6dip">

    <com.xxx.xxxxxx.VerticalLabelView
        app:text="SHORT"
        app:textColor="#666"
        app:textSize="14sp"
        Android:id="@+id/state"
        Android:layout_width="wrap_content"
        Android:layout_height="wrap_content"
        Android:layout_alignParentLeft="true"
        Android:layout_centerVertical="true" />

    <TextView
        Android:id="@+id/quantity"
        Android:layout_width="wrap_content"
        Android:layout_height="wrap_content"
        Android:layout_toRightOf="@id/state" 
        Android:layout_centerVertical="true"
        Android:gravity="center"
        Android:textSize="40sp"
        Android:layout_margin="2dip"
        Android:minWidth="30dip"
        Android:textColor="#555" />


    <RelativeLayout
        Android:layout_toRightOf="@id/quantity"
        Android:layout_centerVertical="true"
        Android:layout_width="fill_parent"
        Android:layout_height="wrap_content">

        <TextView
            Android:id="@+id/instrument"
            Android:layout_width="fill_parent"
            Android:layout_height="wrap_content"
            Android:textSize="20sp"
            Android:textColor="#333"
            Android:layout_marginLeft="2dip"
            Android:layout_marginRight="2dip"
            />


        <TextView
            Android:id="@+id/deets"
            Android:layout_below="@id/instrument"
            Android:layout_width="fill_parent"
            Android:layout_height="wrap_content"
            Android:textSize="16sp"
            Android:textColor="#888"
            Android:layout_marginLeft="2dip"
            Android:layout_marginRight="2dip"
            />

    </RelativeLayout>

        <RadioButton
            Android:id="@+id/selector"
            Android:layout_width="wrap_content"
            Android:layout_height="wrap_content"
            Android:layout_alignParentRight="true"
            Android:layout_centerVertical="true"
            />

</RelativeLayout>

Ma onCreate () méthode:

@Override
public void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);
    setContentView(R.layout.view_orders);
    client = new Client(handler);
    ola = new OrdersAdapter(this, R.layout.orders_row, Orders);
    setListAdapter(ola);
    final RelativeLayout loading = (RelativeLayout) findViewById(R.id.loading);
    panel = (PositionsPanel) findViewById(R.id.panel);
    Utility.showProgressBar(loading);
    client.Connect("orders");
}

Maintenant que tout ce qui est sous-jacent fonctionne comme prévu, vous cliquez sur un bouton radio et vous pouvez le sélectionner de manière appropriée dans la liste et le manipuler à ma guise. Cependant, lorsque le premier bouton radio est cliqué, le dernier sera sélectionné. Cliquez à nouveau sur le même bouton radio pour le sélectionner. Cliquez dessus encore une fois et rien ne se passe, le dernier et le premier sont sélectionnés. Maintenant, je clique sur un autre de la liste, il est sélectionné comme prévu. Cliquez sur l'un des boutons radio sélectionnés et rien ne se passe, le bouton radio reste sélectionné.

J'ai essayé d'utiliser les éléments suivants dans onCreate () :

@Override
public void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);
    setContentView(R.layout.view_orders);
    client = new Client(handler);
    ola = new OrdersAdapter(this, Android.R.layout.simple_list_item_single_choice, Orders);
    setListAdapter(ola);
    final RelativeLayout loading = (RelativeLayout) findViewById(R.id.loading);
    panel = (PositionsPanel) findViewById(R.id.panel);
    Utility.showProgressBar(loading);
    client.Connect("orders");
}

et cela montre simplement pas de boutons radio du tout. IMPRESSIONNANT

Maintenant peut-être (lire: très probablement), je suis juste dense et ne peux pas comprendre cela, mais j'ai vu cette question posée beaucoup sans réelle réponse. Beaucoup de références à d'autres tutoriels ou au livre du gars de Commonsware. Cependant, les commentaires sont anciens et son référentiel a tellement changé que ce ne sont plus des réponses correctes.

Alors, est-ce que quelqu'un a une idée de comment obtenir la fonctionnalité attendue? Sinon, transmettez-moi simplement le code source de l'application GMail. :)

26
zkwentz

N'oubliez pas que dans la ligne ListView, les éléments sont recyclés. Ceci est susceptible d’expliquer pourquoi les actions d’une rangée en affectent une autre. Creusez dans le livre de Mark et vous en trouverez une couverture. 

Si vous utilisez un adaptateur avec la liste, vous pouvez utiliser getView () sur un adaptateur pour ajouter un gestionnaire de clics à chaque ligne créée/recyclée et vous assurer que l'état est géré correctement lorsque les éléments de ligne sont créés et recyclés .

Mon approche consiste à stocker l'état dans ma propre structure de données, puis à utiliser getView () pour refléter cet élément dans l'interface utilisateur lorsque l'utilisateur fait défiler la liste vers le haut ou le bas. Il y a peut-être une meilleure solution, mais cela fonctionne pour moi.

8
Ollie C

Solution simple:

Ajoutez cette ligne dans la disposition RadioButton de votre fichier xml:

<RadioButton 
   ...
   Android:onClick="onClickRadioButton"
   ...
/>

Ensuite, dans la classe d'activité qui utilise ListView, ajoutez les éléments suivants:

   private RadioButton listRadioButton = null;
   int listIndex = -1;

   public void onClickRadioButton(View v) {
        View vMain = ((View) v.getParent());
        // getParent() must be added 'n' times, 
        // where 'n' is the number of RadioButtons' nested parents
        // in your case is one.

        // uncheck previous checked button. 
        if (listRadioButton != null) listRadioButton.setChecked(false);
        // assign to the variable the new one
        listRadioButton = (RadioButton) v;
        // find if the new one is checked or not, and set "listIndex"
        if (listRadioButton.isChecked()) {
            listIndex = ((ViewGroup) vMain.getParent()).indexOfChild(vMain); 
        } else {
            listRadioButton = null;
            listIndex = -1;
        }
    }

Avec ce code simple, un seul RadioButton est coché . Si vous touchez celui qui est déjà coché, il revient au mode non coché.

"listIndex" suit l'élément vérifié, -1 si aucun.

Si vous voulez toujours en cocher une, le code dans onClickRadioButton devrait être:

   public void onClickRadioButton(View v) {
        View vMain = ((View) v.getParent());
        int newIndex = ((ViewGroup) vMain.getParent()).indexOfChild(vMain);
        if (listIndex == newIndex) return;

        if (listRadioButton != null) {
                listRadioButton.setChecked(false);
        }
        listRadioButton = (RadioButton) v;
        listIndex = newIndex; 
    }
7
AlexKots

Mon nom est Gourab Singha. J'ai résolu ce problème. J'ai écrit ce code pour le résoudre. J'espère que cela vous aide tous. Merci. J'ai besoin de 3 heures pour cela.

public void add_option_to_list(View v){


    LinearLayout ll = (LinearLayout)v.getParent();
    LinearLayout ll_helper = null; 
    LinearLayout ll2 =  (LinearLayout)ll.getParent();
    LinearLayout ll3 =  (LinearLayout)ll2.getParent();

    ListView ll4 =  (ListView)ll3.getParent();
    //RadioButton rb1= (RadioButton)ll.getChildAt(1);
    Toast.makeText(getApplicationContext(), ", "+ll4.getChildCount(), Toast.LENGTH_LONG).show();    
    //ll.findViewById(R.id.radio_option_button)
    for(int k=0;k<ll4.getChildCount();k++){

        ll3 = (LinearLayout)ll4.getChildAt(k);
        ll2 = (LinearLayout)ll3.getChildAt(0);
        ll_helper = (LinearLayout)ll2.getChildAt(0);
        RadioButton rb1 = (RadioButton)ll_helper.getChildAt(1);
        if(rb1.isChecked())
        rb1.setChecked(false);


    }
    RadioButton rb1 = (RadioButton)ll.getChildAt(1);
    rb1.setChecked(true);
    //Toast.makeText(getApplicationContext(), ""+((TextView)ll.getChildAt(4)).getText().toString(), Toast.LENGTH_LONG).show();


    }

Le XML:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent"
    Android:orientation="vertical" >

    <LinearLayout

        Android:layout_width="match_parent"
        Android:layout_height="wrap_content"
        Android:orientation="vertical" >


        <LinearLayout
            Android:layout_width="match_parent"
            Android:layout_height="wrap_content"
            Android:orientation="horizontal" >

            <TextView
                Android:id="@+id/id"
                Android:layout_width="wrap_content"
                Android:layout_height="wrap_content"
                Android:text="Category Name"
                Android:textAppearance="?android:attr/textAppearanceMedium"
                Android:visibility="gone" />

            <RadioButton
                Android:id="@+id/radio_option_button"
                Android:layout_width="wrap_content"
                Android:layout_height="wrap_content"
                Android:onClick="add_option_to_list"
                Android:text="" />

            <TextView
                Android:id="@+id/option_name"
                Android:layout_width="wrap_content"
                Android:layout_height="fill_parent"
                Android:layout_marginLeft="3dp"
                Android:layout_weight="5.35"
                Android:text="Category Name"
                Android:textAppearance="?android:attr/textAppearanceMedium" />



            <TextView
                Android:id="@+id/option_price"
                Android:layout_width="76dp"
                Android:layout_height="wrap_content"
                Android:layout_marginRight="10dp"
                Android:gravity="right"
                Android:text="$0.00"
                Android:textAppearance="?android:attr/textAppearanceMedium" />

            <TextView
                Android:id="@+id/option_unit_price"
                Android:layout_width="fill_parent"
                Android:layout_height="wrap_content"
                Android:layout_marginRight="5dp"
                Android:gravity="right"
                Android:text="$0.00"
                Android:textAppearance="?android:attr/textAppearanceMedium"
                Android:visibility="gone" />
        </LinearLayout>

    </LinearLayout>

</LinearLayout>
3
Gourab Singha

Je viens d'avoir un problème similaire. Il semblait que cela était lié au recyclage des vues. Mais j’ajoutai des journaux partout et remarquai que le traitement du recyclage était bon . Le problème était le traitement des boutons radio. J'utilisais:

if(answer == DBAdapter.YES){
   rbYes.setChecked(true); 
   rbNo.setChecked(false);                    
}
else if(answer == DBAdapter.NO){
   rbYes.setChecked(false); 
   rbNo.setChecked(true);
}
else{
  rbYes.setChecked(false); 
  rbNo.setChecked(false);
}                

et la bonne façon de le faire est:

if(answer == DBAdapter.YES){
   rbYes.setChecked(true); 
}
else if(answer == DBAdapter.NO){
   rbNo.setChecked(true);
}
else{
   RadioGroup rg = (RadioGroup)(holder.answerView);
   rg.clearCheck();
}                
2
Yar

Vous pouvez réaliser beaucoup plus simple, suivez ceci. Il a été essayé à la guimauve

1) Avoir une CheckedTextView:

<CheckedTextView xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:id="@Android:id/text1"
    Android:layout_width="match_parent"
    Android:layout_height="wrap_content"
    Android:checkMark="?android:attr/listChoiceIndicatorSingle"
    Android:padding="10dp"
    Android:textAppearance="?android:attr/textAppearanceMedium">

  • ListChoiceIndicatorSingle affichera RadioButton.

  • ListChoiceIndicatorMultiple affichera CheckBox

N'oubliez pas de mettre choiceMode:Single dans votre ListView.

1
Raja Jawahar

À l'intérieur de l'adaptateur

 viewHolder.radioBtn.setOnClickListener(new OnClickListener() {

               @Override
               public void onClick(View v) {
                   Log.e("called", "called");
                   if(position != mSelectedPosition && mSelectedRB != null){
                       mSelectedRB.setChecked(false);
                   }



                   mSelectedPosition = position;
                   mSelectedRB = (RadioButton)v;
               }
           });

           viewHolder.radioBtn.setText(mList[position]);  
           if(mSelectedPosition != position){
               viewHolder.radioBtn.setChecked(false);

           }else{
               viewHolder.radioBtn.setChecked(true);

               if(mSelectedRB != null && viewHolder.radioBtn != mSelectedRB){
                   mSelectedRB = viewHolder.radioBtn;
               }
           }

ajouter du style au bouton radio 

<?xml version="1.0" encoding="utf-8"?>
 <selector xmlns:Android="http://schemas.Android.com/apk/res/Android">

    <item Android:drawable="@drawable/tick" Android:state_checked="true"/>
    <item Android:drawable="@drawable/untick" Android:state_checked="false"/>

 </selector>

et dans le XML utiliser le style de bouton radio 

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

    <RadioButton
        Android:id="@+id/radioBtn"
        Android:layout_width="match_parent"
        Android:layout_height="50dp"
        Android:button="@null"
        Android:checked="true"
        Android:drawablePadding="30dp"
        Android:drawableRight="@drawable/checkmark"
        Android:text="first" />

</RelativeLayout>
0
sharath yadhav