8

I've built a very simple native Android UI component and I want to update the size of its child view when a button from my react native project is clicked. To be more precise, when this button is clicked then I send a command to my SimpleViewManager which in turn calls the resizeLayout() of my custom view.

I can verify that the resizeLayout() is properly called but the layout is not resized until I rotate the phone. Obviously, changing the device's orientation triggers the draw() of my custom view but so does the invalidate() which I explicitly call.

Other layout changes like changing the background color instead of resizing it work fine.

My custom component looks like this:

public class CustomComponent extends RelativeLayout {
    public CustomComponent(Context context) {
        super(context);
        init();
    }

    public CustomComponent(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public CustomComponent(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        inflate(getContext(), R.layout.simple_layout, this);
    }

    public void resizeLayout(){
        LinearLayout childLayout = (LinearLayout) findViewById(R.id.child_layout);
        RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) childLayout.getLayoutParams();
        layoutParams.height = 50;
        layoutParams.width= 50;
        childLayout.setLayoutParams(layoutParams);

        invalidate();
    }
}

and the simple_layout.xml looks like this:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/root_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:id="@+id/child_layout"
        android:layout_width="100dp"
        android:layout_height="50dp"
        android:background="#ffee11"/>
</RelativeLayout>

Any help would be greatly appreciated.

Georgios
  • 4,764
  • 35
  • 48

2 Answers2

21

After a couple of days of searching, I finally found the answer in this old reported issue in React Native's repo.

This solution is the same as the ones used in ReactPicker.java and ReactToolbar.java. I had to put the following code in my CustomComponent and after that there is no need to even call requestLayout() or invalidate(). The changes are propagated as soon as I update my layout's LayoutParams.

@Override
public void requestLayout() {
    super.requestLayout();

    // The spinner relies on a measure + layout pass happening after it calls requestLayout().
    // Without this, the widget never actually changes the selection and doesn't call the
    // appropriate listeners. Since we override onLayout in our ViewGroups, a layout pass never
    // happens after a call to requestLayout, so we simulate one here.
    post(measureAndLayout);
}

private final Runnable measureAndLayout = new Runnable() {
    @Override
    public void run() {
        measure(MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.EXACTLY),
                MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.EXACTLY));
        layout(getLeft(), getTop(), getRight(), getBottom());
    }
};
Georgios
  • 4,764
  • 35
  • 48
  • Thanks. Helped me solve issue of custom-added-using-java views not getting updated in the hierarchy. (just put your custom views in a container with the above method overrides) – Venryx Dec 17 '16 at 08:55
  • Oh actually, for my case there's one more step apparently: after adding the above container view (which I had inherit from RelativeLayout), call ```container.layout(0, 0, width, height);``` on it. – Venryx Dec 17 '16 at 09:03
  • This helped me a lot, our library was stuck at width:0, height:0 because apparently requestLayout in ReactNative app is not very useful. Adding a bounty. – Hugo Gresse Oct 12 '17 at 15:23
  • This really helped me! Thanks! – uiltonsantos Jun 04 '18 at 14:26
  • @George: I am also try to use jwplayer with react-native and i am facing same issue in ios. Have you and idea how to fixed it. – Bhavesh Jariwala Dec 21 '18 at 14:08
  • @BhaveshJariwala no, sorry. On iOS it worked just fine for me. Try asking a new question and if you send me the link I'll see what I've done differently. – Georgios Dec 22 '18 at 18:40
  • @George: I will create a separate issue and add code there. But how can i send you link? If you can share ios and react-native code then . it will be helpful – Bhavesh Jariwala Dec 22 '18 at 18:45
  • @George: Here is task that you ask me to create: https://stackoverflow.com/questions/53096720/how-to-make-jwplayer-fullscreen-in-react-native All the details mentioned in that task. can you please look in to it. – Bhavesh Jariwala Dec 28 '18 at 07:18
  • 2
    I can't believe that in 2019, React Native still doesn't have a mention in their docs about this issue. I wasted hours on this. – John D. Oct 09 '19 at 21:57
0

Try running

requestLayout();

instead of invalidate.

This post also has some good information on view lifecycle.

Usage of forceLayout(), requestLayout() and invalidate()

I would also recommend changing your root element in your view xml to

<merge>

that way your CustomComponent class doesn't have a RelativeLayout as its immediate child, unless that's what you were going for.

Community
  • 1
  • 1
ootinii
  • 1,795
  • 20
  • 15
  • Hello and thank you for your answer. Using `` is actually a good idea. However, I've already tried calling all the above methods both in my CustomComponent as well as on the childLayout. The code works fine in an Android project. The problem appears when I use it in combination with React Native. – Georgios Oct 03 '16 at 18:18