Skip to content

Instantly share code, notes, and snippets.

@mpost
Last active February 17, 2018 05:19
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save mpost/b94275f4321ae0a50973e7cca9d5466c to your computer and use it in GitHub Desktop.
Save mpost/b94275f4321ae0a50973e7cca9d5466c to your computer and use it in GitHub Desktop.
Integrate the Facebook Yoga layout (https://facebook.github.io/yoga/) into the Android layout system as a YogaLayout ViewGroup. The YogaLayout exposes the underlying yoga specific child layout properties on its LayoutParams object as a YogaNode object.
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import com.facebook.yoga.YogaMeasureFunction;
import com.facebook.yoga.YogaMeasureMode;
import com.facebook.yoga.YogaMeasureOutput;
import com.facebook.yoga.YogaNode;
import com.facebook.yoga.YogaNodeAPI;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
@SuppressWarnings( "WrongConstant" )
public class YogaLayout extends ViewGroup {
private static final int LAST_POSITION = -1;
private final MeasureFunction measureFunction;
private final YogaNode node;
public static class LayoutParams extends ViewGroup.LayoutParams {
public final YogaNode node;
public LayoutParams( int width, int height ) {
super( width, height );
node = new YogaNode();
}
public LayoutParams( ViewGroup.LayoutParams params ) {
super( params );
node = new YogaNode();
}
}
public YogaLayout( @NonNull Context context, @Nullable AttributeSet attrs ) {
super( context, attrs );
setLayoutParams( generateDefaultLayoutParams() );
node = new YogaNode();
measureFunction = new MeasureFunction();
}
public YogaNode getNode() {
return node;
}
@Override
protected void onMeasure( int widthMeasureSpec, int heightMeasureSpec ) {
node.setWidth( MeasureSpec.getSize( widthMeasureSpec ) );
node.setHeight( MeasureSpec.getSize( heightMeasureSpec ) );
node.calculateLayout();
int layoutWidth = Math.round( node.getLayoutWidth() );
int layoutHeight = Math.round( node.getLayoutHeight() );
setMeasuredDimension( layoutWidth, layoutHeight );
}
@Override
protected void onLayout( boolean changed, int left, int top, int right, int bottom ) {
for( int i = 0; i < getChildCount(); i++ ) {
View child = getChildAt( i );
YogaNode childNode = getChildNode( child );
int childLeft = Math.round( childNode.getLayoutX() );
int childTop = Math.round( childNode.getLayoutY() );
int childRight = Math.round( childLeft + childNode.getLayoutWidth() );
int childBottom = Math.round( childTop + childNode.getLayoutHeight() );
child.layout( childLeft, childTop, childRight, childBottom );
}
}
@Override
public void addView( View child, int index, ViewGroup.LayoutParams params ) {
super.addView( child, index, params );
YogaNode childNode = getChildNode( child );
childNode.setData( child );
node.addChildAt( childNode, index == LAST_POSITION ? node.getChildCount() : index );
}
private YogaNode getChildNode( View child ) {
YogaNode childNode;
if( child instanceof YogaLayout ) {
childNode = ( ( YogaLayout )child ).getNode();
} else {
childNode = ( ( LayoutParams )child.getLayoutParams() ).node;
childNode.setMeasureFunction( measureFunction );
}
return childNode;
}
@Override
public void removeViewAt( int index ) {
node.getChildAt( index ).setData( null );
node.removeChildAt( index );
super.removeViewAt( index );
}
@Override
protected YogaLayout.LayoutParams generateDefaultLayoutParams() {
return new YogaLayout.LayoutParams( WRAP_CONTENT, WRAP_CONTENT );
}
@Override
protected ViewGroup.LayoutParams generateLayoutParams( ViewGroup.LayoutParams params ) {
return new YogaLayout.LayoutParams( params );
}
private static class MeasureFunction implements YogaMeasureFunction {
@Override
public long measure( YogaNodeAPI node,
float width, YogaMeasureMode widthMode,
float height, YogaMeasureMode heightMode ) {
Object data = node.getData();
if( data instanceof View ) {
View view = ( View )data;
view.measure(
MeasureSpec.makeMeasureSpec( Math.round( width ), widthMode.intValue() ),
MeasureSpec.makeMeasureSpec( Math.round( height ), heightMode.intValue() ) );
int measuredWidth = view.getMeasuredWidth();
int measuredHeight = view.getMeasuredHeight();
return YogaMeasureOutput.make( measuredWidth, measuredHeight );
}
return 0;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment