web-dev-qa-db-ja.com

異なるLinearLayoutsからRadioButtonをグループ化する方法は?

同じ構造を維持する一意のRadioButtonに各RadioGroupをグループ化できるかどうか疑問に思っていました。私の構造は次のようになります。

  • LinearLayout_main
    • LinearLayout_1
      • RadioButton1
    • LinearLayout_2
      • RadioButton2
    • LinearLayout_3
      • RadioButton3

ご覧のとおり、各RadioButtonは異なるLinearLayoutの子です。以下の構造を使用してみましたが、機能しません:

  • ラジオグループ
    • LinearLayout_main
      • LinearLayout_1
        • RadioButton1
      • LinearLayout_2
        • RadioButton2
      • LinearLayout_3
        • RadioButton3
80
marcoqf73

Google/Androidの良き人々は、RadioButtonsを使用するとき、Android UI/layoutシステムの他のすべての側面に付随する柔軟性は必要ないと考えているようです。単純に、彼らはあなたがレイアウトとラジオボタンを入れ子にしたくないのです。

したがって、問題を回避する必要があります。つまり、独自にラジオボタンを実装する必要があります。

これは本当に難しいことではありません。 onCreate()で、独自のonClick()を使用してRadioButtonsを設定し、それらがアクティブになったときにsetChecked(true)を設定し、他のボタンに対して反対の操作を行うようにします。例えば:

class FooActivity {

    RadioButton m_one, m_two, m_three;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...
        m_one = (RadioButton) findViewById(R.id.first_radio_button);
        m_two = (RadioButton) findViewById(R.id.second_radio_button);
        m_three = (RadioButton) findViewById(R.id.third_radio_button);

        m_one.setOnClickListener(new OnClickListener() {
            public void onClick(View v) {
                m_one.setChecked(true);
                m_two.setChecked(false);
                m_three.setChecked(false);
            }
        });

        m_two.setOnClickListener(new OnClickListener() {
            public void onClick(View v) {
                m_one.setChecked(false);
                m_two.setChecked(true);
                m_three.setChecked(false);
            }
        });

        m_three.setOnClickListener(new OnClickListener() {
            public void onClick(View v) {
                m_one.setChecked(false);
                m_two.setChecked(false);
                m_three.setChecked(true);
            }
        });

        ...     
    } // onCreate() 

}

ええ、私は知っています-昔ながらの方法です。しかし、それは機能します。がんばろう!

42
Scott Biggs

作成したこのクラスを使用します。階層内のすべてのチェック可能な子が検索されます。

import Java.util.ArrayList;

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

public class MyRadioGroup extends LinearLayout {

private ArrayList<View> mCheckables = new ArrayList<View>();

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

public MyRadioGroup(Context context, AttributeSet attrs) {
    this(context, attrs, 0);
}

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

@Override
public void addView(View child, int index,
        Android.view.ViewGroup.LayoutParams params) {
    super.addView(child, index, params);
    parseChild(child);
}

public void parseChild(final View child)
{
    if(child instanceof Checkable)
    {
        mCheckables.add(child);
        child.setOnClickListener(new OnClickListener() {

            public void onClick(View v) {
                for(int i = 0; i < mCheckables.size();i++)
                {
                    Checkable view = (Checkable) mCheckables.get(i);
                    if(view == v)
                    {
                        ((Checkable)view).setChecked(true);
                    }
                    else
                    {
                        ((Checkable)view).setChecked(false);
                    }
                }
            }
        });
    }
    else if(child instanceof ViewGroup)
    {
        parseChildren((ViewGroup)child);
    }
}

public void parseChildren(final ViewGroup child)
{
    for (int i = 0; i < child.getChildCount();i++)
    {
        parseChild(child.getChildAt(i));
    }
}
}
26
lostdev

さて、私はこの単純なクラスを書きました。

次のように使用します。

// add any number of RadioButton resource IDs here
GRadioGroup gr = new GRadioGroup(this, 
    R.id.radioButton1, R.id.radioButton2, R.id.radioButton3);

または

GRadioGroup gr = new GRadioGroup(rb1, rb2, rb3);
// where RadioButton rb1 = (RadioButton) findViewById(R.id.radioButton1);
// etc.

たとえば、ActivityのonCreate()で呼び出すことができます。どのRadioButtonをクリックしても、他のチェックボックスはオフになります。また、RadioButtonsの一部がRadioGroupの内部にあるかどうかは関係ありません。

クラスは次のとおりです。

package pl.infografnet.GClasses;

import Java.util.ArrayList;
import Java.util.List;

import Android.view.View;
import Android.view.View.OnClickListener;
import Android.view.ViewParent;
import Android.widget.RadioButton;
import Android.widget.RadioGroup;

public class GRadioGroup {

    List<RadioButton> radios = new ArrayList<RadioButton>();

    /**
     * Constructor, which allows you to pass number of RadioButton instances,
     * making a group.
     * 
     * @param radios
     *            One RadioButton or more.
     */
    public GRadioGroup(RadioButton... radios) {
        super();

        for (RadioButton rb : radios) {
            this.radios.add(rb);
            rb.setOnClickListener(onClick);
        }
    }

    /**
     * Constructor, which allows you to pass number of RadioButtons 
     * represented by resource IDs, making a group.
     * 
     * @param activity
     *            Current View (or Activity) to which those RadioButtons 
     *            belong.
     * @param radiosIDs
     *            One RadioButton or more.
     */
    public GRadioGroup(View activity, int... radiosIDs) {
        super();

        for (int radioButtonID : radiosIDs) {
            RadioButton rb = (RadioButton)activity.findViewById(radioButtonID);
            if (rb != null) {
                this.radios.add(rb);
                rb.setOnClickListener(onClick);
            }
        }
    }

    /**
     * This occurs everytime when one of RadioButtons is clicked, 
     * and deselects all others in the group.
     */
    OnClickListener onClick = new OnClickListener() {

        @Override
        public void onClick(View v) {

            // let's deselect all radios in group
            for (RadioButton rb : radios) {

                ViewParent p = rb.getParent();
                if (p.getClass().equals(RadioGroup.class)) {
                    // if RadioButton belongs to RadioGroup, 
                    // then deselect all radios in it 
                    RadioGroup rg = (RadioGroup) p;
                    rg.clearCheck();
                } else {
                    // if RadioButton DOES NOT belong to RadioGroup, 
                    // just deselect it
                    rb.setChecked(false);
                }
            }

            // now let's select currently clicked RadioButton
            if (v.getClass().equals(RadioButton.class)) {
                RadioButton rb = (RadioButton) v;
                rb.setChecked(true);
            }

        }
    };

}
17
infografnet

@ lostdev solutionRadioGroupの実装に基づく私のソリューションです。これは、子レイアウト内にネストされたRadioButtons(または他のCompoundButtons)で動作するように変更されたRadioGroupです。

import Android.content.Context;
import Android.os.Build;
import Android.support.annotation.IdRes;
import Android.support.annotation.Nullable;
import Android.util.AttributeSet;
import Android.view.View;
import Android.view.ViewGroup;
import Android.widget.CompoundButton;
import Android.widget.LinearLayout;
import Android.widget.RadioButton;

import Java.util.concurrent.atomic.AtomicInteger;

/**
 * This class is a replacement for Android RadioGroup - it supports
 * child layouts which standard RadioGroup doesn't.
 */
public class RecursiveRadioGroup extends LinearLayout {

    public interface OnCheckedChangeListener {
        void onCheckedChanged(RecursiveRadioGroup group, @IdRes int checkedId);
    }

    /**
     * For generating unique view IDs on API < 17 with {@link #generateViewId()}.
     */
    private static final AtomicInteger sNextGeneratedId = new AtomicInteger(1);

    private CompoundButton checkedView;

    private CompoundButton.OnCheckedChangeListener childOnCheckedChangeListener;

    /**
     * When this flag is true, onCheckedChangeListener discards events.
     */
    private boolean mProtectFromCheckedChange = false;

    private OnCheckedChangeListener onCheckedChangeListener;

    private PassThroughHierarchyChangeListener mPassThroughListener;

    public RecursiveRadioGroup(Context context) {
        super(context);
        setOrientation(HORIZONTAL);
        init();
    }

