Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

react-native add fragment to view is not showing

I have added a dynamic generated FrameLayout to a react-native view. The Framelayout is showing up. but when I add a fragment to the view, nothing is showing.

here is a part of the code:

    public FrameLayout createViewInstance(ThemedReactContext context) {

            Fragment fragment = MapFragment.newInstance(mapKey);
            FrameLayout view = new FrameLayout(context);
            view.setWillNotDraw(false);

            LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
            view.setLayoutParams(lp);

            view.setId(View.generateViewId());

            view.setBackgroundColor(Color.parseColor("#1FFDDB"));

            FragmentActivity activity = MainActivity.getFragmentActivity();

            FragmentManager fragmentManager = activity.getSupportFragmentManager();

            fragmentManager.beginTransaction().remove(fragment).commit();
            fragmentManager.executePendingTransactions();

            FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

            fragmentTransaction.add(0, fragment);

            fragmentTransaction.show(fragment);
            fragmentTransaction.attach(fragment);
            fragmentTransaction.commit();
            fragmentManager.executePendingTransactions();
            return view;
        }

When I add a TextView to the view it is showing:

// add a textfield to the view
TextView textView1 = new TextView(context);
textView1.setText("Android");
textView1.setTextSize(30);
textView1.setGravity(Gravity.CENTER);

view.setVisibility(View.VISIBLE);

view.addView(textView1);

so there is an issue with the Fragment. any ideas why the fragment is not showing up?

like image 870
dirk slootmaekers Avatar asked Mar 11 '23 01:03

dirk slootmaekers


1 Answers

I also encountered this problem and found the solution.

Android side: First, create a layout_container.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:orientation="vertical">
    <FrameLayout
        android:id="@+id/container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</LinearLayout>

Then inflate it in createViewInstance method and define function to add fragment to the container.

@Override
public String getName() {
    return "ExampleFragment";
}
@Override
protected View createViewInstance(ThemedReactContext reactContext) {
    mContext = context;
    LinearLayout root = (LinearLayout) LayoutInflater.from(reactContext)
                .inflate(R.layout.layout_container, null);
    return root;
}

@Nullable
@Override
public Map<String, Integer> getCommandsMap() {
    return MapBuilder.of(
            "create", COMMAND_CREATE
    );

}

@Override
public void receiveCommand(View view, int commandId, @Nullable ReadableArray args) {
    Log.d(TAG, "receiveCommand: " + commandId);
    switch (commandId) {
        case COMMAND_CREATE:
            createFragment();
            break;
    }
}

private void createFragment() {
    MapFragment mapFragment = new MapFragment();
    mContext.getCurrentActivity()
            .getFragmentManager()
            .beginTransaction()
            .add(R.id.container, mapFragment)
            .commit();
}

RN side: create Native UI Component Fragment.js

import {NativeModules, requireNativeComponent, View, UIManager, findNodeHandle} from 'react-native';
const fragmentIFace = {
  name: "Fragment",
  propTypes: {
    ...View.propTypes,
  }
};
const NativeFragment = requireNativeComponent("ExampleFragment", fragmentIFace);
class ExampleFragment extends Component {
  fragment;
  render() {
    return <NativeFragment
      ref={c => this.fragment = c}
      {...this.props} style={{flex: 1}}/>
  }

  create = () => {
    UIManager.dispatchViewManagerCommand(
      findNodeHandle(this.fragment),
      UIManager.MapFragment.Commands.create,
      [] // No args
    )
  }
export default ExampleFragment

then you can use it in RN.

class App extends Component {
    componentDidMount() {
        this.fragment.create();
    }
    render() {
        return (
            <View>
                <ExampleFragment ref={r=>this.fragment = r} />
            </View>
        );
    }
}

Here's the explanation:
In your code, you try to add fragment to the view which cannot be a fragment container.
Usually, View.generateViewId() will return id 0x1 which is duplicated with ReactRootView by coincidence so that your app will not crash. If you set other id to the container view, you will get exception and app will crash because the id of your view can not be found from current app's layout.
You can check it with Layout Inspector in Android Studio.

To prevent from duplication of ids, the best way is defining them in xml. And that's why I inflate a root view from xml.
But if you set id for the root(LinearLayout in my example), the id will be removed by RN when it is added, so the child is necessary.
After RN get the root view(didComponentMount()), then you need to call create() from RN component, and you can see it.

like image 65
jordenchang55 Avatar answered Mar 23 '23 16:03

jordenchang55