滚动时在回收器视图中保存 SwitchCompat 状态

问题描述 投票:0回答:3

我有一个

recyclerview
,其项目包含
textView
switchCompat
。在同一个活动中,我还有一个
textView
,其中有一个数值。任务是当
switchCompat
打开包含数值的
recyclerview
上方的文本视图时,应增加
recyclerview
textview
中的值。我已经这样做了,但是当在
recyclerview
中滚动时,
switchCompat
返回到默认状态,并且数字
textview
的值返回到其旧值,

有什么帮助吗?

我很抱歉现在无法发布部分代码,我会尽快发布,我现在才发布它,以防有人以前经历过类似的事情

谢谢你

android android-recyclerview switchcompat
3个回答
0
投票

Android 中回收器视图或任何适配器视图的关键是让适配器使您的模型适应视图。在您的情况下,您的视图是

TextView
Switch
,因此您的适配器必须使某些模型适应此视图。在这种情况下,我会选择一个像这样的简单模型:

class ItemModel {
  String text;
  boolean on;
}

为了简单起见,我省略了 getter 和 setter

此模型包含一个字符串

text
,它反映文本视图中的文本,以及一个布尔值
on
,它反映开关的状态。当 true 时,开关被选中,当 false 时,开关被取消选中。

有很多方法可以表示这个模型。我选择了这个,你可以选择另一个。关键是,您需要将状态保存在某个地方,这就是我所说的模型 - 视图模型。

现在让我们构建一个适配器,它可以做两件事 - 单击开关时更新模型并告诉活动开关更改了状态。这是执行此操作的一种方法:

public class ItemsAdapter extends 
  RecyclerView.Adapter<ItemsAdapter.ViewHolder> {
    @NonNull
    private final List<ItemModel> itemModels;
    @Nullable
    private OnItemCheckedChangeListener onItemCheckedChangeListener;

    ItemsAdapter(@NonNull List<ItemModel> itemModels) {
        this.itemModels = itemModels;
    }

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        return new ViewHolder(LayoutInflater.from(parent.getContext())
                .inflate(R.layout.item, parent, false));
    }

    @Override
    public void onBindViewHolder(@NonNull final ViewHolder holder, int position) {
        ItemModel item = itemModels.get(position);
        holder.text.setText(item.text);
        holder.switchCompat.setChecked(item.on);

        // Make sure we update the model if the user taps the switch
        holder.switchCompat.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                int adapterPosition = holder.getAdapterPosition();

                ItemModel tapped = itemModels.get(adapterPosition);
                itemModels.set(adapterPosition, new ItemModel(tapped.text, isChecked));

                if (onItemCheckedChangeListener != null) {
                    onItemCheckedChangeListener.onItemCheckedChanged(adapterPosition, isChecked);
                }
            }
        });
    }

    @Override
    public void onViewRecycled(@NonNull ViewHolder holder) {
        super.onViewRecycled(holder);

        holder.switchCompat.setOnCheckedChangeListener(null);
    }

    @Override
    public int getItemCount() {
        return itemModels.size();
    }

    public void setOnItemCheckedChangeListener(@Nullable OnItemCheckedChangeListener onItemCheckedChangeListener) {
        this.onItemCheckedChangeListener = onItemCheckedChangeListener;
    }

    interface OnItemCheckedChangeListener {
        /**
         * Fired when the item check state is changed
         */
        void onItemCheckedChanged(int position, boolean isChecked);
    }

    class ViewHolder extends RecyclerView.ViewHolder {
        TextView text;
        SwitchCompat switchCompat;

        ViewHolder(View itemView) {
            super(itemView);
            text = itemView.findViewById(R.id.item_text);
            switchCompat = itemView.findViewById(R.id.item_switch);
        }
    }
}

有很多东西需要消化,但让我们关注重要的部分 - 方法

onBindViewHolder
。前 3 行是视图的经典回收。我们在正确的位置抓取模型,并在视图中设置与模型属性相对应的元素。

然后就变得更有趣了。我们设置一个

OnCheckedChangeListener
来在每次开关改变状态时更新模型和活动。前 3 行更改适配器中的模型,其余行使用自定义接口
OnItemCheckedChangeListener
通知侦听器有关开关更改的信息。需要注意的是,在方法
OnCheckedChangeListener
中,您不应再使用
position
,而应使用
holder.getAdapterPosition
。这将为您提供适配器数据列表中的正确位置。