    public RecursiveRadioGroup(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public RecursiveRadioGroup(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        childOnCheckedChangeListener = new CheckedStateTracker();
        mPassThroughListener = new PassThroughHierarchyChangeListener();

        super.setOnHierarchyChangeListener(mPassThroughListener);
    }

    @Override
    public void setOnHierarchyChangeListener(OnHierarchyChangeListener listener) {
        mPassThroughListener.mOnHierarchyChangeListener = listener;
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();

        // checks the appropriate radio button as requested in the XML file
        if (checkedView != null) {
            mProtectFromCheckedChange = true;
            setCheckedStateForView(checkedView, true);
            mProtectFromCheckedChange = false;
            setCheckedView(checkedView);
        }
    }

    @Override
    public void addView(View child, int index, ViewGroup.LayoutParams params) {
        parseChild(child);

        super.addView(child, index, params);
    }

    private void parseChild(final View child) {
        if (child instanceof CompoundButton) {
            final CompoundButton checkable = (CompoundButton) child;

            if (checkable.isChecked()) {
                mProtectFromCheckedChange = true;
                if (checkedView != null) {
                    setCheckedStateForView(checkedView, false);
                }
                mProtectFromCheckedChange = false;
                setCheckedView(checkable);
            }
        } else if (child instanceof ViewGroup) {
            parseChildren((ViewGroup) child);
        }
    }

    private void parseChildren(final ViewGroup child) {
        for (int i = 0; i < child.getChildCount(); i++) {
            parseChild(child.getChildAt(i));
        }
    }

    /**
     * <p>Sets the selection to the radio button whose identifier is passed in
     * parameter. Using -1 as the selection identifier clears the selection;
     * such an operation is equivalent to invoking {@link #clearCheck()}.</p>
     *
     * @param view the radio button to select in this group
     * @see #getCheckedItemId()
     * @see #clearCheck()
     */
    public void check(CompoundButton view) {
        if(checkedView != null) {
            setCheckedStateForView(checkedView, false);
        }

        if(view != null) {
            setCheckedStateForView(view, true);
        }

        setCheckedView(view);
    }

    private void setCheckedView(CompoundButton view) {
        checkedView = view;

        if(onCheckedChangeListener != null) {
            onCheckedChangeListener.onCheckedChanged(this, checkedView.getId());
        }
    }

    private void setCheckedStateForView(View checkedView, boolean checked) {
        if (checkedView != null && checkedView instanceof CompoundButton) {
            ((CompoundButton) checkedView).setChecked(checked);
        }
    }

    /**
     * <p>Returns the identifier of the selected radio button in this group.
     * Upon empty selection, the returned value is -1.</p>
     *
     * @return the unique id of the selected radio button in this group
     * @attr ref Android.R.styleable#RadioGroup_checkedButton
     * @see #check(CompoundButton)
     * @see #clearCheck()
     */
    @IdRes
    public int getCheckedItemId() {
        return checkedView.getId();
    }

    public CompoundButton getCheckedItem() {
        return checkedView;
    }

    /**
     * <p>Clears the selection. When the selection is cleared, no radio button
     * in this group is selected and {@link #getCheckedItemId()} returns
     * null.</p>
     *
     * @see #check(CompoundButton)
     * @see #getCheckedItemId()
     */
    public void clearCheck() {
        check(null);
    }

    /**
     * <p>Register a callback to be invoked when the checked radio button
     * changes in this group.</p>
     *
     * @param listener the callback to call on checked state change
     */
    public void setOnCheckedChangeListener(RecursiveRadioGroup.OnCheckedChangeListener listener) {
        onCheckedChangeListener = listener;
    }

    /**
     * Generate a value suitable for use in {@link #setId(int)}.
     * This value will not collide with ID values generated at build time by aapt for R.id.
     *
     * @return a generated ID value
     */
    public static int generateViewId() {
        for (; ; ) {
            final int result = sNextGeneratedId.get();
            // aapt-generated IDs have the high byte nonzero; clamp to the range under that.
            int newValue = result + 1;
            if (newValue > 0x00FFFFFF) newValue = 1; // Roll over to 1, not 0.
            if (sNextGeneratedId.compareAndSet(result, newValue)) {
                return result;
            }
        }
    }

    private class CheckedStateTracker implements CompoundButton.OnCheckedChangeListener {

        @Override
        public void onCheckedChanged(CompoundButton view, boolean b) {
            if (mProtectFromCheckedChange) {
                return;
            }

            mProtectFromCheckedChange = true;
            if (checkedView != null) {
                setCheckedStateForView(checkedView, false);
            }
            mProtectFromCheckedChange = false;

            int id = view.getId();
            setCheckedView(view);
        }
    }

    private class PassThroughHierarchyChangeListener implements OnHierarchyChangeListener {

        private OnHierarchyChangeListener mOnHierarchyChangeListener;

        @Override
        public void onChildViewAdded(View parent, View child) {
            if (child instanceof CompoundButton) {
                int id = child.getId();

                if (id == View.NO_ID) {
                    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
                        child.setId(generateViewId());
                    } else {
                        child.setId(View.generateViewId());
                    }
                }

                ((CompoundButton) child).setOnCheckedChangeListener(childOnCheckedChangeListener);

                if (mOnHierarchyChangeListener != null) {
                    mOnHierarchyChangeListener.onChildViewAdded(parent, child);
                }
            } else if(child instanceof ViewGroup) {
                // View hierarchy seems to be constructed from the bottom up,
                // so all child views are already added. That's why we
                // manually call the listener for all children of ViewGroup.
                for(int i = 0; i < ((ViewGroup) child).getChildCount(); i++) {
                    onChildViewAdded(child, ((ViewGroup) child).getChildAt(i));
                }
            }
        }

        @Override
        public void onChildViewRemoved(View parent, View child) {
            if (child instanceof RadioButton) {
                ((CompoundButton) child).setOnCheckedChangeListener(null);
            }

            if (mOnHierarchyChangeListener != null) {
                mOnHierarchyChangeListener.onChildViewRemoved(parent, child);
            }
        }
    }

}

ネストされたRadioGroupビューでも機能することを除いて、通常のRadioButtonと同じ方法でレイアウトで使用できます。

<RecursiveRadioGroup
    Android:layout_width="match_parent"
    Android:layout_height="wrap_content"
    Android:layout_marginTop="16dp"
    Android:layout_marginBottom="16dp"
    Android:layout_marginLeft="16dp"
    Android:layout_marginRight="16dp"
    Android:orientation="horizontal">

    <LinearLayout
        Android:layout_width="0dp"
        Android:layout_height="wrap_content"
        Android:layout_weight="1"
        Android:orientation="vertical">

        <RadioButton
            Android:id="@+id/rbNotEnoughProfileInfo"
            Android:layout_width="wrap_content"
            Android:layout_height="wrap_content"
            Android:text="Not enough profile information"/>

        <RadioButton
            Android:id="@+id/rbNotAGoodFit"
            Android:layout_width="wrap_content"
            Android:layout_height="wrap_content"
            Android:text="Not a good fit"/>

        <RadioButton
            Android:id="@+id/rbDatesNoLongerAvailable"
            Android:layout_width="wrap_content"
            Android:layout_height="wrap_content"
            Android:text="Dates no longer available"/>

    </LinearLayout>

    <LinearLayout
        Android:layout_width="0dp"
        Android:layout_height="match_parent"
        Android:layout_weight="1"
        Android:orientation="vertical">

        <RadioButton
            Android:id="@+id/rbOther"
            Android:layout_width="match_parent"
            Android:layout_height="wrap_content"
            Android:text="Other"/>

        <Android.support.v7.widget.AppCompatEditText
            Android:id="@+id/etReason"
            Android:layout_width="match_parent"
            Android:layout_height="match_parent"
            Android:layout_below="@+id/tvMessageError"
            Android:textSize="15sp"
            Android:gravity="top|left"
            Android:hint="Tell us more"
            Android:padding="16dp"
            Android:background="@drawable/edit_text_multiline_background"/>
    </LinearLayout>

</RecursiveRadioGroup>
7
Ivan Kušt

ため息.. Androidにそのような基本的な機能がないことを本当に責めます。

@ScottBiggsの回答から改編された、Kotlinでそれを行うためのおそらく最短の方法があります:

var currentSelected = button1
listOf<RadioButton>(
    button1, button2, button3, ...
).forEach {
    it.setOnClickListener { _ ->
        currentSelected.isChecked = false
        currentSelected = it
        currentSelected.isChecked = true
    }
}
4
viz

この問題を解決するために、これら2つのメソッドを作成しました。必要なことは、RadioButtonがあるViewGroup(RadioGroup、LinearLayout、RelativeLayoutなど)を渡すだけで、OnClickイベントを排他的に設定します。つまり、ViewGroupの子であるRadioButtonの1つ(ネストされたレベルで)が選択され、その他は選択解除されます。ネストされたレイアウトをいくつでも使用できます。

public class Utils {
    public static void setRadioExclusiveClick(ViewGroup parent) {
        final List<RadioButton> 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<RadioButton> getRadioButtons(ViewGroup parent) {
        List<RadioButton> radios = new ArrayList<RadioButton>();
        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<RadioButton> nestedRadios = getRadioButtons((ViewGroup) v);
                radios.addAll(nestedRadios);
            }
        }
        return radios;
    }
}

アクティビティ内での使用は次のようになります。

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

ネストされたラジオボタンを含めることができる独自のラジオグループクラスを作成しました。見てみな。バグを見つけたら、私に知らせてください。

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

/**
 * This class is used to create a multiple-exclusion scope for a set of compound
 * buttons. Checking one compound button that belongs to a group unchecks any
 * previously checked compound button within the same group. Intially, all of
 * the compound buttons are unchecked. While it is not possible to uncheck a
 * particular compound button, the group can be cleared to remove the checked
 * state. Basically, this class extends functionality of
 * {@link Android.widget.RadioGroup} because it doesn't require that compound
 * buttons are direct childs of the group. This means you can wrap compound
 * buttons with other views. <br>
 * <br>
 * 
 * <b>IMPORTATNT! Follow these instruction when using this class:</b><br>
 * 1. Each direct child of this group must contain one compound button or be
 * compound button itself.<br>
 * 2. Do not set any "on click" or "on checked changed" listeners for the childs
 * of this group.
 */
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;
 }

 /**
  * Register a callback to be invoked when the checked view changes in this
  * group.
  * 
  * @param listener
  *            the callback to call on checked state change.
  */
 public void setOnCheckedChangeListener(OnCheckedChangeListener listener) {
  this.listener = listener;
 }

 /**
  * Returns currently selected view in this group. Upon empty selection, the
  * returned value is null.
  */
 public View getCheckedView() {
  return this.checkedView;
 }

 /**
  * Returns index of currently selected view in this group. Upon empty
  * selection, the returned value is -1.
  */
 public int getCheckedViewIndex() {
  return (this.checkedView != null) ? indexOfChild(this.checkedView) : -1;
 }

 /**
  * Sets the selection to the view whose index in group is passed in
  * parameter.
  * 
  * @param index
  *            the index of the view to select in this group.
  */
 public void check(int index) {
  check(getChildAt(index));
 }

 /**
  * Clears the selection. When the selection is cleared, no view in this
  * group is selected and {@link #getCheckedView()} returns 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;
 }

 /**
  * Interface definition for a callback to be invoked when the checked view
  * changed in this group.
  */
 public interface OnCheckedChangeListener {

  /**
   * Called when the checked view has changed.
   * 
   * @param checkedView
   *            newly checked view or null if selection was cleared in the
   *            group.
   */
  public void onCheckedChanged(View checkedView);
 }

}
2
Egis

次の2つのことを行う必要があります。

  1. mListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);を使用します
  2. カスタム行ビューにCheckableを実装させます。

だから、より良い解決策は、内側のLinearLayout内にCheckableを実装することだと思います:(彼のリンクから、daichan4649のおかげで、 https://Gist.github.com/daichan4649/5245378 以下に貼り付けたコード)

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

<?xml version="1.0" encoding="utf-8"?>
<daichan4649.test.CheckableLayout
    xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:id="@+id/check_area"
    Android:layout_width="match_parent"
    Android:layout_height="wrap_content"
    Android:layout_gravity="center_vertical">

    <TextView
        Android:id="@+id/text"
        Android:layout_width="0dip"
        Android:layout_height="wrap_content"
        Android:layout_gravity="center_vertical"
        Android:layout_weight="1"
        Android:gravity="center_vertical" />

    <RadioButton
        Android:id="@+id/radio"
        Android:layout_width="wrap_content"
        Android:layout_height="wrap_content"
        Android:clickable="false"
        Android:focusable="false"
        Android:focusableInTouchMode="false" />

</daichan4649.test.CheckableLayout>

TestFragment.Java

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

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

    // 表示データ
    List<String> dataList = new ArrayList<String>();

    // 初期選択位置
    int initSelectedPosition = 3;

    // リスト設定
    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) {
            // 選択状態を要素(checkable)へ反映
            Checkable child = (Checkable) parent.getChildAt(position);
            child.toggle();
        }
    });
    return view;
}

private static class TestAdapter extends ArrayAdapter<String> {

    private LayoutInflater inflater;

    public TestAdapter(Context context, List<String> 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;
}
2
madx

2つの異なるlinearlayoutに4つの異なるラジオボタンを配置するのと同じ問題に直面しており、これらのレイアウトはラジオグループの子になります。 RadioGroupで望みの動作を実現するために、addView関数をオーバーロードしました

ここに解決策があります

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<viewGroup.getChildCount(); i++)
            {
                View subChild = viewGroup.getChildAt(i);
                if( subChild instanceof ViewGroup )
                {
                    onViewAdded(subChild);
                }
                else
                {
                    if (subChild instanceof RadioButton) {
                        super.onViewAdded(subChild);
                    }
                }
            }
        }
        if (child instanceof RadioButton)
        {
            super.onViewAdded(child);
        }
    }
}
1
umerk44

@infografnetと@lostdevに基づく0.02ドル(Compound Buttonの提案に対して@Neromancerにも感謝します!)

public class AdvRadioGroup {
    public interface OnButtonCheckedListener {
        void onButtonChecked(CompoundButton button);
    }

    private final List<CompoundButton> buttons;
    private final View.OnClickListener onClick = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            setChecked((CompoundButton) v);
        }
    };

    private OnButtonCheckedListener listener;
    private CompoundButton lastChecked;


    public AdvRadioGroup(View view) {
        buttons = new ArrayList<>();
        parseView(view);
    }

    private void parseView(final View view) {
        if(view instanceof CompoundButton) {
            buttons.add((CompoundButton) view);
            view.setOnClickListener(onClick);
        } else if(view instanceof ViewGroup) {
            final ViewGroup group = (ViewGroup) view;
            for (int i = 0; i < group.getChildCount();i++) {
                parseView(group.getChildAt(i));
            }
        }
    }

    public List<CompoundButton> getButtons() { return buttons; }

    public CompoundButton getLastChecked() { return lastChecked; }

    public void setChecked(int index) { setChecked(buttons.get(index)); }

    public void setChecked(CompoundButton button) {
        if(button == lastChecked) return;

        for (CompoundButton btn : buttons) {
            btn.setChecked(false);
        }

        button.setChecked(true);

        lastChecked = button;

        if(listener != null) {
            listener.onButtonChecked(button);
        }
    }

    public void setOnButtonCheckedListener(OnButtonCheckedListener listener) { this.listener = listener; }
}

