Remove YogaLayout ViewGroup (#1318)

Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1318

We deprecated this as part of the Yoga 2.0 release. The last version is still present as part of the `release-v2.0` branch, and was still published, but we are not carrying the code forward. This removes it.

There are a couple followups here:
1. The top-level organization (e.g. a gradle-specific directory called `build-logic` in a multi-language repo) can and should be cleaned up. Yoga Java bindings should be condensed to the `java` folder.
2. We no longer have a sample app excersizing the bindings. We should resolve this by getting the JNI binding UTs working in OSS again.

Reviewed By: cortinico

Differential Revision: D47136243

fbshipit-source-id: 72f74914effde2c895934ac1100adfd305044d46
This commit is contained in:
Nick Gerleman
2023-07-05 22:00:16 -07:00
committed by Facebook GitHub Bot
parent 1b40f05b8c
commit f7324fb71e
34 changed files with 1 additions and 3010 deletions

View File

@@ -1,21 +0,0 @@
# YogaLayout [![Platform](https://img.shields.io/badge/platforms-Android-orange.svg)](https://facebook.github.io/yoga/docs/api/android/) [![Languages](https://img.shields.io/badge/languages-Java-orange.svg)](https://facebook.github.io/yoga/docs/api/android/) [![Download](https://img.shields.io/bintray/v/facebook/maven/com.facebook.yoga.android:yoga-layout.svg)](https://bintray.com/facebook/maven/com.facebook.yoga.android%3Ayoga-layout/_latestVersion)
## Installation
YogaLayout is available via jcenter:
implementation 'com.facebook.yoga.android:yoga-layout:1.16.0'
## Getting Started
Check out the docs [here](https://yogalayout.com/getting-started/standalone/).
We also have a sample project. To try it, clone the repo and run (with a device attached)
buck install -r android/sample
## Contributing
We welcome all pull-requests! At Facebook we sync the open source version of YogaKit daily, so we're always testing the latest changes.
See the CONTRIBUTING file for how to help out.

View File

@@ -1,41 +0,0 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
plugins {
id("com.android.library")
id("publish")
}
android {
namespace 'com.facebook.yoga.android'
compileSdkVersion rootProject.compileSdkVersion
buildToolsVersion rootProject.buildToolsVersion
ndkVersion rootProject.ndkVersion
defaultConfig {
minSdkVersion rootProject.minSdkVersion
targetSdkVersion rootProject.targetSdkVersion
}
compileOptions {
targetCompatibility rootProject.targetCompatibilityVersion
sourceCompatibility rootProject.sourceCompatibilityVersion
}
publishing {
multipleVariants {
withSourcesJar()
withJavadocJar()
includeBuildTypeValues('debug', 'release')
}
}
}
dependencies {
api project(':yoga')
}

View File

@@ -1,14 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (c) Facebook, Inc. and its affiliates.
This source code is licensed under the MIT license found in the
LICENSE file in the root directory of this source tree.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application/>
</manifest>

View File

@@ -1,149 +0,0 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.yoga.android;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import com.facebook.yoga.YogaNode;
import com.facebook.yoga.YogaNodeFactory;
/**
* Much like a {@link YogaLayout}, except this class does not render itself (the container) to the
* screen. As a result, <i>do not use this if you wish the container to have a background or
* foreground</i>. However, all of its children will still render as expected.
*
* <p>
* In practice, this class never added to the View tree, and all its children become children of its
* parent. As a result, all the layout (such as the traversal of the tree) is performed by Yoga
* (and so natively) increasing performance.
*/
public class VirtualYogaLayout extends ViewGroup {
final private List<View> mChildren = new LinkedList<>();
final private Map<View, YogaNode> mYogaNodes = new HashMap<>();
final private YogaNode mYogaNode = YogaNodeFactory.create();
public VirtualYogaLayout(Context context) {
super(context);
}
public VirtualYogaLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public VirtualYogaLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
YogaLayout.LayoutParams lp = new YogaLayout.LayoutParams(context, attrs);
YogaLayout.applyLayoutParams(lp, mYogaNode, this);
}
public YogaNode getYogaNode() {
return mYogaNode;
}
/**
* Called to add a view, creating a new yoga node for it and adding that yoga node to the parent.
* If the child is a {@link VirtualYogaLayout}, we simply transfer all its children to this one
* in a manner that maintains the tree, and add its root to the tree.
*
* @param child the View to add
* @param index the position at which to add it (ignored)
* @param params the layout parameters to apply
*/
@Override
public void addView(View child, int index, ViewGroup.LayoutParams params) {
if (child instanceof VirtualYogaLayout) {
((VirtualYogaLayout) child).transferChildren(this);
final YogaNode childNode = ((VirtualYogaLayout) child).getYogaNode();
mYogaNode.addChildAt(childNode, mYogaNode.getChildCount());
return;
}
YogaNode node = YogaNodeFactory.create();
YogaLayout.LayoutParams lp = new YogaLayout.LayoutParams(params);
YogaLayout.applyLayoutParams(lp, node, child);
node.setData(child);
node.setMeasureFunction(new YogaLayout.ViewMeasureFunction());
mYogaNode.addChildAt(node, mYogaNode.getChildCount());
addView(child, node);
}
/**
* Called to add a view with a corresponding node, but not to change the Yoga tree in any way.
*
* @param child the View to add
* @param node the corresponding yoga node
*/
public void addView(View child, YogaNode node) {
mChildren.add(child);
mYogaNodes.put(child, node);
}
/**
* Gives up children {@code View}s to the parent, maintaining the Yoga tree. This function calls
* {@link YogaLayout#addView(View, YogaNode)} or {@link VirtualYogaLayout#addView(View, YogaNode)}
* on the parent to add the {@code View} without generating new yoga nodes.
*
* @param parent the parent to pass children to (must be a YogaLayout or a VirtualYogaLayout)
*/
protected void transferChildren(ViewGroup parent) {
if (parent instanceof VirtualYogaLayout) {
for (View child : mChildren) {
((VirtualYogaLayout) parent).addView(child, mYogaNodes.get(child));
}
} else if (parent instanceof YogaLayout) {
for (View child : mChildren) {
((YogaLayout) parent).addView(child, mYogaNodes.get(child));
}
} else {
throw new RuntimeException("VirtualYogaLayout cannot transfer children to ViewGroup of type "
+parent.getClass().getCanonicalName()+". Must either be a VirtualYogaLayout or a " +
"YogaLayout.");
}
mChildren.clear();
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
throw new RuntimeException("Attempting to layout a VirtualYogaLayout");
}
@Override
public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
return new YogaLayout.LayoutParams(getContext(), attrs);
}
@Override
protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
return new YogaLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT);
}
@Override
protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
return new YogaLayout.LayoutParams(p);
}
@Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
return p instanceof YogaLayout.LayoutParams;
}
}

View File

