103 votes

Comment regrouper les boutons radio de différents LinearLayouts?

Je me demandais s'il est possible de regrouper chaque RadioButton individuel dans un unique RadioGroup en conservant la même structure. Ma structure ressemble à ceci :

  • LinearLayout_principal
    • LinearLayout_1
      • RadioButton1
    • LinearLayout_2
      • RadioButton2
    • LinearLayout_3
      • RadioButton3

Comme vous pouvez le constater, chaque RadioButton est actuellement un enfant d'un LinearLayout différent. J'ai essayé d'utiliser la structure ci-dessous, mais cela ne fonctionne pas :

  • RadioGroup
    • LinearLayout_principal
      • LinearLayout_1
        • RadioButton1
      • LinearLayout_2
        • RadioButton2
      • LinearLayout_3
        • RadioButton3

5voto

Roy Ben-Sasson Points 101

Vous pouvez utiliser ce code d'extension simple RadioGroup. Déposez n'importe quel layout/vue/image avec les RadioButtons et cela fonctionnera.

Il contient un rappel de sélection qui renvoie le RadioButton sélectionné avec son index et vous pouvez définir la sélection de manière programmatique par index ou id:

import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.RadioButton;
import android.widget.RadioGroup;

import java.util.ArrayList;

public class EnhancedRadioGroup extends RadioGroup implements View.OnClickListener {

    public interface OnSelectionChangedListener {
        void onSelectionChanged(RadioButton radioButton, int index);
    }

    private OnSelectionChangedListener selectionChangedListener;
    ArrayList radioButtons = new ArrayList<>();

    public EnhancedRadioGroup(Context context) {
        super(context);
    }

    public EnhancedRadioGroup(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        if (changed) {
            getRadioButtons();
        }
    }

    private void getRadioButtons() {
        radioButtons.clear();
        checkForRadioButtons(this);
    }

    private void checkForRadioButtons(ViewGroup viewGroup) {
        if (viewGroup == null) {
            return;
        }
        for (int i = 0; i < viewGroup.getChildCount(); i++) {
            View v = viewGroup.getChildAt(i);
            if (v instanceof RadioButton) {
                v.setOnClickListener(this);
                // store index of item
                v.setTag(radioButtons.size());
                radioButtons.add((RadioButton) v);
            }
            else if (v instanceof ViewGroup) {
                checkForRadioButtons((ViewGroup)v);
            }
        }
    }

    public RadioButton getSelectedItem() {
        if (radioButtons.isEmpty()) {
            getRadioButtons();
        }
        for (RadioButton radioButton : radioButtons) {
            if (radioButton.isChecked()) {
                return radioButton;
            }
        }
        return null;
    }

    public void setOnSelectionChanged(OnSelectionChangedListener selectionChangedListener) {
        this.selectionChangedListener = selectionChangedListener;
    }

    public void setSelectedById(int id) {
        if (radioButtons.isEmpty()) {
            getRadioButtons();
        }
        for (RadioButton radioButton : radioButtons) {
            boolean isSelectedRadioButton = radioButton.getId() == id;
            radioButton.setChecked(isSelectedRadioButton);
            if (isSelectedRadioButton && selectionChangedListener != null) {
                selectionChangedListener.onSelectionChanged(radioButton, (int)radioButton.getTag());
            }
        }
    }

    public void setSelectedByIndex(int index) {
        if (radioButtons.isEmpty()) {
            getRadioButtons();
        }
        if (radioButtons.size() > index) {
            setSelectedRadioButton(radioButtons.get(index));
        }
    }

    @Override
    public void onClick(View v) {
        setSelectedRadioButton((RadioButton) v);
    }

    private void setSelectedRadioButton(RadioButton rb) {
        if (radioButtons.isEmpty()) {
            getRadioButtons();
        }
        for (RadioButton radioButton : radioButtons) {
            radioButton.setChecked(rb == radioButton);
        }
        if (selectionChangedListener != null) {
            selectionChangedListener.onSelectionChanged(rb, (int)rb.getTag());
        }
    }
}

Utilisez-le dans votre layout xml:

       Layouts contenant des RadioButtons/Images/Vues et d'autres RadioButtons

Pour vous inscrire au rappel:

        enhancedRadioGroupInstance.setOnSelectionChanged(new EnhancedRadioGroup.OnSelectionChangedListener() {
            @Override
            public void onSelectionChanged(RadioButton radioButton, int index) {

            }
        });

4voto

Luccas Correa Points 41

J'ai créé ces deux méthodes pour résoudre ce problème. Tout ce que vous avez à faire est de passer le ViewGroup où se trouvent les RadioButtons (peut être un RadioGroup, LinearLayout, RelativeLayout, etc.) et elle définit les événements OnClick de manière exclusive, c'est-à-dire, chaque fois qu'un des RadioButtons qui est un enfant du ViewGroup (à n'importe quel niveau imbriqué) est sélectionné, les autres sont désélectionnés. Cela fonctionne avec autant de mises en page imbriquées que vous le souhaitez.

public class Utils {
    public static void setRadioExclusiveClick(ViewGroup parent) {
        final List radios = getRadioButtons(parent);

        for (RadioButton radio: radios) {
            radio.setOnClickListener(new OnClickListener() {

                @Override
                public void onClick(View v) {
                    RadioButton r = (RadioButton) v;
                    r.setChecked(true);
                    for (RadioButton r2:radios) {
                        if (r2.getId() != r.getId()) {
                            r2.setChecked(false);
                        }
                    }

                }
            });
        }
    }

    private static List getRadioButtons(ViewGroup parent) {
        List radios = new ArrayList();
        for (int i=0;i < parent.getChildCount(); i++) {
            View v = parent.getChildAt(i);
            if (v instanceof RadioButton) {
                radios.add((RadioButton) v);
            } else if (v instanceof ViewGroup) {
                List nestedRadios = getRadioButtons((ViewGroup) v);
                radios.addAll(nestedRadios);
            }
        }
        return radios;
    }
}

L'utilisation dans une activité serait comme ceci :

ViewGroup parent = findViewById(R.id.radios_parent);
Utils.setRadioExclusiveClick(parent);

2voto

Egidijus Points 184

J'ai écrit ma propre classe de groupe de radio qui permet de contenir des boutons radio imbriqués. Jetez un coup d'oeil. Si vous trouvez des bugs, merci de me le faire savoir.

import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CompoundButton;
import android.widget.LinearLayout;

/**
 * Cette classe est utilisée pour créer une portée d'exclusion multiple pour un ensemble de
 * boutons composites. Cocher un bouton composite qui appartient à un groupe désactive tout autre bouton
 * composite précédemment cochés dans le même groupe. Initialement, tous les boutons composites ne sont pas cochés. Bien qu'il ne soit pas possible de décocher un
 * bouton composite en particulier, le groupe peut être effacé pour supprimer l'état coché. En gros, cette classe étend la fonctionnalité de
 * {@link android.widget.RadioGroup} car elle ne nécessite pas que les boutons composites soient des enfants directs du groupe. Cela signifie que vous pouvez envelopper des boutons composites avec d'autres vues. 
 * 
 * 
 * IMPORTANT ! Suivez ces instructions lors de l'utilisation de cette classe :
 * 1. Chaque enfant direct de ce groupe doit contenir un bouton composite ou être
 * lui-même un bouton composite.
 * 2. Ne définissez aucun auditeur "on click" ou "on checked changed" pour les enfants
 * de ce groupe.
 */
public class CompoundButtonsGroup extends LinearLayout {

 private View checkedView;
 private OnCheckedChangeListener listener;
 private OnHierarchyChangeListener onHierarchyChangeListener;

 private OnHierarchyChangeListener onHierarchyChangeListenerInternal = new OnHierarchyChangeListener() {

  @Override
  public final void onChildViewAdded(View parent, View child) {
   notifyHierarchyChanged(null);
   if (CompoundButtonsGroup.this.onHierarchyChangeListener != null) {
    CompoundButtonsGroup.this.onHierarchyChangeListener.onChildViewAdded(
      parent, child);
   }
  }

  @Override
  public final void onChildViewRemoved(View parent, View child) {
   notifyHierarchyChanged(child);
   if (CompoundButtonsGroup.this.onHierarchyChangeListener != null) {
    CompoundButtonsGroup.this.onHierarchyChangeListener.onChildViewRemoved(
      parent, child);
   }
  }
 };

 public CompoundButtonsGroup(Context context) {
  super(context);
  init();
 }

 public CompoundButtonsGroup(Context context, AttributeSet attrs) {
  super(context, attrs);
  init();
 }

 public CompoundButtonsGroup(Context context, AttributeSet attrs, int defStyle) {
  super(context, attrs, defStyle);
  init();
 }

 private void init() {
  super.setOnHierarchyChangeListener(this.onHierarchyChangeListenerInternal);
 }

 @Override
 public final void setOnHierarchyChangeListener(OnHierarchyChangeListener listener) {
  this.onHierarchyChangeListener = listener;
 }

 /**
  * Enregistre un rappel à appeler lorsque la vue cochée change dans ce
  * groupe.
  * 
  * @param listener
  *            le rappel à appeler lors du changement d'état cochée.
  */
 public void setOnCheckedChangeListener(OnCheckedChangeListener listener) {
  this.listener = listener;
 }

 /**
  * Renvoie la vue actuellement sélectionnée dans ce groupe. En cas de sélection vide, la
  * valeur retournée est null.
  */
 public View getCheckedView() {
  return this.checkedView;
 }

 /**
  * Renvoie l'indice de la vue actuellement sélectionnée dans ce groupe. En cas de sélection vide, la valeur
  * retournée est -1.
  */
 public int getCheckedViewIndex() {
  return (this.checkedView != null) ? indexOfChild(this.checkedView) : -1;
 }

 /**
  * Définit la sélection sur la vue dont l'index dans le groupe est passé en
  * paramètre.
  * 
  * @param index
  *            l'indice de la vue à sélectionner dans ce groupe.
  */
 public void check(int index) {
  check(getChildAt(index));
 }

 /**
  * Efface la sélection. Lorsque la sélection est effacée, aucune vue dans ce
  * groupe n'est sélectionnée et {@link #getCheckedView()} renvoie null.
  */
 public void clearCheck() {
  if (this.checkedView != null) {
   findCompoundButton(this.checkedView).setChecked(false);
   this.checkedView = null;
   onCheckedChanged();
  }
 }

 private void onCheckedChanged() {
  if (this.listener != null) {
   this.listener.onCheckedChanged(this.checkedView);
  }
 }

 private void check(View child) {
  if (this.checkedView == null || !this.checkedView.equals(child)) {
   if (this.checkedView != null) {
    findCompoundButton(this.checkedView).setChecked(false);
   }

   CompoundButton comBtn = findCompoundButton(child);
   comBtn.setChecked(true);

   this.checkedView = child;
   onCheckedChanged();
  }
 }

 private void notifyHierarchyChanged(View removedView) {
  for (int i = 0; i < getChildCount(); i++) {
   View child = getChildAt(i);
   child.setOnClickListener(new OnClickListener() {

    @Override
    public void onClick(View v) {
     check(v);
    }
   });
   CompoundButton comBtn = findCompoundButton(child);
   comBtn.setClickable(comBtn.equals(child));
  }

  if (this.checkedView != null && removedView != null
    && this.checkedView.equals(removedView)) {
   clearCheck();
  }
 }

 private CompoundButton findCompoundButton(View view) {
  if (view instanceof CompoundButton) {
   return (CompoundButton) view;
  }

  if (view instanceof ViewGroup) {
   for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
    CompoundButton compoundBtn = findCompoundButton(((ViewGroup) view)
      .getChildAt(i));
    if (compoundBtn != null) {
     return compoundBtn;
    }
   }
  }

  return null;
 }

 /**
  * Définition de l'interface pour un rappel à appeler lorsque la vue cochée
  * est modifiée dans ce groupe.
  */
 public interface OnCheckedChangeListener {

  /**
   * Appelé lorsque la vue cochée a changé.
   * 
   * @param checkedView
   *            nouvelle vue cochée ou null si la sélection a été effacée dans le
   *            groupe.
   */
  public void onCheckedChanged(View checkedView);
 }

}

2voto

madx Points 159

Vous devez faire deux choses :

  1. Utiliser mListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
  2. Faire en sorte que votre vue de ligne personnalisée implémente Checkable.

Je pense donc que la meilleure solution consiste à implémenter Checkable à l'intérieur de votre LinearLayout interne : (merci à daichan4649, à partir de son lien, https://gist.github.com/daichan4649/5245378, j'ai pris tout le code ci-dessous)

CheckableLayout.java

package daichan4649.test;

import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.Checkable;
import android.widget.LinearLayout;

public class CheckableLayout extends LinearLayout implements Checkable {

    private static final int[] CHECKED_STATE_SET = { android.R.attr.state_checked };

    public CheckableLayout(Context context) {
        super(context, null);
    }

    public CheckableLayout(Context context, AttributeSet attrs) {
        super(context, attrs, 0);
    }

    public CheckableLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    private boolean checked;

    @Override
    public boolean isChecked() {
        return checked;
    }

    @Override
    public void setChecked(boolean checked) {
        if (this.checked != checked) {
            this.checked = checked;
            refreshDrawableState();

            for (int i = 0; i < getChildCount(); i++) {
                View child = getChildAt(i);
                if (child instanceof Checkable) {
                    ((Checkable) child).setChecked(checked);
                }
            }
        }
    }

    @Override
    public void toggle() {
        setChecked(!checked);
    }

    @Override
    protected int[] onCreateDrawableState(int extraSpace) {
        final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
        if (isChecked()) {
            mergeDrawableStates(drawableState, CHECKED_STATE_SET);
        }
        return drawableState;
    }
}

inflater_list_column.xml

TestFragment.java

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

    View view = inflater.inflate(R.layout.fragment_test, container, false);

    // Données à afficher
    List dataList = new ArrayList();

    // Position de sélection initiale
    int initSelectedPosition = 3;

    // Configuration de la liste
    TestAdapter adapter = new TestAdapter(getActivity(), dataList);
    ListView listView = (ListView) view.findViewById(R.id.list);
    listView.setAdapter(adapter);
    listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
    listView.setItemChecked(initSelectedPosition, true);

    listView.setOnItemClickListener(new OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView parent, View view, int position, long id) {
            // Répercuter l'état de sélection sur l'élément enfant (checkable)
            Checkable child = (Checkable) parent.getChildAt(position);
            child.toggle();
        }
    });
    return view;
}

private static class TestAdapter extends ArrayAdapter {

    private LayoutInflater inflater;

    public TestAdapter(Context context, List dataList) {
        super(context, 0, dataList);
        inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        final ViewHolder holder;
        if (convertView == null) {
            convertView = inflater.inflate(R.layout.inflater_list_column, null);
            holder = new ViewHolder();
            holder.text = (TextView) convertView.findViewById(R.id.text);
            convertView.setTag(holder);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }

        // bindData
        holder.text.setText(getItem(position));
        return convertView;
    }
}

private static class ViewHolder {
    TextView text;
}

2voto

umerk44 Points 1666

J'ai rencontré le même problème car je veux placer 4 boutons radio différents dans deux linearlayout différents et ces mises en page seront les enfants du groupe de boutons radio. Pour obtenir le comportement souhaité dans RadioGroup, j'ai surchargé la fonction addView

Voici la solution

public class AgentRadioGroup extends RadioGroup
{

    public AgentRadioGroup(Context context) {
        super(context);
    }

    public AgentRadioGroup(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public void onViewAdded(View child) {
        if( child instanceof ViewGroup)
        {
            ViewGroup viewGroup = (ViewGroup) child;
            for(int i=0; i

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