使用法(リスナーを含む):

AdvRadioGroup group = new AdvRadioGroup(findViewById(R.id.YOUR_VIEW));
group.setOnButtonCheckedListener(new AdvRadioGroup.OnButtonCheckedListener() {
    @Override
    public void onButtonChecked(CompoundButton button) {
        // do fun stuff here!
    }
});

ボーナス:最後にチェックしたボタン、ボタン全体のリストを取得できます。これにより、インデックスで任意のボタンをチェックできます!

1
Nick

このソリューションは投稿されていないため、投稿:

ステップ0:CompountButton previousCheckedCompoundButton;をグローバル変数として作成します。

ステップ1:ラジオボタンのOnCheckedChangedListenerを作成する

CompoundButton.OnCheckedChangeListener onRadioButtonCheckedListener = new CompoundButton.OnCheckedChangeListener() {
        @Override
        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
            if (!isChecked) return;
            if (previousCheckedCompoundButton != null) {
                previousCheckedCompoundButton.setChecked(false);
                previousCheckedCompoundButton = buttonView;
            } else {
                previousCheckedCompoundButton = buttonView;
            }
        }
    };

ステップ3:すべてのラジオボタンにリスナーを追加します。

radioButton1.setOnCheckedChangeListener(onRadioButtonCheckedListener);
radioButton2.setOnCheckedChangeListener(onRadioButtonCheckedListener);
radioButton3.setOnCheckedChangeListener(onRadioButtonCheckedListener);
radioButton4.setOnCheckedChangeListener(onRadioButtonCheckedListener);

それでおしまい!!完了しました。

1
Pankaj

そのレイアウト構造の実装を妨げるものはありません(RadioGroupは実際にはLinearLayoutのサブクラスです)、そうすべきではありません。まず、4レベルの深さの構造を作成します(別のレイアウト構造を使用してこれを最適化できます)。次に、RadioButtonsRadioGroupの直接の子でない場合、グループで選択された1つのアイテムは機能しません。つまり、そのレイアウトからRadiobuttonを選択してから別のRadioButtonを選択すると、最後に選択したものではなく、2つのRadioButtonsが選択されることになります。

あなたがそのレイアウトで何をしたいのかを説明するなら、多分私はあなたに代わりを勧めることができます。

1
Luksprog
    int currentCheckedRadioButton = 0;
    int[] myRadioButtons= new int[6];
    myRadioButtons[0] = R.id.first;
    myRadioButtons[1] = R.id.second;
    //..
    for (int radioButtonID : myRadioButtons) {
        findViewById(radioButtonID).setOnClickListener(
                    new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (currentCheckedRadioButton != 0)
                    ((RadioButton) findViewById(currentCheckedRadioButton)).setChecked(false);
                currentCheckedRadioButton = v.getId();

            }
        });
    }
0
med.Hamdan

これは@Infografnetのソリューションの修正バージョンです。シンプルで使いやすいです。

RadioGroupHelper group = new RadioGroupHelper(this,R.id.radioButton1,R.id.radioButton2); group.radioButtons.get(0).performClick(); //programmatically

コピーして貼り付けるだけ

package com.qamar4p.farmer.ui.custom;

import Java.util.ArrayList;
import Java.util.List;

import Android.app.Activity;
import Android.view.View;
import Android.widget.CompoundButton;
import Android.widget.RadioButton;

public class RadioGroupHelper {

    public List<CompoundButton> radioButtons = new ArrayList<>();

    public RadioGroupHelper(RadioButton... radios) {
        super();
        for (RadioButton rb : radios) {
            add(rb);
        }
    }

    public RadioGroupHelper(Activity activity, int... radiosIDs) {
        this(activity.findViewById(Android.R.id.content),radiosIDs);
    }

    public RadioGroupHelper(View rootView, int... radiosIDs) {
        super();
        for (int radioButtonID : radiosIDs) {
            add((RadioButton)rootView.findViewById(radioButtonID));
        }
    }

    private void add(CompoundButton button){
        this.radioButtons.add(button);
        button.setOnClickListener(onClickListener);
    }

    View.OnClickListener onClickListener = v -> {
        for (CompoundButton rb : radioButtons) {
            if(rb != v) rb.setChecked(false);
        }
    };
}
0
Qamar4P

これは古いトピックかもしれませんが、私が書いた簡単なハックコードをすぐに共有したいと思います。

このコードを使用する状況??
このコードは、元の質問などのレイアウトを持っている人向けです。私の場合は以下のとおりです。これは個人的に私が使用していたダイアログ用でした。

  • LinLayout_Main
    • LinLayout_Row1
      • ImageView
      • ラジオボタン
    • LinLayout_Row2
      • ImageView
      • ラジオボタン
    • LinLayout_Row3
      • ImageView
      • ラジオボタン

コードはそれ自体で何をしますか?

単純に親の「LinLayout_Main」を見て、子のLinearLayoutsにあるRadioButtonを見つけます。

MyMethod_ShowDialog
XMLレイアウトファイルを含むダイアログを表示し、見つかったRadioButtonごとに「setOnClickListener」を設定するように表示します

MyMethod_ClickRadio
「MyMethod_ShowDialog」と同じ方法で各RadioButtonをループしますが、「setOnClickListener」を設定する代わりに「setChecked(false)」を使用して各RadioButtonをクリアし、最後のステップで「setChecked(false)」を実行しますクリックイベントを呼び出したRadioButtonに。

public void MyMethod_ShowDialog(final double tmpLat, final double tmpLng) {
        final Dialog dialog = new Dialog(actMain);
        dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
        dialog.setContentView(R.layout.layout_dialogXML);

        final LinearLayout tmpLayMain = (LinearLayout)dialog.findViewById(R.id.LinLayout_Main);
        if (tmpLayMain!=null) {
            // Perform look for each child of main LinearLayout
            int iChildCount1 = tmpLayMain.getChildCount();
            for (int iLoop1=0; iLoop1 < iChildCount1; iLoop1++){
                View tmpChild1 = tmpLayMain.getChildAt(iLoop1);
                if (tmpChild1 instanceof LinearLayout) {
                    // Perform look for each LinearLayout child of main LinearLayout
                    int iChildCount2 = ((LinearLayout) tmpChild1).getChildCount();
                    for (int iLoop2=0; iLoop2 < iChildCount2; iLoop2++){
                        View tmpChild2 = ((LinearLayout) tmpChild1).getChildAt(iLoop2);
                        if (tmpChild2 instanceof RadioButton) {
                            ((RadioButton) tmpChild2).setOnClickListener(new RadioButton.OnClickListener() {
                                public void onClick(View v) {
                                    MyMethod_ClickRadio(v, dialog);
                                }
                            });
                        }
                    }
                }
            }

            Button dialogButton = (Button)dialog.findViewById(R.id.LinLayout_Save);
            dialogButton.setOnClickListener(new Button.OnClickListener() {
                public void onClick(View v) {
                    dialog.dismiss();
                }
            });
        }
       dialog.show();
}


public void MyMethod_ClickRadio(View vRadio, final Dialog dDialog) {

        final LinearLayout tmpLayMain = (LinearLayout)dDialog.findViewById(R.id.LinLayout_Main);
        if (tmpLayMain!=null) {
            int iChildCount1 = tmpLayMain.getChildCount();
            for (int iLoop1=0; iLoop1 < iChildCount1; iLoop1++){
                View tmpChild1 = tmpLayMain.getChildAt(iLoop1);
                if (tmpChild1 instanceof LinearLayout) {
                    int iChildCount2 = ((LinearLayout) tmpChild1).getChildCount();
                    for (int iLoop2=0; iLoop2 < iChildCount2; iLoop2++){
                        View tmpChild2 = ((LinearLayout) tmpChild1).getChildAt(iLoop2);
                        if (tmpChild2 instanceof RadioButton) {
                            ((RadioButton) tmpChild2).setChecked(false);
                        }
                    }
                }
            }
        }

        ((RadioButton) vRadio).setChecked(true);
}

バグがある可能性があります。プロジェクトからコピーし、名前をVoids/XML/IDに変更しました

また、同じタイプのループを実行して、チェックされているアイテムを見つけることもできます

0
Angry 84

私は同じ問題に陥り、性別にはラジオボタンを使用する必要があり、すべて写真とテキストでしたので、次の方法で解決しようとしました。

xmlファイル:

<RadioGroup
       Android:layout_marginTop="40dp"
       Android:layout_marginEnd="23dp"
       Android:id="@+id/rgGender"
       Android:layout_width="match_parent"
       Android:layout_below="@id/tvCustomer"
       Android:orientation="horizontal"
       Android:layout_height="wrap_content">

       <LinearLayout
           Android:layout_width="wrap_content"
           Android:layout_height="wrap_content"
           Android:orientation="vertical"
           Android:gravity="center_horizontal"
           Android:layout_weight="1">
       <RadioButton
           Android:id="@+id/rbMale"
           Android:layout_width="80dp"
           Android:layout_height="60dp"
           Android:background="@drawable/male_radio_btn_selector"
           Android:button="@null"
           style="@style/RadioButton.Roboto.20sp"/>

           <TextView
               Android:layout_width="wrap_content"
               Android:layout_height="wrap_content"
               Android:text="Male"
               style="@style/TextView.RobotoLight.TxtGrey.18sp"
               Android:layout_margin="0dp"
               Android:textSize="@dimen/txtsize_20sp"/>
       </LinearLayout>
       <LinearLayout
           Android:layout_width="wrap_content"
           Android:layout_height="wrap_content"
           Android:orientation="vertical"
           Android:gravity="center_horizontal"
           Android:layout_weight="1">
       <RadioButton
           Android:layout_weight="1"
           Android:gravity="center"
           Android:id="@+id/rbFemale"
           Android:layout_width="80dp"
           Android:layout_height="60dp"
           Android:button="@null"
           Android:background="@drawable/female_radio_btn_selector"
           style="@style/RadioButton.Roboto.20sp"
           Android:textColor="@color/light_grey"/>
           <TextView
               Android:layout_width="wrap_content"
               Android:layout_height="wrap_content"
               Android:text="Female"
               Android:layout_margin="0dp"
               style="@style/TextView.RobotoLight.TxtGrey.18sp"
               Android:textSize="@dimen/txtsize_20sp"/>
       </LinearLayout>
       <LinearLayout
           Android:layout_width="wrap_content"
           Android:layout_height="wrap_content"
           Android:orientation="vertical"
           Android:gravity="center_horizontal"
           Android:layout_weight="1">
       <RadioButton
           Android:layout_weight="1"
           Android:gravity="center"
           Android:id="@+id/rbOthers"
           Android:layout_width="80dp"
           Android:layout_height="60dp"
           Android:button="@null"
           Android:background="@drawable/other_gender_radio_btn_selector"
           style="@style/RadioButton.Roboto.20sp"/>
          <TextView
              Android:layout_width="wrap_content"
              Android:layout_height="wrap_content"
              Android:text="Other"
              Android:layout_margin="0dp"
              style="@style/TextView.RobotoLight.TxtGrey.18sp"
              Android:textSize="@dimen/txtsize_20sp"/>
      </LinearLayout>
   </RadioGroup>

In Java file:3つのラジオボタンすべてにsetOnCheckedChangeListenerを設定し、以下で説明するようにメソッドをオーバーライドします。

@Override
    public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
   switch (compoundButton.getId()){
       case R.id.rbMale:
           if(rbMale.isChecked()){
               rbMale.setChecked(true);
               rbFemale.setChecked(false);
               rbOther.setChecked(false);
           }
           break;
       case R.id.rbFemale:
           if(rbFemale.isChecked()){
               rbMale.setChecked(false);
               rbFemale.setChecked(true);
               rbOther.setChecked(false);
           }
           break;
       case R.id.rbOthers:
           if(rbOther.isChecked()){
               rbMale.setChecked(false);
               rbFemale.setChecked(false);
               rbOther.setChecked(true);
           }
           break;

   }
    }
0
Anupriya

これは、RadioButtonを使用したカスタムレイアウト用のKotlinでの私のソリューションです。

tipInfoContainerFirst.radioButton.isChecked = true

var prevSelected = tipInfoContainerFirst.radioButton
prevSelected.isSelected = true

listOf<RadioButton>(
    tipInfoContainerFirst.radioButton,
    tipInfoContainerSecond.radioButton,
    tipInfoContainerThird.radioButton,
    tipInfoContainerForth.radioButton,
    tipInfoContainerCustom.radioButton
).forEach {
    it.setOnClickListener { _it ->
    if(!it.isSelected) {
        prevSelected.isChecked = false
        prevSelected.isSelected = false
        it.radioButton.isSelected = true
        prevSelected = it.radioButton
    }
  }
}
0
Edgar Khimich

回答に示されているように、ソリューションは単純なカスタムハックです。 Kotlinの私のミニマルなバージョンは次のとおりです。

import Android.widget.RadioButton

class SimpleRadioGroup(private val radioButtons: List<RadioButton>) {

    init {
        radioButtons.forEach {
            it.setOnClickListener { clickedButton ->
                radioButtons.forEach { it.isChecked = false }
                (clickedButton as RadioButton).isChecked = true
            }
        }
    }

    val checkedButton: RadioButton?
        get() = radioButtons.firstOrNull { it.isChecked }
}

次に、アクティビティのonCreateまたはフラグメントのonViewCreatedでそのようなことを行う必要があります。

SimpleRadioGroup(listOf(radio_button_1, radio_button_2, radio_button_3))
0
Achraf Amil