@@ -1,816 +0,0 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.yoga.android;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.TypedArray;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.util.AttributeSet;
import android.util.SparseArray;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
import com.facebook.yoga.YogaAlign;
import com.facebook.yoga.YogaConstants;
import com.facebook.yoga.YogaDirection;
import com.facebook.yoga.YogaDisplay;
import com.facebook.yoga.YogaEdge;
import com.facebook.yoga.YogaFlexDirection;
import com.facebook.yoga.YogaJustify;
import com.facebook.yoga.YogaMeasureFunction;
import com.facebook.yoga.YogaMeasureMode;
import com.facebook.yoga.YogaMeasureOutput;
import com.facebook.yoga.YogaNode;
import com.facebook.yoga.YogaNodeFactory;
import com.facebook.yoga.YogaOverflow;
import com.facebook.yoga.YogaPositionType;
import com.facebook.yoga.YogaWrap;
import java.util.HashMap;
import java.util.Map;
/**
* A {@code ViewGroup} based on the Yoga layout engine.
*
* <p>
* This class is designed to be as "plug and play" as possible. That is, you can use it in XML
* like this (note: to use {@code YogaLayout} you need to use the {@link YogaViewLayoutFactory}):
* <p>
* <pre>{@code
* <YogaLayout
* xmlns:android="http://schemas.android.com/apk/res/android"
* xmlns:yoga="http://schemas.android.com/apk/com.facebook.yoga.android"
* android:layout_width="match_owner"
* android:layout_height="match_owner"
* yoga:flex_direction="row"
* yoga:padding_all="10dp"
* >
* <TextView
* android:layout_width="match_owner"
* android:layout_height="match_owner"
* android:text="Hello, World!"
* yoga:flex="1"
* />
* </YogaLayout>
* }</pre>
*
* Under the hood, all views added to this {@code ViewGroup} are laid out using flexbox rules
* and the Yoga engine.
*/
public class YogaLayout extends ViewGroup {
private final Map<View, YogaNode> mYogaNodes;
private final YogaNode mYogaNode;
public YogaLayout(Context context) {
this(context, null, 0);
}
public YogaLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public YogaLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mYogaNode = YogaNodeFactory.create();
mYogaNodes = new HashMap<>();
mYogaNode.setData(this);
mYogaNode.setMeasureFunction(new ViewMeasureFunction());
LayoutParams layoutParams = null;
if (attrs != null) {
layoutParams = new LayoutParams(context, attrs);
} else {
layoutParams = (LayoutParams) generateDefaultLayoutParams();
}
applyLayoutParams(layoutParams, mYogaNode, this);
}
public YogaNode getYogaNode() {
return mYogaNode;
}
public YogaNode getYogaNodeForView(View view) {
return mYogaNodes.get(view);
}
/**
* Adds a child view with the specified layout parameters.
*
* In the typical View is added, this constructs a {@code YogaNode} for this child and applies all
* the {@code yoga:*} attributes. The Yoga node is added to the Yoga tree and the child is added
* to this ViewGroup.
*
* If the child is a {@link YogaLayout} itself, we do not construct a new Yoga node for that
* child, but use its root node instead.
*
* If the child is a {@link VirtualYogaLayout}, we also use its Yoga node, but we also instruct it
* to transfer all of its children to this {@link YogaLayout} while preserving the Yoga tree (so
* that the layout of its children is correct). The {@link VirtualYogaLayout} is then not added
* to the View hierarchy.
*
* <p><strong>Note:</strong> do not invoke this method from
* {@code #draw(android.graphics.Canvas)}, {@code onDraw(android.graphics.Canvas)},
* {@code #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
*
* @param child the child view to add
* @param index the position at which to add the child or -1 to add last
* @param params the layout parameters to set on the child
*/
@Override
public void addView(View child, int index, ViewGroup.LayoutParams params) {
// Internal nodes (which this is now) cannot have measure functions
mYogaNode.setMeasureFunction(null);
if (child instanceof VirtualYogaLayout) {
((VirtualYogaLayout) child).transferChildren(this);
final YogaNode childNode = ((VirtualYogaLayout) child).getYogaNode();
mYogaNode.addChildAt(childNode, mYogaNode.getChildCount());
return;
}
super.addView(child, index, params);
// It is possible that addView is being called as part of a transferal of children, in which
// case we already know about the YogaNode and only need the Android View tree to be aware
// that we now own this child. If so, we don't need to do anything further
if (mYogaNodes.containsKey(child)) {
return;
}
YogaNode childNode;
if (child instanceof YogaLayout) {
childNode = ((YogaLayout) child).getYogaNode();
} else {
if(mYogaNodes.containsKey(child)) {
childNode = mYogaNodes.get(child);
} else {
childNode = YogaNodeFactory.create();
}
childNode.setData(child);
childNode.setMeasureFunction(new ViewMeasureFunction());
}
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
applyLayoutParams(lp, childNode, child);
mYogaNodes.put(child, childNode);
mYogaNode.addChildAt(childNode, mYogaNode.getChildCount());
}
/**
* Adds a view to this {@code ViewGroup} with an already given {@code YogaNode}. Use
* this function if you already have a Yoga node (and perhaps tree) associated with the view you
* are adding, that you would like to preserve.
*
* @param child The view to add
* @param node The Yoga node belonging to the view
*/
public void addView(View child, YogaNode node) {
mYogaNodes.put(child, node);
addView(child);
}
@Override
public void removeView(View view) {
removeViewFromYogaTree(view, false);
super.removeView(view);
}
@Override
public void removeViewAt(int index) {
removeViewFromYogaTree(getChildAt(index), false);
super.removeViewAt(index);
}
@Override
public void removeViewInLayout(View view) {
removeViewFromYogaTree(view, true);
super.removeViewInLayout(view);
}
@Override
public void removeViews(int start, int count) {
for (int i = start; i < start + count; i++) {
removeViewFromYogaTree(getChildAt(i), false);
}
super.removeViews(start, count);
}
@Override
public void removeViewsInLayout(int start, int count) {
for (int i = start; i < start + count; i++) {
removeViewFromYogaTree(getChildAt(i), true);
}
super.removeViewsInLayout(start, count);
}
@Override
public void removeAllViews() {
final int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
removeViewFromYogaTree(getChildAt(i), false);
}
super.removeAllViews();
}
@Override
public void removeAllViewsInLayout() {
final int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
removeViewFromYogaTree(getChildAt(i), true);
}
super.removeAllViewsInLayout();
}
/**
* Marks a particular view as "dirty" and to be relaid out. If the view is not a child of this
* {@link YogaLayout}, the entire tree is traversed to find it.
*
* @param view the view to mark as dirty
*/
public void invalidate(View view) {
if (mYogaNodes.containsKey(view)) {
mYogaNodes.get(view).dirty();
return;
}
final int childCount = mYogaNode.getChildCount();
for (int i = 0; i < childCount; i++) {
final YogaNode yogaNode = mYogaNode.getChildAt(i);
if (yogaNode.getData() instanceof YogaLayout) {
((YogaLayout) yogaNode.getData()).invalidate(view);
}
}
invalidate();
}
private void removeViewFromYogaTree(View view, boolean inLayout) {
final YogaNode node = mYogaNodes.get(view);
if (node == null) {
return;
}
final YogaNode owner = node.getOwner();
for (int i = 0; i < owner.getChildCount(); i++) {
if (owner.getChildAt(i).equals(node)) {
owner.removeChildAt(i);
break;
}
}
node.setData(null);
mYogaNodes.remove(view);
if (inLayout) {
mYogaNode.calculateLayout(YogaConstants.UNDEFINED, YogaConstants.UNDEFINED);
}
}
private void applyLayoutRecursive(YogaNode node, float xOffset, float yOffset) {
View view = (View) node.getData();
if (view != null && view != this) {
if (view.getVisibility() == GONE) {
return;
}
int left = Math.round(xOffset + node.getLayoutX());
int top = Math.round(yOffset + node.getLayoutY());
view.measure(
View.MeasureSpec.makeMeasureSpec(
Math.round(node.getLayoutWidth()),
View.MeasureSpec.EXACTLY),
View.MeasureSpec.makeMeasureSpec(
Math.round(node.getLayoutHeight()),
View.MeasureSpec.EXACTLY));
view.layout(left, top, left + view.getMeasuredWidth(), top + view.getMeasuredHeight());
}
final int childrenCount = node.getChildCount();
for (int i = 0; i < childrenCount; i++) {
if (this.equals(view)) {
applyLayoutRecursive(node.getChildAt(i), xOffset, yOffset);
} else if (view instanceof YogaLayout) {
continue;
} else {
applyLayoutRecursive(
node.getChildAt(i),
xOffset + node.getLayoutX(),
yOffset + node.getLayoutY());
}
}
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
// Either we are a root of a tree, or this function is called by our owner's onLayout, in which
// case our r-l and b-t are the size of our node.
if (!(getParent() instanceof YogaLayout)) {
createLayout(
MeasureSpec.makeMeasureSpec(r - l, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(b - t, MeasureSpec.EXACTLY));
}
applyLayoutRecursive(mYogaNode, 0, 0);
}
/**
* This function is mostly unneeded, because Yoga is doing the measuring. Hence we only need to
* return accurate results if we are the root.
*
* @param widthMeasureSpec the suggested specification for the width
* @param heightMeasureSpec the suggested specification for the height
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (!(getParent() instanceof YogaLayout)) {
createLayout(widthMeasureSpec, heightMeasureSpec);
}
setMeasuredDimension(
Math.round(mYogaNode.getLayoutWidth()),
Math.round(mYogaNode.getLayoutHeight()));
}
private void createLayout(int widthMeasureSpec, int heightMeasureSpec) {
final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
if (heightMode == MeasureSpec.EXACTLY) {
mYogaNode.setHeight(heightSize);
}
if (widthMode == MeasureSpec.EXACTLY) {
mYogaNode.setWidth(widthSize);
}
if (heightMode == MeasureSpec.AT_MOST) {
mYogaNode.setMaxHeight(heightSize);
}
if (widthMode == MeasureSpec.AT_MOST) {
mYogaNode.setMaxWidth(widthSize);
}
mYogaNode.calculateLayout(YogaConstants.UNDEFINED, YogaConstants.UNDEFINED);
}
/**
* Applies the layout parameters to the YogaNode. That is, this function is a translator from
* {@code yoga:X="Y"} to {@code yogaNode.setX(Y);}, with some reasonable defaults.
*
* <p>
* If the SDK version is high enough, and the {@code yoga:direction} is not set on
* the component, the direction (LTR or RTL) is set according to the locale.
*
* <p>
* The attributes {@code padding_top}, {@code padding_right} etc. default to those of the view's
* drawable background, if it has one.
*
* @param layoutParameters The source set of params
* @param node The destination node
*/
protected static void applyLayoutParams(LayoutParams layoutParameters, YogaNode node, View view) {
// JELLY_BEAN_MR1 (17) is the first version supporting getLayoutDirection()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
Configuration configuration = view.getResources().getConfiguration();
if (configuration.getLayoutDirection() == LAYOUT_DIRECTION_RTL) {
node.setDirection(YogaDirection.RTL);
}
}
Drawable background = view.getBackground();
if (background != null) {
final Rect backgroundPadding = new Rect();
if (background.getPadding(backgroundPadding)) {
node.setPadding(YogaEdge.LEFT, backgroundPadding.left);
node.setPadding(YogaEdge.TOP, backgroundPadding.top);
node.setPadding(YogaEdge.RIGHT, backgroundPadding.right);
node.setPadding(YogaEdge.BOTTOM, backgroundPadding.bottom);
}
}
for (int i = 0; i < layoutParameters.numericAttributes.size(); i++) {
final int attribute = layoutParameters.numericAttributes.keyAt(i);
final float value = layoutParameters.numericAttributes.valueAt(i);
if (attribute == R.styleable.yoga_yg_alignContent) {
node.setAlignContent(YogaAlign.fromInt(Math.round(value)));
} else if (attribute == R.styleable.yoga_yg_alignItems) {
node.setAlignItems(YogaAlign.fromInt(Math.round(value)));
} else if (attribute == R.styleable.yoga_yg_alignSelf) {
node.setAlignSelf(YogaAlign.fromInt(Math.round(value)));
} else if (attribute == R.styleable.yoga_yg_aspectRatio) {
node.setAspectRatio(value);
} else if (attribute == R.styleable.yoga_yg_borderLeft) {
node.setBorder(YogaEdge.LEFT, value);
} else if (attribute == R.styleable.yoga_yg_borderTop) {
node.setBorder(YogaEdge.TOP, value);
} else if (attribute == R.styleable.yoga_yg_borderRight) {
node.setBorder(YogaEdge.RIGHT, value);
} else if (attribute == R.styleable.yoga_yg_borderBottom) {
node.setBorder(YogaEdge.BOTTOM, value);
} else if (attribute == R.styleable.yoga_yg_borderStart) {
node.setBorder(YogaEdge.START, value);
} else if (attribute == R.styleable.yoga_yg_borderEnd) {
node.setBorder(YogaEdge.END, value);
} else if (attribute == R.styleable.yoga_yg_borderHorizontal) {
node.setBorder(YogaEdge.HORIZONTAL, value);
} else if (attribute == R.styleable.yoga_yg_borderVertical) {
node.setBorder(YogaEdge.VERTICAL, value);
} else if (attribute == R.styleable.yoga_yg_borderAll) {
node.setBorder(YogaEdge.ALL, value);
} else if (attribute == R.styleable.yoga_yg_direction) {
node.setDirection(YogaDirection.fromInt(Math.round(value)));
} else if (attribute == R.styleable.yoga_yg_display) {
node.setDisplay(YogaDisplay.fromInt(Math.round(value)));
} else if (attribute == R.styleable.yoga_yg_flex) {
node.setFlex(value);
} else if (attribute == R.styleable.yoga_yg_flexBasis) {
node.setFlexBasis(value);
} else if (attribute == R.styleable.yoga_yg_flexDirection) {
node.setFlexDirection(YogaFlexDirection.fromInt(Math.round(value)));
} else if (attribute == R.styleable.yoga_yg_flexGrow) {
node.setFlexGrow(value);
} else if (attribute == R.styleable.yoga_yg_flexShrink) {
node.setFlexShrink(value);
} else if (attribute == R.styleable.yoga_yg_height) {
node.setHeight(value);
} else if (attribute == R.styleable.yoga_yg_marginLeft) {
node.setMargin(YogaEdge.LEFT, value);
} else if (attribute == R.styleable.yoga_yg_justifyContent) {
node.setJustifyContent(YogaJustify.fromInt(Math.round(value)));
} else if (attribute == R.styleable.yoga_yg_marginTop) {
node.setMargin(YogaEdge.TOP, value);
} else if (attribute == R.styleable.yoga_yg_marginRight) {
node.setMargin(YogaEdge.RIGHT, value);
} else if (attribute == R.styleable.yoga_yg_marginBottom) {
node.setMargin(YogaEdge.BOTTOM, value);
} else if (attribute == R.styleable.yoga_yg_marginStart) {
node.setMargin(YogaEdge.START, value);
} else if (attribute == R.styleable.yoga_yg_marginEnd) {
node.setMargin(YogaEdge.END, value);
} else if (attribute == R.styleable.yoga_yg_marginHorizontal) {
node.setMargin(YogaEdge.HORIZONTAL, value);
} else if (attribute == R.styleable.yoga_yg_marginVertical) {
node.setMargin(YogaEdge.VERTICAL, value);
} else if (attribute == R.styleable.yoga_yg_marginAll) {
node.setMargin(YogaEdge.ALL, value);
} else if (attribute == R.styleable.yoga_yg_maxHeight) {
node.setMaxHeight(value);
} else if (attribute == R.styleable.yoga_yg_maxWidth) {
node.setMaxWidth(value);
} else if (attribute == R.styleable.yoga_yg_minHeight) {
node.setMinHeight(value);
} else if (attribute == R.styleable.yoga_yg_minWidth) {
node.setMinWidth(value);
} else if (attribute == R.styleable.yoga_yg_overflow) {
node.setOverflow(YogaOverflow.fromInt(Math.round(value)));
} else if (attribute == R.styleable.yoga_yg_paddingLeft) {
node.setPadding(YogaEdge.LEFT, value);
} else if (attribute == R.styleable.yoga_yg_paddingTop) {
node.setPadding(YogaEdge.TOP, value);
} else if (attribute == R.styleable.yoga_yg_paddingRight) {
node.setPadding(YogaEdge.RIGHT, value);
} else if (attribute == R.styleable.yoga_yg_paddingBottom) {
node.setPadding(YogaEdge.BOTTOM, value);
} else if (attribute == R.styleable.yoga_yg_paddingStart) {
node.setPadding(YogaEdge.START, value);
} else if (attribute == R.styleable.yoga_yg_paddingEnd) {
node.setPadding(YogaEdge.END, value);
} else if (attribute == R.styleable.yoga_yg_paddingHorizontal) {
node.setPadding(YogaEdge.HORIZONTAL, value);
} else if (attribute == R.styleable.yoga_yg_paddingVertical) {
node.setPadding(YogaEdge.VERTICAL, value);
} else if (attribute == R.styleable.yoga_yg_paddingAll) {
node.setPadding(YogaEdge.ALL, value);
} else if (attribute == R.styleable.yoga_yg_positionLeft) {
node.setPosition(YogaEdge.LEFT, value);
} else if (attribute == R.styleable.yoga_yg_positionTop) {
node.setPosition(YogaEdge.TOP, value);
} else if (attribute == R.styleable.yoga_yg_positionRight) {
node.setPosition(YogaEdge.RIGHT, value);
} else if (attribute == R.styleable.yoga_yg_positionBottom) {
node.setPosition(YogaEdge.BOTTOM, value);
} else if (attribute == R.styleable.yoga_yg_positionStart) {
node.setPosition(YogaEdge.START, value);
} else if (attribute == R.styleable.yoga_yg_positionEnd) {
node.setPosition(YogaEdge.END, value);
} else if (attribute == R.styleable.yoga_yg_positionHorizontal) {
node.setPosition(YogaEdge.HORIZONTAL, value);
} else if (attribute == R.styleable.yoga_yg_positionVertical) {
node.setPosition(YogaEdge.VERTICAL, value);
} else if (attribute == R.styleable.yoga_yg_positionAll) {
node.setPosition(YogaEdge.ALL, value);
} else if (attribute == R.styleable.yoga_yg_positionType) {
node.setPositionType(YogaPositionType.fromInt(Math.round(value)));
} else if (attribute == R.styleable.yoga_yg_width) {
node.setWidth(value);
} else if (attribute == R.styleable.yoga_yg_wrap) {
node.setWrap(YogaWrap.fromInt(Math.round(value)));
}
}
for (int i = 0; i < layoutParameters.stringAttributes.size(); i++) {
final int attribute = layoutParameters.stringAttributes.keyAt(i);
final String value = layoutParameters.stringAttributes.valueAt(i);
if (value.equals("auto")) {
if (attribute == R.styleable.yoga_yg_marginLeft) {
node.setMarginAuto(YogaEdge.LEFT);
} else if (attribute == R.styleable.yoga_yg_marginTop) {
node.setMarginAuto(YogaEdge.TOP);
} else if (attribute == R.styleable.yoga_yg_marginRight) {
node.setMarginAuto(YogaEdge.RIGHT);
} else if (attribute == R.styleable.yoga_yg_marginBottom) {
node.setMarginAuto(YogaEdge.BOTTOM);
} else if (attribute == R.styleable.yoga_yg_marginStart) {
node.setMarginAuto(YogaEdge.START);
} else if (attribute == R.styleable.yoga_yg_marginEnd) {
node.setMarginAuto(YogaEdge.END);
} else if (attribute == R.styleable.yoga_yg_marginHorizontal) {
node.setMarginAuto(YogaEdge.HORIZONTAL);
} else if (attribute == R.styleable.yoga_yg_marginVertical) {
node.setMarginAuto(YogaEdge.VERTICAL);
} else if (attribute == R.styleable.yoga_yg_marginAll) {
node.setMarginAuto(YogaEdge.ALL);
}
}
if (value.endsWith("%")) {
final float numericValue = Float.parseFloat(value.substring(0, value.length()-1));
if (attribute == R.styleable.yoga_yg_flexBasis) {
node.setFlexBasisPercent(numericValue);
} else if (attribute == R.styleable.yoga_yg_height) {
node.setHeightPercent(numericValue);
} else if (attribute == R.styleable.yoga_yg_marginLeft) {
node.setMarginPercent(YogaEdge.LEFT, numericValue);
} else if (attribute == R.styleable.yoga_yg_marginTop) {
node.setMarginPercent(YogaEdge.TOP, numericValue);
} else if (attribute == R.styleable.yoga_yg_marginRight) {
node.setMarginPercent(YogaEdge.RIGHT, numericValue);
} else if (attribute == R.styleable.yoga_yg_marginBottom) {
node.setMarginPercent(YogaEdge.BOTTOM, numericValue);
} else if (attribute == R.styleable.yoga_yg_marginStart) {
node.setMarginPercent(YogaEdge.START, numericValue);
} else if (attribute == R.styleable.yoga_yg_marginEnd) {
node.setMarginPercent(YogaEdge.END, numericValue);
} else if (attribute == R.styleable.yoga_yg_marginHorizontal) {
node.setMarginPercent(YogaEdge.HORIZONTAL, numericValue);
} else if (attribute == R.styleable.yoga_yg_marginVertical) {
node.setMarginPercent(YogaEdge.VERTICAL, numericValue);
} else if (attribute == R.styleable.yoga_yg_marginAll) {
node.setMarginPercent(YogaEdge.ALL, numericValue);
} else if (attribute == R.styleable.yoga_yg_maxHeight) {
node.setMaxHeightPercent(numericValue);
} else if (attribute == R.styleable.yoga_yg_maxWidth) {
node.setMaxWidthPercent(numericValue);
} else if (attribute == R.styleable.yoga_yg_minHeight) {
node.setMinHeightPercent(numericValue);
} else if (attribute == R.styleable.yoga_yg_minWidth) {
node.setMinWidthPercent(numericValue);
} else if (attribute == R.styleable.yoga_yg_paddingLeft) {
node.setPaddingPercent(YogaEdge.LEFT, numericValue);
} else if (attribute == R.styleable.yoga_yg_paddingTop) {
node.setPaddingPercent(YogaEdge.TOP, numericValue);
} else if (attribute == R.styleable.yoga_yg_paddingRight) {
node.setPaddingPercent(YogaEdge.RIGHT, numericValue);
} else if (attribute == R.styleable.yoga_yg_paddingBottom) {
node.setPaddingPercent(YogaEdge.BOTTOM, numericValue);
} else if (attribute == R.styleable.yoga_yg_paddingStart) {
node.setPaddingPercent(YogaEdge.START, numericValue);
} else if (attribute == R.styleable.yoga_yg_paddingEnd) {
node.setPaddingPercent(YogaEdge.END, numericValue);
} else if (attribute == R.styleable.yoga_yg_paddingHorizontal) {
node.setPaddingPercent(YogaEdge.HORIZONTAL, numericValue);
} else if (attribute == R.styleable.yoga_yg_paddingVertical) {
node.setPaddingPercent(YogaEdge.VERTICAL, numericValue);
} else if (attribute == R.styleable.yoga_yg_paddingAll) {
node.setPaddingPercent(YogaEdge.ALL, numericValue);
} else if (attribute == R.styleable.yoga_yg_positionLeft) {
node.setPositionPercent(YogaEdge.LEFT, numericValue);
} else if (attribute == R.styleable.yoga_yg_positionTop) {
node.setPositionPercent(YogaEdge.TOP, numericValue);
} else if (attribute == R.styleable.yoga_yg_positionRight) {
node.setPositionPercent(YogaEdge.RIGHT, numericValue);
} else if (attribute == R.styleable.yoga_yg_positionBottom) {
node.setPositionPercent(YogaEdge.BOTTOM, numericValue);
} else if (attribute == R.styleable.yoga_yg_positionStart) {
node.setPositionPercent(YogaEdge.START, numericValue);
} else if (attribute == R.styleable.yoga_yg_positionEnd) {
node.setPositionPercent(YogaEdge.END, numericValue);
} else if (attribute == R.styleable.yoga_yg_positionHorizontal) {
node.setPositionPercent(YogaEdge.HORIZONTAL, numericValue);
} else if (attribute == R.styleable.yoga_yg_positionVertical) {
node.setPositionPercent(YogaEdge.VERTICAL, numericValue);
} else if (attribute == R.styleable.yoga_yg_positionAll) {
node.setPositionPercent(YogaEdge.ALL, numericValue);
} else if (attribute == R.styleable.yoga_yg_width) {
node.setWidthPercent(numericValue);
}
}
}
}
@Override
public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
return new YogaLayout.LayoutParams(getContext(), attrs);
}
@Override
protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
return new YogaLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT);
}
@Override
protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
return new YogaLayout.LayoutParams(p);
}
@Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
return p instanceof LayoutParams;
}
/**
* {@code YogaLayout.LayoutParams} are used by views to tell {@link YogaLayout} how they want to
* be laid out. More precisely, the specify the yoga parameters of the view.
*
* <p>
* This is actually mostly a wrapper around a {@code SparseArray} that holds a mapping between
* styleable id's ({@code R.styleable.yoga_yg_*}) and the float of their values. In cases where
* the value is an enum or an integer, they should first be cast to int (with rounding) before
* using.
*/
public static class LayoutParams extends ViewGroup.LayoutParams {
/**
* A mapping from attribute keys ({@code R.styleable.yoga_yg_*}) to the float of their values.
* For attributes like position_percent_left (float), this is the native type. For attributes
* like align_self (enums), the integer enum value is cast (rounding is used on the other side
* to prevent precision errors). Dimension attributes are stored as float pixels.
*/
SparseArray<Float> numericAttributes;
/**
* A mapping from attribute keys ({@code R.styleable.yoga_yg_*}) with string values to those
* strings. This is used for values such as "auto".
*/
SparseArray<String> stringAttributes;
/**
* Constructs a set of layout params from a source set. In the case that the source set is
* actually a {@link YogaLayout.LayoutParams}, we can copy all the yoga attributes. Otherwise
* we start with a blank slate.
*
* @param source The layout params to copy from
*/
public LayoutParams(ViewGroup.LayoutParams source) {
super(source);
if (source instanceof LayoutParams) {
numericAttributes = ((LayoutParams) source).numericAttributes.clone();
stringAttributes = ((LayoutParams) source).stringAttributes.clone();
} else {
numericAttributes = new SparseArray<>();
stringAttributes = new SparseArray<>();
// Negative values include MATCH_PARENT and WRAP_CONTENT
if (source.width >= 0) {
numericAttributes.put(R.styleable.yoga_yg_width, (float) width);
}
if (source.height >= 0) {
numericAttributes.put(R.styleable.yoga_yg_height, (float) height);
}
}
}
/**
* Constructs a set of layout params, given width and height specs. In this case, we can set
* the {@code yoga:width} and {@code yoga:height} if we are given them explicitly. If other
* options (such as {@code match_owner} or {@code wrap_content} are given, then the owner
* LayoutParams will store them, and we deal with them during layout. (see
* {@link YogaLayout#createLayout})
*
* @param width the requested width, either a pixel size, {@code WRAP_CONTENT} or
* {@code MATCH_PARENT}.
* @param height the requested height, either a pixel size, {@code WRAP_CONTENT} or
* {@code MATCH_PARENT}.
*/
public LayoutParams(int width, int height) {
super(width, height);
numericAttributes = new SparseArray<>();
stringAttributes = new SparseArray<>();
// Negative values include MATCH_PARENT and WRAP_CONTENT
if (width >= 0) {
numericAttributes.put(R.styleable.yoga_yg_width, (float) width);
}
if (height >= 0) {
numericAttributes.put(R.styleable.yoga_yg_height, (float) height);
}
}
/**
* Constructs a set of layout params, given attributes. Grabs all the {@code yoga:*}
* defined in {@code ALL_YOGA_ATTRIBUTES} and collects the ones that are set in {@code attrs}.
*
* @param context the application environment
* @param attrs the set of attributes from which to extract the yoga specific attributes
*/
public LayoutParams(Context context, AttributeSet attrs) {
super(context, attrs);
numericAttributes = new SparseArray<>();
stringAttributes = new SparseArray<>();
final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.yoga);
// Negative values include MATCH_PARENT and WRAP_CONTENT
if (width >= 0) {
numericAttributes.put(R.styleable.yoga_yg_width, (float) width);
}
if (height >= 0) {
numericAttributes.put(R.styleable.yoga_yg_height, (float) height);
}
final int attributeCount = a.getIndexCount();
for (int i = 0; i < attributeCount; i++) {
final int attribute = a.getIndex(i);
final TypedValue val = new TypedValue();
a.getValue(attribute, val);
if (val.type == TypedValue.TYPE_DIMENSION) {
numericAttributes.put(
attribute,
(float) a.getDimensionPixelSize(attribute, 0));
} else if (val.type == TypedValue.TYPE_STRING) {
stringAttributes.put(attribute, a.getString(attribute));
} else {
numericAttributes.put(attribute, a.getFloat(attribute, 0));
}
}
a.recycle();
}
}
/**
* Wrapper around measure function for yoga leaves.
*/
public static class ViewMeasureFunction implements YogaMeasureFunction {
/**
* A function to measure leaves of the Yoga tree. Yoga needs some way to know how large
* elements want to be. This function passes that question directly through to the relevant
* {@code View}'s measure function.
*
* @param node The yoga node to measure
* @param width The suggested width from the owner
* @param widthMode The type of suggestion for the width
* @param height The suggested height from the owner
* @param heightMode The type of suggestion for the height
* @return A measurement output ({@code YogaMeasureOutput}) for the node
*/
public long measure(
YogaNode node,
float width,
YogaMeasureMode widthMode,
float height,
YogaMeasureMode heightMode) {
final View view = (View) node.getData();
if (view == null || view instanceof YogaLayout) {
return YogaMeasureOutput.make(0, 0);
}
final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(
(int) width,
viewMeasureSpecFromYogaMeasureMode(widthMode));
final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(
(int) height,
viewMeasureSpecFromYogaMeasureMode(heightMode));
view.measure(widthMeasureSpec, heightMeasureSpec);
return YogaMeasureOutput.make(view.getMeasuredWidth(), view.getMeasuredHeight());
}
private int viewMeasureSpecFromYogaMeasureMode(YogaMeasureMode mode) {
if (mode == YogaMeasureMode.AT_MOST) {
return MeasureSpec.AT_MOST;
} else if (mode == YogaMeasureMode.EXACTLY) {
return MeasureSpec.EXACTLY;
} else {
return MeasureSpec.UNSPECIFIED;
}
}
}
}

View File

@@ -1,57 +0,0 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.yoga.android;
import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
/**
* A layout inflater factory. This provides our custom {@link YogaViewLayoutFactory#onCreateView}
* to the XML inflation system, allowing us to replace XML tags.
*/
public class YogaViewLayoutFactory implements LayoutInflater.Factory {
private static YogaViewLayoutFactory sYogaViewLayoutFactory;
/**
* Obtains (and initialises if necessary) the singleton {@link YogaViewLayoutFactory}.
*
* @return The singleton instance
*/
public static YogaViewLayoutFactory getInstance() {
if (sYogaViewLayoutFactory == null) {
sYogaViewLayoutFactory = new YogaViewLayoutFactory();
}
return sYogaViewLayoutFactory;
}
YogaViewLayoutFactory() {}
/**
* Hook for inflating from a LayoutInflater. This hook replaces the cumbersome
* {@code com.facebook.etc.YogaLayout} with simply {@code YogaLayout} in your XML and the same
* with {@code VirtualYogaLayout}.
*
* @param name Tag name to be inflated.
* @param context The context the view is being created in.
* @param attrs Inflation attributes as specified in XML file.
*
* @return View Newly created view. Return null for the default behavior.
*/
@Override
public View onCreateView(String name, Context context, AttributeSet attrs) {
if (YogaLayout.class.getSimpleName().equals(name)) {
return new YogaLayout(context, attrs);
}
if (VirtualYogaLayout.class.getSimpleName().equals(name)) {
return new VirtualYogaLayout(context, attrs);
}
return null;
}
}

View File

@@ -1,150 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<!--
Copyright (c) Facebook, Inc. and its affiliates.
This source code is licensed under the MIT license found in the
LICENSE file in the root directory of this source tree.
-->
<resources>
<declare-styleable name="yoga">
<attr name="yg_alignContent" format="enum">
<enum name="auto" value="0"/>
<enum name="flex_start" value="1"/>
<enum name="center" value="2"/>
<enum name="flex_end" value="3"/>
<enum name="stretch" value="4"/>
<enum name="baseline" value="5"/>
</attr>
<attr name="yg_alignItems" format="enum">
<enum name="auto" value="0"/>
<enum name="flex_start" value="1"/>
<enum name="center" value="2"/>
<enum name="flex_end" value="3"/>
<enum name="stretch" value="4"/>
<enum name="baseline" value="5"/>
</attr>
<attr name="yg_alignSelf" format="enum">
<enum name="auto" value="0"/>
<enum name="flex_start" value="1"/>
<enum name="center" value="2"/>
<enum name="flex_end" value="3"/>
<enum name="stretch" value="4"/>
<enum name="baseline" value="5"/>
</attr>
<attr name="yg_aspectRatio" format="float"/>
<attr name="yg_borderLeft" format="dimension"/>
<attr name="yg_borderTop" format="dimension"/>
<attr name="yg_borderRight" format="dimension"/>
<attr name="yg_borderBottom" format="dimension"/>
<attr name="yg_borderStart" format="dimension"/>
<attr name="yg_borderEnd" format="dimension"/>
<attr name="yg_borderHorizontal" format="dimension"/>
<attr name="yg_borderVertical" format="dimension"/>
<attr name="yg_borderAll" format="dimension"/>
<attr name="yg_direction" format="enum">
<enum name="inherit" value="0"/>
<enum name="ltr" value="1"/>
<enum name="rtl" value="2"/>
</attr>
<attr name="yg_display" format="enum">
<enum name="flex" value="0"/>
<enum name="none" value="1"/>
</attr>
<attr name="yg_flex" format="float"/>
<attr name="yg_flexBasis" format="float|string"/>
<attr name="yg_flexDirection" format="enum">
<enum name="column" value="0"/>
<enum name="column_reverse" value="1"/>
<enum name="row" value="2"/>
<enum name="row_reverse" value="3"/>
</attr>
<attr name="yg_flexGrow" format="float"/>
<attr name="yg_flexShrink" format="float"/>
<attr name="yg_height" format="dimension|string"/>
<attr name="yg_justifyContent" format="enum">
<enum name="flex_start" value="0"/>
<enum name="center" value="1"/>
<enum name="flex_end" value="2"/>
<enum name="space_between" value="3"/>
<enum name="space_around" value="4"/>
</attr>
<attr name="yg_marginLeft" format="dimension|string"/>
<attr name="yg_marginTop" format="dimension|string"/>
<attr name="yg_marginRight" format="dimension|string"/>
<attr name="yg_marginBottom" format="dimension|string"/>
<attr name="yg_marginStart" format="dimension|string"/>
<attr name="yg_marginEnd" format="dimension|string"/>
<attr name="yg_marginHorizontal" format="dimension|string"/>
<attr name="yg_marginVertical" format="dimension|string"/>
<attr name="yg_marginAll" format="dimension|string"/>
<attr name="yg_maxHeight" format="dimension|string"/>
<attr name="yg_maxWidth" format="dimension|string"/>
<attr name="yg_minHeight" format="dimension|string"/>
<attr name="yg_minWidth" format="dimension|string"/>
<attr name="yg_overflow" format="enum">
<enum name="visible" value="0"/>
<enum name="hidden" value="1"/>
<enum name="scroll" value="2"/>
</attr>
<attr name="yg_paddingLeft" format="dimension|string"/>
<attr name="yg_paddingTop" format="dimension|string"/>
<attr name="yg_paddingRight" format="dimension|string"/>
<attr name="yg_paddingBottom" format="dimension|string"/>
<attr name="yg_paddingStart" format="dimension|string"/>
<attr name="yg_paddingEnd" format="dimension|string"/>
<attr name="yg_paddingHorizontal" format="dimension|string"/>
<attr name="yg_paddingVertical" format="dimension|string"/>
<attr name="yg_paddingAll" format="dimension|string"/>
<attr name="yg_positionLeft" format="dimension|string"/>
<attr name="yg_positionTop" format="dimension|string"/>
<attr name="yg_positionRight" format="dimension|string"/>
<attr name="yg_positionBottom" format="dimension|string"/>
<attr name="yg_positionStart" format="dimension|string"/>
<attr name="yg_positionEnd" format="dimension|string"/>
<attr name="yg_positionHorizontal" format="dimension|string"/>
<attr name="yg_positionVertical" format="dimension|string"/>
<attr name="yg_positionAll" format="dimension|string"/>
<attr name="yg_positionType" format="enum">
<!-- "static" is a reserved keyword
<enum name="static" value="0"/>
-->
<enum name="relative" value="1"/>
<enum name="absolute" value="2"/>
<enum name="position_static" value="0"/>
<enum name="position_relative" value="1"/>
<enum name="position_absolute" value="2"/>
</attr>
<attr name="yg_width" format="dimension|string"/>
<attr name="yg_wrap" format="enum">
<enum name="no_wrap" value="0"/>
<enum name="wrap" value="1"/>
</attr>
</declare-styleable>
</resources>

View File

@@ -1,34 +0,0 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
plugins {
id("com.android.application")
}
android {
namespace 'com.facebook.yoga.sample'
compileSdkVersion rootProject.compileSdkVersion
buildToolsVersion rootProject.buildToolsVersion
ndkVersion rootProject.ndkVersion
defaultConfig {
minSdkVersion rootProject.minSdkVersion
targetSdkVersion rootProject.targetSdkVersion
}
compileOptions {
targetCompatibility rootProject.targetCompatibilityVersion
sourceCompatibility rootProject.sourceCompatibilityVersion
}
}
dependencies {
implementation project(':yoga-layout')
implementation("androidx.appcompat:appcompat:1.6.1")
implementation("com.facebook.soloader:soloader:0.10.4")
}

View File

@@ -1,28 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
android:versionCode="1"
android:versionName="1.0">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application
android:allowBackup="false"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/NoTitleBarWhiteBG">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".BenchmarkActivity"
android:exported="false" />
</application>
</manifest>

View File

@@ -1,121 +0,0 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.yoga.sample;
import android.content.Intent;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;
import androidx.fragment.app.FragmentTransaction;
import androidx.viewpager.widget.ViewPager;
import com.facebook.yoga.android.YogaViewLayoutFactory;
public class BenchmarkActivity extends AppCompatActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
LayoutInflater.from(this).setFactory(YogaViewLayoutFactory.getInstance());
super.onCreate(savedInstanceState);
setContentView(R.layout.benchmark_select_layout);
ViewPager viewPager = findViewById(R.id.viewpager);
viewPager.setAdapter(new PagerAdapter(getSupportFragmentManager()));
final ActionBar actionBar = getSupportActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
ActionBar.TabListener tabListener = new ActionBar.TabListener() {
@Override
public void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft) {
ViewPager viewPager = findViewById(R.id.viewpager);
viewPager.setCurrentItem(tab.getPosition());
}
@Override
public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft) {
}
@Override
public void onTabReselected(ActionBar.Tab tab, FragmentTransaction ft) {
}
};
actionBar.addTab(
actionBar.newTab()
.setText("Inflate")
.setTabListener(tabListener));
actionBar.addTab(
actionBar.newTab()
.setText("Measure")
.setTabListener(tabListener));
actionBar.addTab(
actionBar.newTab()
.setText("Layout")
.setTabListener(tabListener));
viewPager.setOnPageChangeListener(
new ViewPager.SimpleOnPageChangeListener() {
@Override
public void onPageSelected(int position) {
// When swiping between pages, select the
// corresponding tab.
actionBar.setSelectedNavigationItem(position);
}
});
viewPager.setOffscreenPageLimit(3);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.action_bar_benchmark, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// There is only one option
Intent intent = new Intent(this, MainActivity.class);
startActivity(intent);
this.finish();
return true;
}
public static class PagerAdapter extends FragmentPagerAdapter {
public PagerAdapter(FragmentManager fm) {
super(fm);
}
@Override
public Fragment getItem(int i) {
switch (i) {
case 0:
return new BenchmarkInflate();
case 1:
return new BenchmarkMeasure();
default:
return new BenchmarkLayout();
}
}
@Override
public int getCount() {
return 3;
}
}
}

View File

@@ -1,195 +0,0 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.yoga.sample;
import java.io.File;
import java.io.PrintWriter;
import java.lang.Math;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Date;
import android.content.Context;
import android.util.Log;
import android.os.Environment;
import static java.util.Collections.sort;
public class BenchmarkAggregator {
private final int GRAPH_WIDTH = 30;
private final int GRAPH_HEIGHT = 6;
private List<Long> times;
private boolean tracing;
private long lastTraceStart;
private boolean statsFresh;
private long mean;
private long variance;
private long stddev;
private long min;
private long max;
private long p10;
private long p50;
private long p90;
private String name;
public BenchmarkAggregator(String name) {
times = new ArrayList<>();
tracing = false;
this.name = name;
}
public void startTrace() {
if (tracing) {
throw new RuntimeException("Cannot start trace while running previous one");
}
tracing = true;
lastTraceStart = System.nanoTime();
}
public void endTrace() {
if (!tracing) {
throw new RuntimeException("Cannot stop trace if none are running!");
}
times.add(System.nanoTime() - lastTraceStart);
tracing = false;
statsFresh = false;
}
private void computeStats() {
if (statsFresh) {
return;
}
sort(times);
min = Long.MAX_VALUE;
max = -1;
mean = 0;
for (long f: times) {
mean += f;
if (f < min) {
min = f;
}
if (f > max) {
max = f;
}
}
mean /= times.size();
variance = 0;
for (long f: times) {
variance += (f-mean)*(f-mean);
}
variance /= times.size();
stddev = (long) Math.sqrt((double) variance);
p10 = times.get(times.size()*10/100);
p50 = times.get(times.size()*50/100);
p90 = times.get(times.size()*90/100);
statsFresh = true;
}
public String toString() {
computeStats();
return String.format(
"%s:\n" +
"| %d samples\n" +
"| Mean %.3f\u00B1%.3fms\n" + // plusminus
"| Min %.3fms ; Max %.3fms\n" +
"| p10 %.3fms ; p50 %.3fms ; p90 %.3fms\n" +
"%s",
name,
times.size(),
mean/10e6,
stddev/10e6,
min/10e6,
max/10e6,
p10/10e6,
p50/10e6,
p90/10e6,
makeGraph());
}
private String makeGraph() {
char canvas[][] = new char[GRAPH_HEIGHT][GRAPH_WIDTH];
for (int i = 0; i < GRAPH_HEIGHT; i++)
for (int j = 0; j < GRAPH_WIDTH; j++)
canvas[i][j] = ' ';
long bucketSize = (p90 - p10) / GRAPH_WIDTH+1;
int bucketCount[] = new int[GRAPH_WIDTH];
for (long time : times) {
if (time<p90 && time>p10) {
bucketCount[(int) ((time - p10) / bucketSize)]++;
}
}
int maxBucket = 0;
for (int i = 0; i < GRAPH_WIDTH; i++)
if (bucketCount[i] > maxBucket) {
maxBucket = bucketCount[i];
}
for (int i = 0; i < GRAPH_HEIGHT; i++)
for (int j = 0; j < GRAPH_WIDTH; j++)
if (i < bucketCount[j] * GRAPH_HEIGHT / maxBucket) {
canvas[i][j] = 'Z';
}
String graph = new String();
for (int i = 0; i < GRAPH_HEIGHT; i++)
{
int percentage = 100 * (GRAPH_HEIGHT - i - 1) * maxBucket / (times.size() * GRAPH_HEIGHT);
graph += String.format("| %2d%% ", percentage);
for (int j = 0; j < GRAPH_WIDTH; j++)
graph += canvas[GRAPH_HEIGHT-1-i][j];
graph += '\n';
}
graph += "| p10";
for (int i = 0; i < GRAPH_WIDTH-6; i++)
graph += " ";
graph += "p90\n";
return graph;
}
/**
* Dumps the collected times to a file on the device. This allows us to grab the raw data
* and perform more in-depth analysis.
*/
public void dump(Context context) {
String state = Environment.getExternalStorageState();
if (!Environment.MEDIA_MOUNTED.equals(state)) {
Log.e("YogaLayoutBenchmark","No external file storage");
return;
}
SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss");
String filename = format.format(new Date()) + "_" + name.replace(' ','_');
File file = new File(context.getExternalFilesDir(
Environment.DIRECTORY_DOCUMENTS), filename);
try {
PrintWriter printWriter = new PrintWriter(file);
for (long l : times) {
printWriter.println(l);
}
printWriter.close();
Log.i("YogaLayoutBenchmark","Benchmark data saved in "+file.getPath());
} catch (java.io.IOException e) {
Log.e("YogaLayoutBenchmark", "Could not save benchmark data", e);
}
}
}

View File

@@ -1,116 +0,0 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.yoga.sample;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.LinearLayout;
import android.widget.Spinner;
import android.widget.TextView;
import androidx.fragment.app.Fragment;
import com.facebook.yoga.android.YogaLayout;
import java.util.Random;
public class BenchmarkFragment extends Fragment implements AdapterView.OnItemSelectedListener {
private LayoutInflater mInflater;
protected com.facebook.yoga.android.YogaLayout rootLayout;
protected int yogaLayout;
protected int linearLayout;
static final Random random = new Random();
static void randomizeText(View root) {
if (root instanceof TextView) {
((TextView) root).setText("" + random.nextInt(1000));
((TextView) root).setTextSize(10 + random.nextInt(20));
ViewParent parent = root.getParent();
if (parent instanceof YogaLayout) {
((YogaLayout) parent).invalidate(root);
}
} else if (root instanceof ViewGroup) {
for (int i = 0; i < ((ViewGroup) root).getChildCount(); i++) {
randomizeText(((ViewGroup) root).getChildAt(i));
}
}
}
public BenchmarkFragment() {
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(
LayoutInflater inflater,
ViewGroup container,
Bundle savedInstanceState) {
mInflater = inflater;
rootLayout = (YogaLayout) inflater.inflate(
R.layout.benchmark_fragment,
container,
false);
Spinner benchmarkSelect = rootLayout.findViewById(R.id.benchmarkSelect);
String[] items = new String[]{"Basic", "Typical", "Nested"};
ArrayAdapter<String> adapter = new ArrayAdapter<>(getActivity(), android.R.layout.simple_spinner_dropdown_item, items);
benchmarkSelect.setAdapter(adapter);
benchmarkSelect.setOnItemSelectedListener(this);
return rootLayout;
}
@Override
public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
switch (pos) {
case 0:
yogaLayout = R.layout.benchmark_layout_1;
linearLayout = R.layout.benchmark_layout_1_linear;
break;
case 1:
yogaLayout = R.layout.benchmark_layout_2;
linearLayout = R.layout.benchmark_layout_2_linear;
break;
case 2:
default:
yogaLayout = R.layout.benchmark_layout_3;
linearLayout = R.layout.benchmark_layout_3_linear;
break;
}
updatePreview();
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
yogaLayout = R.layout.benchmark_layout_1;
linearLayout = R.layout.benchmark_layout_1_linear;
updatePreview();
}
private void updatePreview() {
LinearLayout previewLayout = rootLayout.findViewById(R.id.preview);
View v = mInflater.inflate(yogaLayout, rootLayout, false);
v.setLayoutParams(new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.MATCH_PARENT));
previewLayout.removeAllViews();
previewLayout.addView(v);
}
}

View File

@@ -1,61 +0,0 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.yoga.sample;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
public class BenchmarkInflate extends BenchmarkFragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
Button b = rootLayout.findViewById(R.id.btn);
b.setOnClickListener(v -> startBenchmark());
return rootLayout;
}
protected void startBenchmark() {
LayoutInflater inflater = LayoutInflater.from(getActivity());
TextView textView = rootLayout.findViewById(R.id.text);
final int ITERATIONS = 500;
inflater.inflate(yogaLayout, null);
inflater.inflate(linearLayout, null);
BenchmarkAggregator yogaInflationAggregator = new BenchmarkAggregator("Yoga Inflate");
BenchmarkAggregator linearInflationAggregator = new BenchmarkAggregator("Linear Inflate");
for (int i = 0; i < ITERATIONS; i++) {
yogaInflationAggregator.startTrace();
inflater.inflate(yogaLayout, null);
yogaInflationAggregator.endTrace();
linearInflationAggregator.startTrace();
inflater.inflate(linearLayout, null);
linearInflationAggregator.endTrace();
}
textView.setText(
yogaInflationAggregator.toString() +
"\n" +
linearInflationAggregator.toString());
Log.i(
"YogaLayoutBenchmark",
yogaInflationAggregator.toString() +
"\n" +
linearInflationAggregator.toString());
rootLayout.invalidate();
}
}

View File