由于现在适配器在数据列表中始终具有正确的模型,因此每次调用方法

onBindViewHolder
时,适配器都确切地知道如何设置视图。这意味着在滚动和回收视图时,它将保留
data
列表内模型中每个项目的状态。

当视图被回收时,删除

OnCheckedChangeListener
很重要 -
onViewRecycled
。这可以避免适配器在
switchCompat
中设置
onBindViewHolder
的值时弄乱计数。

以下是该活动的示例:

public class MainActivity extends AppCompatActivity {
    private int count = 0;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        List<ItemModel> data = new ArrayList<>();

        for (int i = 1; i <= 100; i++)
            data.add(new ItemModel("Item " + i, false));

        ItemsAdapter adapter = new ItemsAdapter(data);

        ((RecyclerView) findViewById(R.id.recyclerview)).setAdapter(adapter);

        final TextView countTextView = findViewById(R.id.count);

        drawCount(countTextView);

        adapter.setOnItemCheckedChangeListener(new ItemsAdapter.OnItemCheckedChangeListener() {
            @Override
            public void onItemCheckedChanged(int position, boolean isChecked) {
                if (isChecked)
                    count++;
                else
                    count--;

                drawCount(countTextView);
            }
        });
    }

    private void drawCount(TextView countTextView) {
        countTextView.setText(String.valueOf(count));
    }
}

此代码旨在演示该想法,而不是遵循:)无论如何,我们设置所有初始状态,然后设置自定义侦听器

OnItemCheckedChangeListener
以更新活动中的文本视图。

布局文件在这里不应该相关,但正如您可以想象的那样,该活动有一个 id 为

count
的文本视图,并且有一个 id 为
recyclerview
的回收器视图。

希望这有帮助


0
投票

将以下方法添加到适配器后,它为我解决了:

@Override
public int getItemViewType(int position) {

    return position;
}

0
投票
// ViewHolder
inner class TransactionViewHolder(val binding: AutoRenewAdapterBinding) :
    RecyclerView.ViewHolder(binding.root) {

    fun bind(item: AutoRenewResponse.Data) {
        binding.item = item

        // Remove any existing listener to prevent issues during recycling
        binding.kidSafeSwitch.setOnCheckedChangeListener(null)

        // First-time initialization based on autoRenew, then use isSwitchEnabled
        if (!item.isInitialized) {
            item.isSwitchEnabled = item.autoRenew != 0
            item.isInitialized = true
        }

        // Set the switch state and text based on isSwitchEnabled
        binding.kidSafeSwitch.isChecked = item.isSwitchEnabled
        binding.kidSafeText.text = if (item.isSwitchEnabled) "Enable" else "Disable"

        // OnCheckedChangeListener
        binding.kidSafeSwitch.setOnCheckedChangeListener { _, isChecked ->
            item.isSwitchEnabled = isChecked
            binding.kidSafeText.text = if (isChecked) "Enable" else "Disable"

            // Notify the listener in the AutoRenewSettingActivity
            listener.onItemToggle(item.stbId, isChecked)
        }

        binding.executePendingBindings()
    }
}


<!-- XML layout -->
<androidx.constraintlayout.widget.ConstraintLayout
    android:id="@+id/kid_safe_layout"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toEndOf="@+id/lc_auto_renew"
    app:layout_constraintTop_toTopOf="parent">

    <androidx.appcompat.widget.SwitchCompat
        android:id="@+id/kid_safe_switch"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:thumb="@drawable/custom_thumb"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:track="@drawable/custom_track" />

    <TextView
        android:id="@+id/kid_safe_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:alpha="0.5"
        android:text="Disabled"
        android:textColor="@color/text_primary"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/kid_safe_switch" />
</androidx.constraintlayout.widget.ConstraintLayout>


// Model class
class Data : Serializable {
    var stbId: Int? = null
    var STATUS: String? = null
    var macAddress: String? = null
    var autoRenew: Int? = null
    var stbType: String? = null
    // Bind the switch state
    var isSwitchEnabled: Boolean = false
    var isInitialized: Boolean = false
}


 it Working
© www.soinside.com 2019 - 2024. All rights reserved.