Android 添加片段容器视图后膨胀布局崩溃
Android inflate layout crashes after adding fragment container view
目前我有一个扩展 AppCompatActivity 的自定义抽屉 activity,如下所示:
public class DrawerActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener{
...
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.setContentView(R.layout.activity_drawer);
...
}
@Override
public void setContentView(int layoutResID) {
frameLayout = findViewById(R.id.drawer_frame);
LayoutInflater.from(getApplicationContext()).inflate(layoutResID, frameLayout);
super.setContentView(drawerLayout);
}
...
}
正如你在这里看到的,我覆盖了 setContentView 方法,所以扩展这个抽屉 activity 的 activity 的布局 id 将被传递到这个方法中并在抽屉中膨胀 activity的子视图。简而言之,我希望扩展此抽屉 activity 的所有活动都将具有导航抽屉。它适用于一般活动。
但是现在,我有一个 activity,其中包含片段容器视图并扩展了抽屉 activity。当我启动此 activity 时,应用程序崩溃并出现以下错误:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.aaa, PID: 10730
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.aaa/com.example.aaa.gym.GymActivity}: android.view.InflateException: Binary XML file line #29 in com.example.aaa:layout/activity_gym: Binary XML file line #29 in com.example.aaa:layout/activity_gym: Error inflating class androidx.fragment.app.FragmentContainerView
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3635)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3792)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:103)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2210)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loopOnce(Looper.java:201)
at android.os.Looper.loop(Looper.java:288)
at android.app.ActivityThread.main(ActivityThread.java:7839)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)
Caused by: android.view.InflateException: Binary XML file line #29 in com.example.aaa:layout/activity_gym: Binary XML file line #29 in com.example.aaa:layout/activity_gym: Error inflating class androidx.fragment.app.FragmentContainerView
Caused by: android.view.InflateException: Binary XML file line #29 in com.example.aaa:layout/activity_gym: Error inflating class androidx.fragment.app.FragmentContainerView
Caused by: java.lang.reflect.InvocationTargetException
at java.lang.reflect.Constructor.newInstance0(Native Method)
at java.lang.reflect.Constructor.newInstance(Constructor.java:343)
at android.view.LayoutInflater.createView(LayoutInflater.java:858)
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:1010)
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:965)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:1127)
at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:1088)
at android.view.LayoutInflater.inflate(LayoutInflater.java:686)
at android.view.LayoutInflater.inflate(LayoutInflater.java:538)
at android.view.LayoutInflater.inflate(LayoutInflater.java:485)
at com.example.aaa.DrawerActivity.setContentView(DrawerActivity.java:57)
at com.example.aaa.gym.GymActivity.onCreate(GymActivity.java:62)
at android.app.Activity.performCreate(Activity.java:8051)
at android.app.Activity.performCreate(Activity.java:8031)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1329)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3608)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3792)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:103)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2210)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loopOnce(Looper.java:201)
at android.os.Looper.loop(Looper.java:288)
at android.app.ActivityThread.main(ActivityThread.java:7839)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)
Caused by: java.lang.UnsupportedOperationException: FragmentContainerView must be within a FragmentActivity to use android:name="com.example.aaa.gym.PlaceFragment"
E/AndroidRuntime: at androidx.fragment.app.FragmentContainerView.<init>(FragmentContainerView.java:142)
at androidx.fragment.app.FragmentContainerView.<init>(FragmentContainerView.java:120)
... 28 more
如您所见,根本原因是 java.lang.UnsupportedOperationException: FragmentContainerView must be within a FragmentActivity to use android:name="com.example.aaa.gym.PlaceFragment"
。但是,activity 应该能够扩充片段,因为它扩展了 DrawerActivity 并且 DrawerActivity 扩展了 AppCompatActivity,而 AppCompatActivity 又扩展了 Fragment Activity。我对此很困惑。
这里是 activity 代码:
public class GymActivity extends DrawerActivity implements OnMapReadyCallback, GoogleMap.OnMarkerClickListener {
private ActivityGymBinding binding;
...
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
markers = new ArrayList<>();
binding = ActivityGymBinding.inflate(getLayoutInflater());
// here crashes, the actual crash line is the second line of the drawer activity's setContentView
setContentView(binding.getRoot().getSourceLayoutResId());
...
}
...
}
这是 activity xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/mapLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".gym.GymActivity">
<com.google.android.gms.maps.MapView
android:id="@+id/mapView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:contentDescription="@string/title_activity_gym"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.fragment.app.FragmentContainerView
android:id="@+id/place_fragment"
android:name="com.example.aaa.gym.PlaceFragment"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:layout="@layout/place_fragment" />
</androidx.constraintlayout.widget.ConstraintLayout>
片段,几乎没有任何变化
public class PlaceFragment extends Fragment {
private PlaceViewModel mViewModel;
private PlaceFragmentBinding binding;
public static PlaceFragment newInstance() {
return new PlaceFragment();
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
mViewModel = new ViewModelProvider(requireActivity()).get(PlaceViewModel.class);
mViewModel.getName().observe(getViewLifecycleOwner(), s -> binding.name.setText(s));
mViewModel.getLocation().observe(getViewLifecycleOwner(), s -> binding.location.setText(s));
return inflater.inflate(R.layout.place_fragment, container, false);
}
}
fragment.xml,只是一个简单的框架布局
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".gym.PlaceFragment">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/name"
android:text="Hello" />
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/location"
android:text="Hello" />
</FrameLayout>
谁能看出我的应用程序崩溃的原因?如有任何建议,我们将不胜感激。
问题出在这里:
LayoutInflater.from(getApplicationContext()).inflate(layoutResID, frameLayout);
您正在使用应用程序上下文 - 具体来说 不是 您的 Activity 因此,不使用 Activity 的主题,也不您的应用程序 class 扩展 FragmentActivity
。您需要使用 LayoutInflater.from(this)
来实际使用您的 Activity 作为布局充气器。
然而,虽然这会修复您的崩溃问题,但您的 GymActivity
仍然无法正常工作,因为这些行:
binding = ActivityGymBinding.inflate(getLayoutInflater());
// here crashes, the actual crash line is the second line of the drawer activity's setContentView
setContentView(binding.getRoot().getSourceLayoutResId());
在这里,您创建了一个 binding
对象,但从未真正将它添加到您的 Activity。相反,您调用您的 setContentView
重写来膨胀该视图的全新实例。这意味着对 binding
的任何更改都不会反映在 UI.
中
相反,您希望提供对 setContentView
的覆盖,它采用 View
并在 FrameLayout 上调用 addView
以将已经膨胀的视图添加到您的布局中。
@Override
public void setContentView(View view) {
frameLayout = findViewById(R.id.drawer_frame);
frameLayout.addView(view)
// Note you should never be calling super.setContentView here.
// You've already called it in your own onCreate()
}
这让您可以像这样使用它:
binding = ActivityGymBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
目前我有一个扩展 AppCompatActivity 的自定义抽屉 activity,如下所示:
public class DrawerActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener{
...
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.setContentView(R.layout.activity_drawer);
...
}
@Override
public void setContentView(int layoutResID) {
frameLayout = findViewById(R.id.drawer_frame);
LayoutInflater.from(getApplicationContext()).inflate(layoutResID, frameLayout);
super.setContentView(drawerLayout);
}
...
}
正如你在这里看到的,我覆盖了 setContentView 方法,所以扩展这个抽屉 activity 的 activity 的布局 id 将被传递到这个方法中并在抽屉中膨胀 activity的子视图。简而言之,我希望扩展此抽屉 activity 的所有活动都将具有导航抽屉。它适用于一般活动。
但是现在,我有一个 activity,其中包含片段容器视图并扩展了抽屉 activity。当我启动此 activity 时,应用程序崩溃并出现以下错误:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.aaa, PID: 10730
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.aaa/com.example.aaa.gym.GymActivity}: android.view.InflateException: Binary XML file line #29 in com.example.aaa:layout/activity_gym: Binary XML file line #29 in com.example.aaa:layout/activity_gym: Error inflating class androidx.fragment.app.FragmentContainerView
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3635)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3792)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:103)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2210)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loopOnce(Looper.java:201)
at android.os.Looper.loop(Looper.java:288)
at android.app.ActivityThread.main(ActivityThread.java:7839)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)
Caused by: android.view.InflateException: Binary XML file line #29 in com.example.aaa:layout/activity_gym: Binary XML file line #29 in com.example.aaa:layout/activity_gym: Error inflating class androidx.fragment.app.FragmentContainerView
Caused by: android.view.InflateException: Binary XML file line #29 in com.example.aaa:layout/activity_gym: Error inflating class androidx.fragment.app.FragmentContainerView
Caused by: java.lang.reflect.InvocationTargetException
at java.lang.reflect.Constructor.newInstance0(Native Method)
at java.lang.reflect.Constructor.newInstance(Constructor.java:343)
at android.view.LayoutInflater.createView(LayoutInflater.java:858)
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:1010)
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:965)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:1127)
at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:1088)
at android.view.LayoutInflater.inflate(LayoutInflater.java:686)
at android.view.LayoutInflater.inflate(LayoutInflater.java:538)
at android.view.LayoutInflater.inflate(LayoutInflater.java:485)
at com.example.aaa.DrawerActivity.setContentView(DrawerActivity.java:57)
at com.example.aaa.gym.GymActivity.onCreate(GymActivity.java:62)
at android.app.Activity.performCreate(Activity.java:8051)
at android.app.Activity.performCreate(Activity.java:8031)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1329)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3608)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3792)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:103)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2210)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loopOnce(Looper.java:201)
at android.os.Looper.loop(Looper.java:288)
at android.app.ActivityThread.main(ActivityThread.java:7839)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)
Caused by: java.lang.UnsupportedOperationException: FragmentContainerView must be within a FragmentActivity to use android:name="com.example.aaa.gym.PlaceFragment"
E/AndroidRuntime: at androidx.fragment.app.FragmentContainerView.<init>(FragmentContainerView.java:142)
at androidx.fragment.app.FragmentContainerView.<init>(FragmentContainerView.java:120)
... 28 more
如您所见,根本原因是 java.lang.UnsupportedOperationException: FragmentContainerView must be within a FragmentActivity to use android:name="com.example.aaa.gym.PlaceFragment"
。但是,activity 应该能够扩充片段,因为它扩展了 DrawerActivity 并且 DrawerActivity 扩展了 AppCompatActivity,而 AppCompatActivity 又扩展了 Fragment Activity。我对此很困惑。
这里是 activity 代码:
public class GymActivity extends DrawerActivity implements OnMapReadyCallback, GoogleMap.OnMarkerClickListener {
private ActivityGymBinding binding;
...
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
markers = new ArrayList<>();
binding = ActivityGymBinding.inflate(getLayoutInflater());
// here crashes, the actual crash line is the second line of the drawer activity's setContentView
setContentView(binding.getRoot().getSourceLayoutResId());
...
}
...
}
这是 activity xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/mapLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".gym.GymActivity">
<com.google.android.gms.maps.MapView
android:id="@+id/mapView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:contentDescription="@string/title_activity_gym"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.fragment.app.FragmentContainerView
android:id="@+id/place_fragment"
android:name="com.example.aaa.gym.PlaceFragment"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:layout="@layout/place_fragment" />
</androidx.constraintlayout.widget.ConstraintLayout>
片段,几乎没有任何变化
public class PlaceFragment extends Fragment {
private PlaceViewModel mViewModel;
private PlaceFragmentBinding binding;
public static PlaceFragment newInstance() {
return new PlaceFragment();
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
mViewModel = new ViewModelProvider(requireActivity()).get(PlaceViewModel.class);
mViewModel.getName().observe(getViewLifecycleOwner(), s -> binding.name.setText(s));
mViewModel.getLocation().observe(getViewLifecycleOwner(), s -> binding.location.setText(s));
return inflater.inflate(R.layout.place_fragment, container, false);
}
}
fragment.xml,只是一个简单的框架布局
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".gym.PlaceFragment">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/name"
android:text="Hello" />
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/location"
android:text="Hello" />
</FrameLayout>
谁能看出我的应用程序崩溃的原因?如有任何建议,我们将不胜感激。
问题出在这里:
LayoutInflater.from(getApplicationContext()).inflate(layoutResID, frameLayout);
您正在使用应用程序上下文 - 具体来说 不是 您的 Activity 因此,不使用 Activity 的主题,也不您的应用程序 class 扩展 FragmentActivity
。您需要使用 LayoutInflater.from(this)
来实际使用您的 Activity 作为布局充气器。
然而,虽然这会修复您的崩溃问题,但您的 GymActivity
仍然无法正常工作,因为这些行:
binding = ActivityGymBinding.inflate(getLayoutInflater());
// here crashes, the actual crash line is the second line of the drawer activity's setContentView
setContentView(binding.getRoot().getSourceLayoutResId());
在这里,您创建了一个 binding
对象,但从未真正将它添加到您的 Activity。相反,您调用您的 setContentView
重写来膨胀该视图的全新实例。这意味着对 binding
的任何更改都不会反映在 UI.
相反,您希望提供对 setContentView
的覆盖,它采用 View
并在 FrameLayout 上调用 addView
以将已经膨胀的视图添加到您的布局中。
@Override
public void setContentView(View view) {
frameLayout = findViewById(R.id.drawer_frame);
frameLayout.addView(view)
// Note you should never be calling super.setContentView here.
// You've already called it in your own onCreate()
}
这让您可以像这样使用它:
binding = ActivityGymBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());