【问题标题】:How to save a custom View's state in a RecyclerView in a dynamically added fragment?如何在动态添加的片段中将自定义视图的状态保存在 RecyclerView 中?
【发布时间】:2016-10-28 10:25:28
【问题描述】:

TL;DR :RecyclerView 回收View 或替换Fragment 时,我无法区分RecyclerViewonBindViewHolder 的调用。

我确实花了很多时间在 SO 上寻找答案,但最终没有任何效果。它所做的就是告诉我答案可能是使用savedInstanceState,但我没有成功使用它。

结构:

  • 主要片段
    • 主视图
    • SwitchFragmentButton

MainView里面是一个FrameLayout,用来存放一个Fragment

第一个Fragment无所谓,但第二个FragmentOverviewFragment,其结构为:

  • 概述片段
    • 回收站查看
      • 自定义项目视图
        • 文本视图
        • 更改文本颜色按钮
        • 删除项目按钮
        • ...
    • 添加项目按钮

我使用的是RecyclerView,因为列表必须是水平的,这是我发现的最简单的方法。

期望的行为:

我希望在单击按钮两次(更改片段然后返回)时保留颜色更改,但在从列表中删除项目然后再次添加时不保留。

问题:

颜色要么一直保存,要么从不保存。在RecyclerView 的适配器中使用onBindViewHolder 设置文本颜色不允许我区分绑定是来自片段替换(我将恢复更改的颜色)还是视图回收(我不会恢复它)

代码:

SwitchFragmentButtonOnClickListener 调用MainView 的方法

void switchFragment(Fragment fragment) {
    FragmentManager fm = mainFragment.getChildFragmentManager();
    FragmentTransaction ft = fm.beginTransaction();
    ft.replace(R.id.mainView_fragment_slot, fragment);
    ft.commit();
}

以下是管理颜色的方式(简化):

public class CustomItemView extends FrameLayout {
    private TextView textView;
    private int color;
    /* other attributes */

    public CustomItemView(/* any default constructor */) {
        super(/* args */);
        init();
    }

    private void init() {
        inflate(getContext, R.layout.custom_item_view, this);
        textView = (TextView) findViewById(R.id.customItemView_text_view);
        ChangeTextColorButton ctcButton = /* ... */;
        /* other views */

        ctcButton.setOnClickListener(new OnClickListener() {
            public void onClick() {
                setColor(getColorWithDialogAndAll());
            }
        }

        resetColor();
    }

    public void resetColor() {
        setColor(0xff0000ff);
    }

    public void setColor(int color) {
        this.color = color;
        textView.setTextColor(color);
    }

    /* other methods */
}

这是RecyclerViewAdapter 定义:

public class MyAdapter extends RecyclerView.Adapter {
    private List<MyItem> myItemList = new LinkedList<>();

    public void onBindViewHolder(final RecyclerView.ViewHolder holder, int position) {
        MyViewHolder viewHolder = (MyViewHolder) holder;
        //this is called in both situations
        viewHolder.customItemView.resetColor();
    }

    /* other methods */

    private class MyViewHolder extends RecyclerView.ViewHolder {
        CustomItemView cutsomItemView;

        /* constructor, calling super() and setting the attribute */
    }
}

MyItem这里是CustomItemView的核心item(每个MyItem都有它的CustomItemView,每个CustomItemView都有它的MyItem)

注意事项:

minSdkVersion:19.

编辑:

感谢@goldenb,问题已经缩小,出现了两种解决方案。

解决方案 1:

不要替换片段,而是将它们全部添加然后显示/隐藏它们。

void switchFragment(Fragment fragment) {
    FragmentManager fm = mainFragment.getChildFragmentManager();
    FragmentTransaction ft = fm.beginTransaction();
    ft.hide(currentFragment);
    ft.show(R.id.mainView_fragment_slot, fragment);
    ft.commit();

    currentFragment = fragment;
}

此解决方案在某种程度上可行,但不能真正解决问题。它避免删除Fragments,因此状态被保留。

但是,我的问题是关于如何保存然后恢复 Fragment 的状态,所以我不能接受这个作为答案。

解决方案 2:

覆盖OverviewFragment#onPause 以保存状态,并在OverviewFragment#onCreate 中恢复它。

这个解决方案似乎是合理的,我目前正在探索它。然而,它需要在我的项目中进行一些重组,以将数据从 CustomItemView 重新定位到 MyAdapter,因此需要一些时间。

【问题讨论】:

  • 伙计,你的问题很有趣,但我不清楚。我认为您应该尝试将其归结为: 1. 最小层次结构,去除不感兴趣的子级和不感兴趣的层 2. 清楚地说明问题所在。
  • 作为一个快速响应,请注意,恢复其自己的状态是 view 的责任,而不是你的。覆盖自定义视图的 onSaveInstanceState() 和 onRestoreInstanceState() 方法以保存和恢复其颜色。
  • @natario 1. 你会删除哪个孩子?我试图对上下文给出一个很好的想法。 2.我改写了,看看是否有帮助。 3. 重写这些方法不会导致回收的视图也恢复不需要的颜色吗?

标签: android android-fragments android-lifecycle fragmenttransaction android-savedstate


【解决方案1】:

您可以在 Activity 中使用 savedInstancestate 或 RestoresavedInstancestate,或者在片段中使用共享首选项来存储数据并在您回调片段时提取数据

【讨论】:

  • 这里的活动不受影响。当我切换片段时,不会调用 Activity 的 onSaveInstanceState(..) 和 onRestoreInstanceState(..)。
【解决方案2】:

如果您选择完全不破坏视图层次结构是可以接受的:

FragmentManager fragmentManager = getFragmentManager()
// Or: FragmentManager fragmentManager = getSupportFragmentManager()
fragmentManager.beginTransaction()
    .remove(fragment1)
    .add(R.id.fragment_container, fragment2)
    .show(fragment3)
    .hide(fragment4)
    .commit();

希望在完成所有编辑后能有所帮助!

【讨论】:

  • 没有改变任何东西......只是我的代码现在更干净了。谢谢! :)
  • @Irhala 看代码,你是在做transaction.addToBackStack(null);在提交之前?当您稍后调用 FragmentManager.popBackStack() 时会发生什么?
  • 这正是我所做的。我对 addToBackStack() 和 popBackStack() 了解不多,但我猜 popBackStack 所做的就是先删除(新)然后再添加(旧),这是我之前手动完成的。似乎什么都没有改变。
  • 我想我知道你的问题是什么,我会更新答案,给我几分钟时间
  • @Irhala 检查答案更新并告诉我它是否有效
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-11-29
  • 1970-01-01
  • 2023-03-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多