@@ -1,70 +0,0 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.yoga.sample;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
public class BenchmarkLayout extends BenchmarkFragment {
@Override
public View onCreateView(
LayoutInflater inflater,
ViewGroup container,
Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
Button b = rootLayout.findViewById(R.id.btn);
b.setOnClickListener(v -> startBenchmark());
return rootLayout;
}
protected void startBenchmark() {
LayoutInflater inflater = LayoutInflater.from(getActivity());
TextView textView = rootLayout.findViewById(R.id.text);
final int ITERATIONS = 500;
BenchmarkAggregator yogaInflationAggregator = new BenchmarkAggregator("Yoga Layout");
BenchmarkAggregator linearInflationAggregator = new BenchmarkAggregator("Linear Layout");
View yogaView = inflater.inflate(yogaLayout, null);
View linearView = inflater.inflate(linearLayout, null);
for (int i = 0; i < ITERATIONS; i++) {
randomizeText(yogaView);
randomizeText(linearView);
yogaView.measure(
View.MeasureSpec.makeMeasureSpec(1000, View.MeasureSpec.EXACTLY),
View.MeasureSpec.makeMeasureSpec(1000, View.MeasureSpec.EXACTLY));
linearView.measure(
View.MeasureSpec.makeMeasureSpec(1000, View.MeasureSpec.EXACTLY),
View.MeasureSpec.makeMeasureSpec(1000, View.MeasureSpec.EXACTLY));
yogaInflationAggregator.startTrace();
yogaView.layout(0, 0, yogaView.getMeasuredWidth(), yogaView.getMeasuredHeight());
yogaInflationAggregator.endTrace();
linearInflationAggregator.startTrace();
linearView.layout(0, 0, linearView.getMeasuredWidth(), linearView.getMeasuredHeight());
linearInflationAggregator.endTrace();
}
textView.setText(
yogaInflationAggregator +
"\n" +
linearInflationAggregator);
Log.i(
"YogaLayoutBenchmark",
yogaInflationAggregator +
"\n" +
linearInflationAggregator);
}
}

View File

@@ -1,74 +0,0 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.yoga.sample;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import java.util.Random;
public class BenchmarkMeasure extends BenchmarkFragment {
@Override
public View onCreateView(
LayoutInflater inflater,
ViewGroup container,
Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
Button b = rootLayout.findViewById(R.id.btn);
b.setOnClickListener(v -> startBenchmark());
return rootLayout;
}
protected void startBenchmark() {
LayoutInflater inflater = LayoutInflater.from(getActivity());
TextView textView = (TextView) rootLayout.findViewById(R.id.text);
Random random = new Random();
final int ITERATIONS = 500;
BenchmarkAggregator yogaMeasureAggregator = new BenchmarkAggregator("Yoga Measure");
BenchmarkAggregator linearMeasureAggregator = new BenchmarkAggregator("Linear Measure");
View yogaView = inflater.inflate(yogaLayout, null);
View linearView = inflater.inflate(linearLayout, null);
for (int i = 0; i < ITERATIONS; i++) {
randomizeText(yogaView);
randomizeText(linearView);
yogaMeasureAggregator.startTrace();
yogaView.measure(
View.MeasureSpec.makeMeasureSpec(1000, View.MeasureSpec.EXACTLY),
View.MeasureSpec.makeMeasureSpec(1000, View.MeasureSpec.EXACTLY));
yogaMeasureAggregator.endTrace();
linearMeasureAggregator.startTrace();
linearView.measure(
View.MeasureSpec.makeMeasureSpec(1000, View.MeasureSpec.EXACTLY),
View.MeasureSpec.makeMeasureSpec(1000, View.MeasureSpec.EXACTLY));
linearMeasureAggregator.endTrace();
}
textView.setText(
yogaMeasureAggregator.toString() +
"\n" +
linearMeasureAggregator.toString());
Log.i(
"YogaLayoutBenchmark",
yogaMeasureAggregator.toString() +
"\n" +
linearMeasureAggregator.toString());
yogaMeasureAggregator.dump(getActivity());
linearMeasureAggregator.dump(getActivity());
}
}

View File

@@ -1,54 +0,0 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.yoga.sample;
import android.content.Intent;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import com.facebook.soloader.SoLoader;
import com.facebook.yoga.android.YogaViewLayoutFactory;
/**
* An activity to show off Yoga in Android. This activity shows a simple layout (defined in
* {@code main_layout.xml}) that shows off the awesome functionality of the Yoga layout engine
* as well as some optimisations on layout systems that it facilitates.
*/
public class MainActivity extends AppCompatActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
LayoutInflater.from(this).setFactory(YogaViewLayoutFactory.getInstance());
super.onCreate(savedInstanceState);
SoLoader.init(this, false);
setContentView(R.layout.main_layout);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.action_bar_home, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
// There is only one option
Intent intent = new Intent(this, BenchmarkActivity.class);
startActivity(intent);
this.finish();
return true;
}
}

View File

@@ -1,17 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<!--
Copyright 2014-present, Facebook, Inc.
All rights reserved.
This source code is licensed under the license found in the
LICENSE-examples file in the root directory of this source tree.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle"
>
<solid
android:color="@color/yoga_grey"
/>
</shape>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

View File

@@ -1,34 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<!--
Copyright 2014-present, Facebook, Inc.
All rights reserved.
This source code is licensed under the license found in the
LICENSE-examples file in the root directory of this source tree.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle"
>
<corners
android:radius="4dp"
/>
<stroke
android:width="1dp"
android:color="@color/children_stroke"
/>
<padding
android:top="6dp"
android:bottom="6dp"
android:left="8dp"
android:right="8dp"
/>
<solid
android:color="@color/children_background"
/>
</shape>

View File

@@ -1,50 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<YogaLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/rt"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:yg_flexDirection="column"
>
<VirtualYogaLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:yg_flexDirection="row"
app:yg_height="50dp"
>
<Button
android:id="@+id/btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:text="Run benchmark"
app:yg_flex="1"
/>
<Spinner
android:id="@+id/benchmarkSelect"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:spinnerMode="dropdown"
/>
</VirtualYogaLayout>
<TextView
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textSize="10sp"
android:fontFamily="monospace"
app:yg_flex="1"
/>
<View
android:layout_width="match_parent"
android:layout_height="2dp"
android:background="@android:color/darker_gray"
/>
<LinearLayout
android:id="@+id/preview"
android:layout_width="match_parent"
android:layout_height="60dp"
android:orientation="horizontal"
/>
</YogaLayout>

View File

@@ -1,30 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<YogaLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="60dp"
app:yg_flexDirection="row"
app:yg_alignItems="center"
>
<View
android:layout_width="50dp"
android:layout_height="50dp"
android:background="@color/yoga_blue"
app:yg_flex="0"
app:yg_marginAll="5dp"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/child_1_text"
app:yg_flex="0"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/child_2_text"
app:yg_flex="1"
app:yg_marginHorizontal="5dp"
/>
</YogaLayout>

View File

@@ -1,28 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center"
>
<ImageView
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_margin="5dp"
android:src="@drawable/ic_launcher"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/child_1_text"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:text="@string/child_2_text"
/>
</LinearLayout>

View File

@@ -1,104 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<YogaLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="60dp"
app:yg_flexDirection="column"
app:yg_alignItems="stretch"
>
<View
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/yoga_blue"
app:yg_flex="1"
/>
<VirtualYogaLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:height="40dp"
app:yg_flexDirection="row"
app:yg_alignItems="stretch"
>
<View
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/yoga_blue"
app:yg_marginAll="10dp"
app:yg_aspectRatio="1"
/>
<VirtualYogaLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:height="40dp"
app:yg_flexDirection="column"
app:yg_alignItems="stretch"
app:yg_flex="1"
app:yg_justifyContent="space_around"
>
<VirtualYogaLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:height="8dp"
app:yg_flexDirection="row"
app:yg_alignItems="stretch"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/child_1_text"
android:textSize="5sp"
app:yg_flex="1"
/>
<View
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/yoga_blue"
app:yg_aspectRatio="1"
/>
</VirtualYogaLayout>
<VirtualYogaLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:height="8dp"
app:yg_flexDirection="row"
app:yg_alignItems="stretch"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/child_1_text"
android:textSize="5sp"
app:yg_flex="1"
/>
<View
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/yoga_blue"
app:yg_aspectRatio="1"
/>
</VirtualYogaLayout>
<VirtualYogaLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:height="8dp"
app:yg_flexDirection="row"
app:yg_alignItems="stretch"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/child_1_text"
android:textSize="5sp"
app:yg_flex="1"
/>
<View
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/yoga_blue"
app:yg_aspectRatio="1"
/>
</VirtualYogaLayout>
</VirtualYogaLayout>
</VirtualYogaLayout>
</YogaLayout>

View File

@@ -1,95 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="60dp"
android:orientation="vertical"
>
<View
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:src="@drawable/ic_launcher"
android:background="@color/yoga_blue"
android:layout_weight="1"
/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="40dp"
android:orientation="horizontal"
>
<View
android:layout_width="20dp"
android:layout_height="20dp"
android:src="@drawable/ic_launcher"
android:background="@color/yoga_blue"
android:layout_margin="10dp"
/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="40dp"
android:layout_weight="1"
android:orientation="vertical"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="8dp"
android:orientation="horizontal"
android:layout_marginTop="4dp"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/child_1_text"
android:textSize="5sp"
android:layout_weight="1"
/>
<View
android:layout_width="8dp"
android:layout_height="8dp"
android:src="@drawable/ic_launcher"
android:background="@color/yoga_blue"
/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="8dp"
android:orientation="horizontal"
android:layout_marginTop="4dp"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/child_1_text"
android:textSize="5sp"
android:layout_weight="1"
/>
<View
android:layout_width="8dp"
android:layout_height="8dp"
android:src="@drawable/ic_launcher"
android:background="@color/yoga_blue"
/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="8dp"
android:orientation="horizontal"
android:layout_marginTop="4dp"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/child_1_text"
android:textSize="5sp"
android:layout_weight="1"
/>
<View
android:layout_width="8dp"
android:layout_height="8dp"
android:src="@drawable/ic_launcher"
android:background="@color/yoga_blue"
/>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</LinearLayout>

View File

@@ -1,206 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<YogaLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:yg_alignItems="center"
app:yg_flexDirection="row"
app:yg_justifyContent="center"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/yoga_blue"
app:yg_marginLeft="10dp"
/>
<VirtualYogaLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:yg_flexDirection="column"
app:yg_flex="1"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/yoga_grey"
app:yg_marginLeft="10dp"
/>
<VirtualYogaLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:yg_flexDirection="row"
app:yg_flex="1"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/yoga_grey"
app:yg_marginLeft="10dp"
/>
<VirtualYogaLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:yg_flexDirection="row"
app:yg_flex="1"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/yoga_blue"
app:yg_marginLeft="10dp"
/>
<VirtualYogaLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:yg_flexDirection="row"
app:yg_flex="1"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/yoga_grey"
app:yg_marginLeft="10dp"
/>
<VirtualYogaLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:yg_flexDirection="row"
app:yg_flex="1"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/yoga_grey"
app:yg_marginLeft="10dp"
/>
<VirtualYogaLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:yg_flexDirection="column"
app:yg_flex="1"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/yoga_blue"
app:yg_marginLeft="10dp"
/>
<VirtualYogaLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:yg_flexDirection="row"
app:yg_flex="1"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/yoga_grey"
app:yg_marginLeft="10dp"
/>
<VirtualYogaLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:yg_flexDirection="row"
app:yg_flex="1"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/yoga_grey"
app:yg_marginLeft="10dp"
/>
<VirtualYogaLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:yg_flexDirection="row"
app:yg_flex="1"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/yoga_blue"
app:yg_marginLeft="10dp"
/>
<VirtualYogaLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:yg_flexDirection="row"
app:yg_flex="1"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/yoga_grey"
app:yg_marginLeft="10dp"
/>
<VirtualYogaLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:yg_flexDirection="column"
app:yg_flex="1"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/yoga_grey"
app:yg_marginLeft="10dp"
/>
<VirtualYogaLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:yg_flexDirection="row"
app:yg_flex="1"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/yoga_blue"
app:yg_marginLeft="10dp"
/>
<VirtualYogaLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:yg_flexDirection="row"
app:yg_flex="1"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/yoga_grey"
app:yg_marginLeft="10dp"
/>
<VirtualYogaLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:yg_flexDirection="row"
app:yg_flex="1"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/yoga_grey"
app:yg_marginLeft="10dp"
/>
<VirtualYogaLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:yg_flexDirection="row"
app:yg_flex="1"
>
</VirtualYogaLayout>
</VirtualYogaLayout>
</VirtualYogaLayout>
</VirtualYogaLayout>
</VirtualYogaLayout>
</VirtualYogaLayout>
</VirtualYogaLayout>
</VirtualYogaLayout>
</VirtualYogaLayout>
</VirtualYogaLayout>
</VirtualYogaLayout>
</VirtualYogaLayout>
</VirtualYogaLayout>
</VirtualYogaLayout>
</VirtualYogaLayout>
</YogaLayout>

View File

@@ -1,204 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:background="@color/yoga_blue"
/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:background="@color/yoga_grey"
/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="horizontal"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:background="@color/yoga_grey"
/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="horizontal"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:background="@color/yoga_blue"
/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="horizontal"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:background="@color/yoga_grey"
/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="horizontal"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:background="@color/yoga_grey"
/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:background="@color/yoga_blue"
/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="horizontal"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:background="@color/yoga_grey"
/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="horizontal"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:background="@color/yoga_grey"
/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="horizontal"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:background="@color/yoga_blue"
/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="horizontal"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:background="@color/yoga_grey"
/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:background="@color/yoga_grey"
/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="horizontal"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:background="@color/yoga_blue"
/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="horizontal"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:background="@color/yoga_grey"
/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="horizontal"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:background="@color/yoga_grey"
/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="horizontal"
>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</LinearLayout>

View File

@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<androidx.viewpager.widget.ViewPager
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>

View File

@@ -1,137 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<!--
Copyright 2014-present, Facebook, Inc.
All rights reserved.
This source code is licensed under the license found in the
LICENSE-examples file in the root directory of this source tree.
-->
<YogaLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<YogaLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/sample_children_background"
app:yg_marginHorizontal="10dp"
app:yg_marginTop="5dp"
app:yg_flexDirection="row"
app:yg_alignItems="center"
>
<ImageView
android:layout_width="50dp"
android:layout_height="50dp"
android:src="@drawable/ic_launcher"
app:yg_flex="0"
/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/child_1_text"
android:textColor="@color/children_text"
app:yg_flex="1"
app:yg_marginStart="8dp"
/>
</YogaLayout>
<YogaLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/sample_children_background"
app:yg_marginHorizontal="10dp"
app:yg_marginTop="5dp"
app:yg_flexDirection="row"
app:yg_alignItems="center"
>
<ImageView
android:layout_width="50dp"
android:layout_height="50dp"
android:src="@drawable/ic_launcher"
app:yg_flex="0"
/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/child_2_text"
android:textColor="@color/children_text"
app:yg_flex="1"
app:yg_marginStart="8dp"
/>
</YogaLayout>
<YogaLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/sample_children_background"
app:yg_marginHorizontal="10dp"
app:yg_marginTop="5dp"
app:yg_flexDirection="row"
app:yg_alignItems="center"
>
<ImageView
android:layout_width="50dp"
android:layout_height="50dp"
android:src="@drawable/ic_launcher"
app:yg_flex="0"
/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/child_3_text"
android:textColor="@color/children_text"
app:yg_flex="1"
app:yg_marginStart="8dp"
/>
</YogaLayout>
<YogaLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/sample_children_background"
app:yg_marginHorizontal="10dp"
app:yg_marginTop="5dp"
app:yg_flexDirection="row"
app:yg_alignItems="center"
>
<ImageView
android:layout_width="50dp"
android:layout_height="50dp"
android:src="@drawable/ic_launcher"
app:yg_flex="0"
/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/child_4_text"
android:textColor="@color/children_text"
app:yg_flex="1"
app:yg_marginStart="8dp"
/>
</YogaLayout>
<YogaLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/sample_children_background"
app:yg_marginHorizontal="10dp"
app:yg_marginTop="5dp"
app:yg_flexDirection="row"
app:yg_alignItems="center"
>
<ImageView
android:layout_width="50dp"
android:layout_height="50dp"
android:src="@drawable/ic_launcher"
app:yg_flex="0"
/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/child_5_text"
android:textColor="@color/children_text"
app:yg_flex="1"
app:yg_marginStart="10dp"
/>
</YogaLayout>
</YogaLayout>

View File

@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_home"
android:title="@string/home"
app:showAsAction="always" />
</menu>

View File

@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_benchmark"
android:title="@string/benchmark"
app:showAsAction="always" />
</menu>

View File

@@ -1,20 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2014-present, Facebook, Inc.
All rights reserved.
This source code is licensed under the license found in the
LICENSE-examples file in the root directory of this source tree.
-->
<resources>
<color name="yoga_grey">#FF303846</color>
<color name="yoga_blue">#FF97DCCF</color>
<color name="children_background">#FFFFFFFF</color>
<color name="children_stroke">#665890ff</color>
<color name="children_text">#FF23355b</color>
</resources>

View File

@@ -1,35 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2014-present, Facebook, Inc.
All rights reserved.
This source code is licensed under the license found in the
LICENSE-examples file in the root directory of this source tree.
-->
<resources>
<string name="app_name">Yoga</string>
<string
name="child_1_text"
description="Placeholder text for the first element in the layout"
>Hello. I am Yoga!</string>
<string
name="child_2_text"
description="Placeholder text for the second element in the layout"
>I am a layout engine!</string>
<string
name="child_3_text"
description="Placeholder text for the third element in the layout"
>I run natively.</string>
<string
name="child_4_text"
description="Placeholder text for the fourth element in the layout"
>So I\'m fast.</string>
<string
name="child_5_text"
description="Placeholder text for the fifth element in the layout"
>Who are you?</string>
<string name="home">Home</string>
<string name="benchmark">Benchmark</string>
</resources>

View File

@@ -1,23 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2014-present, Facebook, Inc.
All rights reserved.
This source code is licensed under the license found in the
LICENSE-examples file in the root directory of this source tree.
-->
<resources>
<style name="NoTitleBarWhiteBG" parent="Theme.AppCompat.Light">
<item name="android:actionBarStyle">@style/MyActionBar</item>
<item name="android:textDirection">locale</item>
</style>
<style name="MyActionBar" parent="Widget.AppCompat.Light.ActionBar">
<item name="android:titleTextStyle">@style/MyTitleText</item>
<item name="android:background">@drawable/action_bar_background</item>
</style>
<style name="MyTitleText" parent="TextAppearance.AppCompat.Widget.ActionBar.Title">
<item name="android:textColor">@color/yoga_blue</item>
</style>
</resources>

View File

@@ -15,14 +15,12 @@ pluginManagement {
plugins { id("com.gradle.enterprise").version("3.7.1") }
include(":sample", ":yoga", ":yoga-layout")
include(":yoga")
includeBuild("build-logic")
project(":yoga").projectDir = file("java")
project(":yoga-layout").projectDir = file("android")
rootProject.name = "yoga-github"
// If you specify a file inside gradle/gradle-enterprise.gradle.kts