【问题标题】:Reusing layout created on Fragment.onCreateView() to avoid inflation when showing multiple times重用在 Fragment.onCreateView() 上创建的布局以避免多次显示时出现膨胀
【发布时间】:2020-01-25 18:57:51
【问题描述】:

我正在使用一个 DialogFragment 来显示一个“模态”底部菜单(更多信息在这里:https://material.io/develop/android/components/bottom-sheet-dialog-fragment/)。由于它包含一种用于 RecyclerView 中包含的项目的上下文菜单,因此它可能会在运行时显示多次。

但是,总是调用DialogFragment.show(),同时调用Fragment.onCreateView(),这会导致布局膨胀,这可以(?)被认为是要在UI线程中计算的“繁重”任务,我想出于性能原因避免。因此,为了避免每次显示 DialogFragment 时布局膨胀,我创建了一个 ViewGroup 成员对象,指向要返回的视图 Fragment.onCreateView() 以便重用,如下所示:

public class BottomMenu extends BottomSheetDialogFragment {

    private ViewGroup mLayout;
    private TextView mLabel;

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        if (mLayout == null) {
            mLayout = (ViewGroup) LayoutInflater.from(getContext()).inflate(R.layout.bottom_sheet, container, false);
            mLabel = mLayout.findViewById(R.id.bottom_sheet_label);
        }
        return mLayout;
    }

    @Override
    public void onDismiss(@NonNull DialogInterface dialog) {
        super.onDismiss(dialog);
        // The view cannot be reused if it's already attached to the previous parent view
        ((ViewGroup) mLayout.getParent()).removeView(mLayout);
    }

    public void setLabel(String label) {
        mLabel.setText(label)
    }

}

但是一旦第一次使用,这样的视图必须从 Fragment 容器视图中分离出来才能重用(参见发布的 sn-p 上的 onDismissed() 覆盖方法),这似乎是一个讨厌的解决方法

所以我发布这个问题是为了检查是否有人知道为同一片段重用布局的更好方法

更多细节在这里:

public class ActivityMain extends AppCompatActivity {

    private BottomMenu mBottomMenu;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        [...]
        mBottomMenu = new BottomMenu();
    }

    @Override 
    public boolean onLongClick(View v) {
        mBottomSheet.setLabel(label);
        // The following calls onCreateView() in Fragment, so try to return
        // there the previously inflated layout, if any
        mBottomSheet.show(getSupportFragmentManager(), "TAG?");
        return true;
    }

}

【问题讨论】:

  • “可以(?)被认为是要在 UI 线程中计算的“繁重”任务”——通常不会,尤其是对于像您这样的小型布局。 “出于性能原因,我想避免这种情况”——为什么?您是否有证据表明它导致了问题?
  • 好吧,实际上并非如此,但我认为这是一种很好的做法,因为在使用较重的布局@CommonsWare 时性能会更好
  • “我认为这是一种很好的做法”——为什么?如果你无法解释这种额外的复杂性和脆弱性如何使用户受益,为什么它会在代码审查中被接受?通常,框架和库提供视图回收,这种回收几乎肯定会带来好处(AdapterViewRecyclerViewViewPager 等)。其他一切都需要一些证据证明它值得复杂。
  • 只是因为当您可以多次使用时,我不认为多次膨胀布局是有效的,所以如果有一种更有效的简单方法我会使用它(即使它没有引入显着性能改进)。而且我猜库和框架示例在这里不适用,因为它们被设计为被许多开发人员广泛使用,用于多种目的,而这种情况只是特定于应用程序的。我不是在寻找过早的优化,只是为了做一个合适的事情
  • “所以如果有一种更简单的方法可以提高效率,我会使用它”——在这种情况下,我不相信有。虽然有些场景可以重用片段及其视图,但我认为 DialogFragment 和子类并不实用。

标签: android performance android-fragments layout-inflater


【解决方案1】:

只要您不屈服于任何可能的错误,这已经是一种很好的做法。但是,关于恢复 dialogFragment,我想告诉您一两件事。

public class BottomMenu extends BottomSheetDialogFragment {
    private ViewGroup mLayout;
    private TextView mLabel;

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        if (mLayout == null) {
            mLayout = (ViewGroup) LayoutInflater.from(getContext()).inflate(R.layout.bottom_sheet, container, false);
            mLabel = mLayout.findViewById(R.id.bottom_sheet_label);
        } else if(mLayout.getParent()!=null) { // it's not a lot of code. just a few lines……
            ((ViewGroup)mLayout.getParent()).removeView(mLayout);
        }
        return mLayout;
    }
}

一件事是关于嵌套片段。当 dialogFragment 持有一个 viewpager 并且 viewpager 有多个子片段时,您必须在 onCreateView 的重用调用上重置 viewpager 的适配器。原因是关闭dialogFragment后,getChildFragmentManager()返回的旧的fragmentManager不再有效,应该更新一下。

... onCreateView(...)

        if (mLayout == null) {
            ...
        } else { 
            ...
            viewpager.setAdapter(new MyFragmentAdapter(getChildFragmentManager(), fragments));
        }

如果省略这一步,你可能会在重用dialogFragment时观察到奇怪的行为,例如子片段中的recyclerviews停止更新以响应NotifyDatasetChanged,但如果你滚动它,它会更新。

另一件事是我倾向于使用 Wea​​kReference 来保存要重用的 dialogFragment。我什至有一个数组。

在java应用程序中,如果你不使用类似的机制,当用户一次又一次地打开和关闭同一个对话框时,你会看到内存使用量急剧增加。因此,至少在必要时重用对话框并不是一个坏习惯。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-10-21
    • 1970-01-01
    • 2017-08-11
    • 2011-10-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多