We have been working on adding AR features to our existing APP for a couple of months with limited progress. Very excited to read the recent development from google on sceneForm and arFragment. our current APP consists three Fragments and one of them will need AR features.
It looks straight forward to us,so We replaced the Fragment in our APP with arFragment. The build is successful and stopped during running with little information for debugging. any suggestion on the proper steps for us to upgrade from Fragment to arFragment? or maybe I missed the points of arFragment here?
in order to show the problem without for you to go through our length code (yet valuable to us), we constructed a dummy project based on the sample project from Google: HelloSceneform. Basically, we changed the static Fragment to dynamic Fragment. Only two files are changed and two files are added, which are attached thereafter. The modified project can be built successfully, but stopped when starting to run.
Thank you
Peter
/////// File modified, HelloSceneformActivity.java:
import android.support.v4.app.FragmentTransaction;
// private ArFragment arFragment;
private ItemOneFragment arFragment;
//arFragment = (ArFragment) getSupportFragmentManager().findFragmentById(R.id.ux_fragment);
arFragment = ItemOneFragment.newInstance();
//Manually displaying the first fragment - one time only
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.frame_layout, arFragment);
transaction.commit();
/////// File modified, activity_ux.xml:
<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=".HelloSceneformActivity">
</FrameLayout>
////// File added fragment_item_one.xml:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/frame_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ItemOneFragment">
</FrameLayout>
/////// File added, ItemOneragment.java:
package com.google.ar.sceneform.samples.hellosceneform;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.google.ar.sceneform.ux.ArFragment;
public class ItemOneFragment extends ArFragment {
public static ItemOneFragment newInstance() {
ItemOneFragment fragment = new ItemOneFragment();
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_item_one, container, false);
}
}
I have experienced the same when I try to add the ArFragment dynamically to my activity. It is crashing because I was trying to access the ArSceneView right after I commit the fragment which appeared to be null at that point.
The solution that worked for me was to implement a completion listener that will provide a callback in the Activity when the fragment is done configuring the ARSession.
Below is the basic idea.
public class MyActivity implements MyArFragment.OnCompletionListener{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getSupportFragmentManager().beginTransaction().add(R.id.fragment_holder, new
MyArFragment(), "my_arfragment").commit();
}
@Override
public void onComplete() {
ArFragment arFragment = (ArFragment) getSupportFragmentManager().findFragmentByTag("my_arfragment");
ArSceneView view = arFragment.getArSceneView();
Scene scene = view.getScene();
scene.addOnUpdateListener(this::onUpdateFrame);
}
}
And the fragment:
public class MyFragment extends ArFragment{
public static interface OnCompleteListener {
public abstract void onComplete();
}
private OnCompleteListener mListener;
@Override
public void onAttach(Context context) {
super.onAttach(context);
try {
this.mListener = (OnCompleteListener)context;
}
catch (final ClassCastException e) {
throw new ClassCastException(context.toString() + " must implement
OnCompleteListener");
}
}
@Override
protected Config getSessionConfiguration(Session session) {
//Update session config...
mListener.onComplete();
return config;
}
}
In one of my project I've used the following structure to accomplish the integration of ArFragment. Maybe this will give you some new hints.
I have a root layout with the first element that is a FrameLayout called "body".
This "body" is used as a placeholder to switch the 3 Fragment that exist into the app. One of these 3 is called "SimulationFragment" and extends ArFragment of Sceneform. The corresponding layout is composed by the root FrameLayout with some elements and another nested FrameLayout called "ar_frameLayout".
At Runtime I've changed the implementation of onCreateView for the SimulationFragment with a direct call of super.onCreateView() that gives me the base view of ArFragment (this call initializes also the scene from getArSceneView().getScene()). After that I've added this view into the container view of the simulation fragment that I've previously inflated and then returned it. Something like this:
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View containerView = inflater.inflate(R.layout.fragment_simulation, container, false);
FrameLayout arCoreFrameLayout = containerView.findViewById(R.id.ar_core_frame_layout);
// Inflate the layout for this fragment
View view = super.onCreateView(inflater, container, savedInstanceState);
arCoreFrameLayout.addView(view);
return containerView;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With