Android 重用在Fragment.onCreateView()上创建的布局,以避免多次显示时出现膨胀
我正在使用DialogFragment来显示“模式”底部工作表菜单(更多信息,请参见此处)。由于它包含一种用于RecyclerView中包含的项目的上下文菜单,因此在运行时可能会多次显示它 但是,always被调用,也被调用,这会导致布局膨胀,这可以(?)被视为在UI线程中计算的“繁重”任务,出于性能原因,我希望避免这样做。因此,为了避免每次显示DialogFragment时布局膨胀,我创建了一个ViewGroup成员对象,指向返回的视图Fragment.onCreateView(),以便重用,如下所示:Android 重用在Fragment.onCreateView()上创建的布局,以避免多次显示时出现膨胀,android,performance,android-fragments,layout-inflater,Android,Performance,Android Fragments,Layout Inflater,我正在使用DialogFragment来显示“模式”底部工作表菜单(更多信息,请参见此处)。由于它包含一种用于RecyclerView中包含的项目的上下文菜单,因此在运行时可能会多次显示它 但是,always被调用,也被调用,这会导致布局膨胀,这可以(?)被视为在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)
}
}
但是,一旦第一次使用,这样的视图必须从片段容器视图中分离出来才能重用(请参见发布的代码段上的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;
}
}
只要你不屈服于任何可能的错误,这已经是一个很好的实践。。但是,关于恢复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
,但如果滚动它,它将更新
另一件事是,我倾向于使用weakrefence来保存要重用的dialogFragment。我甚至有一个数组
在java应用程序中,如果不使用类似的机制,当用户一次又一次地打开和关闭同一个对话框时,可以看到内存使用量的快速激增。因此,至少在必要时重用对话框是一种不错的做法。“这可以被视为在UI线程中计算的一项‘繁重’任务”——通常不是这样,特别是对于像您这样的小型布局。“出于性能原因,我希望避免使用它”——为什么?你有证据表明这是一个问题吗?嗯,实际上不是,但我认为这是一个很好的做法,因为在使用更重的布局“普通SWAWARE”的情况下,可以更好地执行“我认为这是一个很好的实践”——为什么?如果您无法解释这种额外的复杂性和脆弱性是如何让用户受益的,那么为什么会在代码评审中接受它呢?一般来说,框架和库提供了视图回收,这种回收几乎肯定会带来好处(
AdapterView
,RecyclerView
,ViewPager
,等等)。所有其他的东西都需要大量的证据证明它是值得的。只是因为我不认为在多次使用时会有效率地对布局进行多次渲染,所以如果有一种简单的方法来提高效率,我会使用它(即使它不会带来显著的性能改进)。我猜库和框架示例在这里不适用,因为它们被设计为被许多开发人员广泛用于多种目的,而这种情况只是特定于应用程序。我不是在寻找过早的优化,只是为了找到一个合适的方法来做到这一点“因此,如果有一个简单的方法可以提高效率,我会使用它”——在这种情况下,我不相信有。虽然存在可以重用片段及其视图的场景,但我认为它不适用于DialogFragment
和子类。