【发布时间】:2020-03-19 20:09:33
【问题描述】:
我需要将单选选项显示为网格,并发现这个 gist 提供了一个 RadioGridGroup 类,我可以使用它来代替 Android 中提供的 RadioGroup。 (https://gist.github.com/saiaspire/a73135cfee1110a64cb0ab3451b6ca33)
我已将所有内容转换为下面的代码,只是想问一些问题。
(1) 我刚刚删除了所有“final”关键字可以吗?即int result = sNextGeneratedId.Get();
(2) 将parent == RadioGridGroup.this && child instanceof AppCompatRadioButton 转换为parent is RadioGridGroup && child is AppCompatRadioButton 是否准确?
(3) 在扩展/实现侦听器的 CheckedStateTracker 中。它可以访问 RadioGridGroup 字段,例如 Java 示例中的 mProtectFromCheckedChange,但在 C# 示例中,这些变量在那里无法访问。我该如何解决?
(4) 对于侦听器,我也让他们实现了 Java.Lang.Object,因此我不需要实现 Dispose 端 CheckedStateTracker implements CompoundButton.OnCheckedChangeListener 变为 CheckedStateTracker : Java.Lang.Object, CompoundButton.IOnCheckedChangeListener
using System;
using Android.Content;
using Android.Support.V7;
using Android.Support.V7.Widget;
using Android.Text;
using Android.Util;
using Android.Views;
using Android.Views.Accessibility;
using Android.Widget;
using Java.Util.Concurrent.Atomic;
/**
* https://stackoverflow.com/questions/60764344/how-to-convert-listener-from-java-to-c-sharp
*
* <p>This class is used to create a multiple-exclusion scope for a set of radio
* buttons. Checking one radio button that belongs to a radio group unchecks
* any previously checked radio button within the same group.</p>
* <p/>
* <p>Intially, all of the radio buttons are unchecked. While it is not possible
* to uncheck a particular radio button, the radio group can be cleared to
* remove the checked state.</p>
* <p/>
* <p>The selection is identified by the unique id of the radio button as defined
* in the XML layout file.</p>
* <p/>
* <p>See
* {@link android.widget.GridLayout.LayoutParams GridLayout.LayoutParams}
* for layout attributes.</p>
*
* @see AppCompatRadioButton
*/
public class RadioGridGroup: Android.Support.V7.Widget.GridLayout
{
private static AtomicInteger sNextGeneratedId = new AtomicInteger(1);
private int mCheckedId = -1;
private CompoundButton.IOnCheckedChangeListener mChildOnCheckedChangeListener;
private bool mProtectFromCheckedChange = false;
private OnCheckedChangeListener mOnCheckedChangeListener;
private PassThroughHierarchyChangeListener mPassThroughListener;
public RadioGridGroup(Context context) : base(context)
{
init();
}
public RadioGridGroup(Context context, IAttributeSet attrs): base(context, attrs)
{
init();
}
private void init()
{
mChildOnCheckedChangeListener = new CheckedStateTracker();
mPassThroughListener = new PassThroughHierarchyChangeListener();
base.SetOnHierarchyChangeListener(mPassThroughListener);
}
public override void SetOnHierarchyChangeListener(IOnHierarchyChangeListener listener)
{
mPassThroughListener.mOnHierarchyChangeListener = listener;
}
protected override void OnFinishInflate()
{
base.OnFinishInflate();
if (mCheckedId != -1)
{
mProtectFromCheckedChange = true;
SetCheckedStateForView(mCheckedId, true);
mProtectFromCheckedChange = false;
setCheckedId(mCheckedId);
}
}
public override void AddView(View child, int index, ViewGroup.LayoutParams prs)
{
if (child is AppCompatRadioButton) {
AppCompatRadioButton button = (AppCompatRadioButton)child;
if (button.Checked)
{
mProtectFromCheckedChange = true;
if (mCheckedId != -1)
{
SetCheckedStateForView(mCheckedId, false);
}
mProtectFromCheckedChange = false;
setCheckedId(button.Id);
}
}
base.AddView(child, index, prs);
}
public void check(int id)
{
if (id != -1 && (id == mCheckedId))
{
return;
}
if (mCheckedId != -1)
{
SetCheckedStateForView(mCheckedId, false);
}
if (id != -1)
{
SetCheckedStateForView(id, true);
}
setCheckedId(id);
}
private void setCheckedId(int id)
{
mCheckedId = id;
if (mOnCheckedChangeListener != null)
{
mOnCheckedChangeListener.onCheckedChanged(this, mCheckedId);
}
}
private void SetCheckedStateForView(int viewId, bool chkd)
{
View checkedView = FindViewById(viewId);
if (checkedView != null && checkedView is AppCompatRadioButton) {
((AppCompatRadioButton)checkedView).Checked = (chkd);
}
}
public int GetCheckedCheckableImageButtonId()
{
return mCheckedId;
}
public void clearCheck()
{
check(-1);
}
public void SetOnCheckedChangeListener(OnCheckedChangeListener listener)
{
mOnCheckedChangeListener = listener;
}
public override void OnInitializeAccessibilityEvent(AccessibilityEvent ev)
{
base.OnInitializeAccessibilityEvent(ev);
ev.ClassName = (this.GetType().Name);
}
public override void OnInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info)
{
base.OnInitializeAccessibilityNodeInfo(info);
info.ClassName = (this.GetType().Name);
}
public interface OnCheckedChangeListener
{
void onCheckedChanged(RadioGridGroup group, int checkedId);
}
internal class CheckedStateTracker : Java.Lang.Object, CompoundButton.IOnCheckedChangeListener
{
public void OnCheckedChanged(CompoundButton buttonView, bool isChecked)
{
if (mProtectFromCheckedChange)
{
return;
}
mProtectFromCheckedChange = true;
if (mCheckedId != -1)
{
SetCheckedStateForView(mCheckedId, false);
}
mProtectFromCheckedChange = false;
int id = buttonView.Id;
SetCheckId(id);
}
}
internal class PassThroughHierarchyChangeListener : Java.Lang.Object
ViewGroup.IOnHierarchyChangeListener
{
internal ViewGroup.IOnHierarchyChangeListener mOnHierarchyChangeListener;
public void OnChildViewAdded(View parent, View child)
{
if (parent is RadioGridGroup && child is AppCompatRadioButton) {
int id = child.Id;
// generates an id if it's missing
if (id == View.NoId)
{
id = View.GenerateViewId();
child.Id = (id);
}
((AppCompatRadioButton)child).SetOnCheckedChangeListener(
mChildOnCheckedChangeListener);
}
if (mOnHierarchyChangeListener != null)
{
mOnHierarchyChangeListener.OnChildViewAdded(parent, child);
}
}
public void OnChildViewRemoved(View parent, View child)
{
if (parent is RadioGridGroup && child is AppCompatRadioButton) {
((AppCompatRadioButton)child).SetOnCheckedChangeListener(null);
}
if (mOnHierarchyChangeListener != null)
{
mOnHierarchyChangeListener.OnChildViewRemoved(parent, child);
}
}
}
public static int GenerateViewId()
{
for (; ; )
{
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;
}
}
}
}
更新:
下面是我的代码,其中包括将封闭的父级传递给侦听器,并使用它们来比较父级以及访问父级的字段。我还将只读添加到它没有出错的一个位置。
using System;
using Android.Content;
using Android.Support.V7;
using Android.Support.V7.Widget;
using Android.Text;
using Android.Util;
using Android.Views;
using Android.Views.Accessibility;
using Android.Widget;
using Java.Util.Concurrent.Atomic;
/**
* https://stackoverflow.com/questions/60764344/how-to-convert-listener-from-java-to-c-sharp
*
* <p>This class is used to create a multiple-exclusion scope for a set of radio
* buttons. Checking one radio button that belongs to a radio group unchecks
* any previously checked radio button within the same group.</p>
* <p/>
* <p>Intially, all of the radio buttons are unchecked. While it is not possible
* to uncheck a particular radio button, the radio group can be cleared to
* remove the checked state.</p>
* <p/>
* <p>The selection is identified by the unique id of the radio button as defined
* in the XML layout file.</p>
* <p/>
* <p>See
* {@link android.widget.GridLayout.LayoutParams GridLayout.LayoutParams}
* for layout attributes.</p>
*
* @see AppCompatRadioButton
*/
public class RadioGridGroup : Android.Support.V7.Widget.GridLayout
{
private static readonly AtomicInteger sNextGeneratedId = new AtomicInteger(1);
private int mCheckedId = -1;
private CompoundButton.IOnCheckedChangeListener mChildOnCheckedChangeListener;
private bool mProtectFromCheckedChange = false;
private OnCheckedChangeListener mOnCheckedChangeListener;
private PassThroughHierarchyChangeListener mPassThroughListener;
public RadioGridGroup(Context context) : base(context)
{
Init();
}
public RadioGridGroup(Context context, IAttributeSet attrs): base(context, attrs)
{
Init();
}
private void Init()
{
mChildOnCheckedChangeListener = new CheckedStateTracker(this);
mPassThroughListener = new PassThroughHierarchyChangeListener(this);
base.SetOnHierarchyChangeListener(mPassThroughListener);
}
public override void SetOnHierarchyChangeListener(IOnHierarchyChangeListener listener)
{
mPassThroughListener.mOnHierarchyChangeListener = listener;
}
protected override void OnFinishInflate()
{
base.OnFinishInflate();
if (mCheckedId != -1)
{
mProtectFromCheckedChange = true;
SetCheckedStateForView(mCheckedId, true);
mProtectFromCheckedChange = false;
SetCheckedId(mCheckedId);
}
}
public override void AddView(View child, int index, ViewGroup.LayoutParams prs)
{
if (child is AppCompatRadioButton) {
AppCompatRadioButton button = (AppCompatRadioButton)child;
if (button.Checked)
{
mProtectFromCheckedChange = true;
if (mCheckedId != -1)
{
SetCheckedStateForView(mCheckedId, false);
}
mProtectFromCheckedChange = false;
SetCheckedId(button.Id);
}
}
base.AddView(child, index, prs);
}
public void check(int id)
{
if (id != -1 && (id == mCheckedId))
{
return;
}
if (mCheckedId != -1)
{
SetCheckedStateForView(mCheckedId, false);
}
if (id != -1)
{
SetCheckedStateForView(id, true);
}
SetCheckedId(id);
}
private void SetCheckedId(int id)
{
mCheckedId = id;
if (mOnCheckedChangeListener != null)
{
mOnCheckedChangeListener.onCheckedChanged(this, mCheckedId);
}
}
private void SetCheckedStateForView(int viewId, bool chkd)
{
View checkedView = FindViewById(viewId);
if (checkedView != null && checkedView is AppCompatRadioButton) {
((AppCompatRadioButton)checkedView).Checked = (chkd);
}
}
public int GetCheckedCheckableImageButtonId()
{
return mCheckedId;
}
public void clearCheck()
{
check(-1);
}
public void SetOnCheckedChangeListener(OnCheckedChangeListener listener)
{
mOnCheckedChangeListener = listener;
}
public override void OnInitializeAccessibilityEvent(AccessibilityEvent ev)
{
base.OnInitializeAccessibilityEvent(ev);
ev.ClassName = (this.GetType().Name);
}
public override void OnInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info)
{
base.OnInitializeAccessibilityNodeInfo(info);
info.ClassName = (this.GetType().Name);
}
public interface OnCheckedChangeListener
{
void onCheckedChanged(RadioGridGroup group, int checkedId);
}
private class CheckedStateTracker : Java.Lang.Object, CompoundButton.IOnCheckedChangeListener
{
readonly RadioGridGroup enclosingClass;
public CheckedStateTracker(RadioGridGroup enclosing)
{
enclosingClass = enclosing;
}
public void OnCheckedChanged(CompoundButton buttonView, bool isChecked)
{
if (enclosingClass.mProtectFromCheckedChange)
{
return;
}
enclosingClass.mProtectFromCheckedChange = true;
if (enclosingClass.mCheckedId != -1)
{
enclosingClass.SetCheckedStateForView(enclosingClass.mCheckedId, false);
}
enclosingClass.mProtectFromCheckedChange = false;
int id = buttonView.Id;
enclosingClass.SetCheckedId(id);
}
}
private class PassThroughHierarchyChangeListener : Java.Lang.Object,
ViewGroup.IOnHierarchyChangeListener
{
internal ViewGroup.IOnHierarchyChangeListener mOnHierarchyChangeListener;
readonly RadioGridGroup enclosingClass;
public PassThroughHierarchyChangeListener(RadioGridGroup enclosing)
{
enclosingClass = enclosing;
}
public void OnChildViewAdded(View parent, View child)
{
if (parent == enclosingClass && child is AppCompatRadioButton) {
int id = child.Id;
// generates an id if it's missing
if (id == View.NoId)
{
id = View.GenerateViewId();
child.Id = (id);
}
((AppCompatRadioButton)child).SetOnCheckedChangeListener(
enclosingClass.mChildOnCheckedChangeListener);
}
if (mOnHierarchyChangeListener != null)
{
mOnHierarchyChangeListener.OnChildViewAdded(parent, child);
}
}
public void OnChildViewRemoved(View parent, View child)
{
if (parent == enclosingClass && child is AppCompatRadioButton) {
((AppCompatRadioButton)child).SetOnCheckedChangeListener(null);
}
if (mOnHierarchyChangeListener != null)
{
mOnHierarchyChangeListener.OnChildViewRemoved(parent, child);
}
}
}
public static int GenerateViewId()
{
for (; ; )
{
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;
}
}
}
}
【问题讨论】: