【问题标题】:Radio buttons looses values when scrolling in RecyclerView在 RecyclerView 中滚动时,单选按钮会丢失值
【发布时间】:2019-04-16 21:18:15
【问题描述】:

请帮我找出错误!我已经阅读了很多关于这个问题的帖子,但我无法让我的工作。对我来说,看起来我是根据其他解决方案来做的,但我错过了一些东西。

我使用 recyclerview 将一项调查作为一个问题实施,其中有 3 个可能的答案,其中每一行都有一个带有 3 个单选按钮的单选组。

当滚动单选按钮时,它的值会丢失,并且不会在 onbind 事件中再次设置。

public class MyQItem
{
    public int Id;
    public string Question;
    public int Value;

    public MyQItem(int id, string question)
    {
        Id = id;
        Question = question;
    }
}

internal class MyQFormAdapter : RecyclerView.Adapter
{
    private MyQItem[] mDataSource;

    public MyQFormAdapter(MyQItem[] mDataSource)
    {
        this.mDataSource = mDataSource;
    }

    public override int ItemCount => mDataSource.Length;

    public override void OnBindViewHolder(RecyclerView.ViewHolder holder, int position)
    {
        MyQViewHolder vh = holder as MyQViewHolder;

        vh.QuestionId.Text = mDataSource[position].Id.ToString();
        vh.Question.Text = mDataSource[position].Question;

        vh.Button1.CheckedChange -= Button1_CheckedChange;
        vh.Button2.CheckedChange -= Button2_CheckedChange;
        vh.Button3.CheckedChange -= Button3_CheckedChange;

        vh.Button1.Checked = mDataSource[position].Value == 1;
        vh.Button2.Checked = mDataSource[position].Value == 2;
        vh.Button3.Checked = mDataSource[position].Value == 3;

        vh.Button1.Tag = position;
        vh.Button2.Tag = position;
        vh.Button3.Tag = position;

        vh.Button1.CheckedChange += Button1_CheckedChange;
        vh.Button2.CheckedChange += Button2_CheckedChange;
        vh.Button3.CheckedChange += Button3_CheckedChange;
    }

    void Button1_CheckedChange(object sender, CompoundButton.CheckedChangeEventArgs e) => SetChecked((RadioButton)sender, 1, e.IsChecked);
    void Button2_CheckedChange(object sender, CompoundButton.CheckedChangeEventArgs e) => SetChecked((RadioButton)sender, 2, e.IsChecked);
    void Button3_CheckedChange(object sender, CompoundButton.CheckedChangeEventArgs e) => SetChecked((RadioButton)sender, 3, e.IsChecked);

    void SetChecked(RadioButton btn, int value, bool isChecked)
    {
        if (!isChecked)
            return;

        var position = (int)btn.Tag;
        mDataSource[position].Value = value;
    }

    public override RecyclerView.ViewHolder OnCreateViewHolder(ViewGroup parent, int viewType)
    {
        View itemView = LayoutInflater.From(parent.Context).Inflate(Resource.Layout.MyQItemLayout, parent, false);

        MyQViewHolder vh = new MyQViewHolder(itemView);
        return vh;
    }
}

[Activity(Label = "MyQEditActivity")]
public class MyQEditActivity : Activity
{
    MyQItem[] mDataSource = new MyQItem[] { 
        new MyQItem(1, "Lorem Ipsum"),
        new MyQItem(2, "Lorem Ipsum"),
        new MyQItem(3, "Lorem Ipsum"),
        new MyQItem(4, "Lorem Ipsum"),
        new MyQItem(5, "Lorem Ipsum"),
        new MyQItem(6, "Lorem Ipsum"),
        new MyQItem(7, "Lorem Ipsum"),
        new MyQItem(8, "Lorem Ipsum"),
        new MyQItem(9, "Lorem Ipsum"),
        new MyQItem(10, "Lorem Ipsum"),
        new MyQItem(11, "Lorem Ipsum"),
        new MyQItem(12, "Lorem Ipsum"),
    };

    protected override void OnCreate(Bundle savedInstanceState)
    {
        base.OnCreate(savedInstanceState);

        SetContentView(Resource.Layout.MyQEditLayout);

        var recyclerView = FindViewById<RecyclerView>(Resource.Id.recyclerView);
        recyclerView.SetLayoutManager(new LinearLayoutManager(this));
        recyclerView.SetAdapter(new MyQFormAdapter(mDataSource));
    }

}

public class MyQViewHolder : RecyclerView.ViewHolder
{
    public TextView QuestionId { get; private set; }
    public TextView Question { get; private set; }
    public RadioButton Button1 { get; private set; }
    public RadioButton Button2 { get; private set; }
    public RadioButton Button3 { get; private set; }

    public MyQViewHolder(View itemView) : base(itemView)
    {
        QuestionId = itemView.FindViewById<TextView>(Resource.Id.MyQuestionId);
        Question = itemView.FindViewById<TextView>(Resource.Id.MyQuestion);

        Button1 = itemView.FindViewById<RadioButton>(Resource.Id.noButton);
        Button2 = itemView.FindViewById<RadioButton>(Resource.Id.someButton);
        Button3 = itemView.FindViewById<RadioButton>(Resource.Id.yesButton);
    }
}

【问题讨论】:

  • 代码看起来不错....所以你说单选按钮没有被设置,但QuestionIdQuestion的文本开始在OnBindViewHolder中设置?
  • 文本设置良好,加载时列表正确显示。当我检查第一个单选按钮然后滚动以触发 onbind 事件以生成新行并且我再次滚动时,即使我可以在调试时再次遵循已设置(检查)的位置,单选按钮也会被清除。单选按钮也不能在用户界面中“再次检查”?只有先点击Button2或Button3,才能再次选择Button1?!所以感觉就像按钮被选中但 UI 没有显示它?! UI 中的按钮“反应”为橙色点击颜色,但未设置“点”。
  • 似乎是 UI 刷新/显示问题,而不是所示代码中的问题。您正在测试什么 API 级别?模拟器或设备?您是否尝试过其他设备?
  • 仅供参考:我将您的代码复制到一个空白项目中并添加了两个布局,您的代码很好......
  • vh.Button1.CheckedChange += Button1_CheckedChange;在 OnBindViewHolder 中可能会出现问题,因为每次项目变得可见时它们都会叠加

标签: android xamarin android-recyclerview android-viewholder android-radiogroup


【解决方案1】:

您可以将侦听器添加到您的 RadioGroup 并像这样修改您的适配器:

class MyQFormAdapter : RecyclerView.Adapter, RadioGroup.IOnCheckedChangeListener
{
    private MyQItem[] mDataSource;
    public MyQFormAdapter(MyQItem[] mDataSource)
     {
      this.mDataSource = mDataSource;
     }


    public override int ItemCount => mDataSource.Length;

    public override void OnBindViewHolder(RecyclerView.ViewHolder holder, int position)
    {
        MyQViewHolder vh = holder as MyQViewHolder;

       // cancel the listener to prevent RadioGroup from displaying confusing key codes
        vh.Group.SetOnCheckedChangeListener(null);
        vh.Group.ClearCheck();
        vh.Group.Tag = position;

        switch (mDataSource[position].Value)
        {
            case 1:
                vh.Group.Check(Resource.Id.noButton);
                break;
            case 2:
                vh.Group.Check(Resource.Id.someButton);
                break;
            case 3:
                vh.Group.Check(Resource.Id.yesButton);
                break;

        }
        vh.QuestionId.Text = mDataSource[position].Id.ToString();
        vh.Question.Text = mDataSource[position].Question;
        vh.Group.SetOnCheckedChangeListener(this);
    }
    public override RecyclerView.ViewHolder OnCreateViewHolder(ViewGroup parent, int viewType)
    {

        View itemView = LayoutInflater.From(parent.Context).Inflate(Resource.Layout.MyQItemLayout, parent,false);

        MyQViewHolder vh = new MyQViewHolder(itemView);
        return vh;
    }

    public class MyQViewHolder : RecyclerView.ViewHolder
    {
        public TextView QuestionId { get; private set; }
        public TextView Question { get; private set; }

        public RadioGroup Group { get; private set; }
        public MyQViewHolder(View itemView) : base(itemView)
        {
            QuestionId = itemView.FindViewById<TextView>(Resource.Id.MyQuestionId);
            Question = itemView.FindViewById<TextView>(Resource.Id.MyQuestion);
            Group = itemView.FindViewById<RadioGroup>(Resource.Id.radiogroup);
        }
    }

    public void OnCheckedChanged(RadioGroup @group, int checkedId)
    {
        switch (checkedId)
        {
            case Resource.Id.noButton:
                mDataSource[(int) @group.Tag].Value = 1;
                break;
            case Resource.Id.someButton:
                mDataSource[(int)@group.Tag].Value = 2;
                break;
            case Resource.Id.yesButton:
                mDataSource[(int)@group.Tag].Value = 3;
                break;
        }
    }
}

【讨论】:

  • 这个解决方案对我有用,看起来更干净,是我将使用的,但我不清楚最初的帖子有什么问题? @Joachim Haglund 建议与 CheckedChanged 事件的连接有关吗?
猜你喜欢
  • 1970-01-01
  • 2022-12-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-06-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多