鸿蒙开源三方组件--跨平台自适应布局yoga组件

开源
文章由鸿蒙社区产出,想要了解更多内容请前往:51CTO和华为官方战略合作共建的鸿蒙技术社区https://harmonyos.51cto.com

[[396344]]

想了解更多内容,请访问:

51CTO和华为官方合作共建的鸿蒙技术社区

https://harmonyos.51cto.com

介绍

yoga是facebook打造的一个跨IOS、Android、Window平台在内的布局引擎,兼容Flexbox布局方式,让界面更加简单。

Yoga官网:https://facebook.github.io/yoga/

官网上描述的特性包括:

  • 完全兼容Flexbox布局,遵循W3C的规范
  • 支持java、C#、Objective-C、C四种语言
  • 底层代码使用C语言编写,性能不是问题
  • 支持流行框架如React Native

目前在已开源的鸿蒙组件(https://gitee.com/openharmony-tpc/yoga)的功能现状如下:

  • native层和接口已经打通
  • 支持自定义xml属性来控制布局(通过YogaLayout)
  • 设置布局中不支持Image控件(onDrawCanvas暂不支持主动回调,所以yoga没办法扫描到它),请使用Text控件替代
  • 不支持VirtualYogaLayout

如何使用

首先我们在MainAbility中定义界面路由。

  1. public class MainAbility extends Ability { 
  2.     @Override 
  3.     public void onStart(Intent intent) { 
  4.         super.onStart(intent); 
  5.         super.setMainRoute(MainAbilitySlice.class.getName()); 
  6.         addActionRoute("action.dydrawnode.slice", DynamicsDrawNodeSlice.class.getName()); 
  7.         addActionRoute("action.showrow.slice", ShowRowAbilitySlice.class.getName()); 
  8.         addActionRoute("action.inflate.slice", BenchmarkInflateAbilitySlice.class.getName()); 
  9.     } 

然后我们来到MainAbilitySlice,其实就是做了一个向其他界面跳转的动作,并提前加载yoga的so库。

  1. public class MainAbilitySlice extends AbilitySlice { 
  2.  
  3.  
  4.  
  5.     static { 
  6.         System.loadLibrary("yoga"); 
  7.         System.loadLibrary("yogacore"); 
  8.         System.loadLibrary("fb"); 
  9.     } 
  10.  
  11.  
  12.  
  13.     @Override 
  14.     public void onStart(Intent intent) { 
  15.         super.onStart(intent); 
  16.         setUIContent(ResourceTable.Layout_main_layout); 
  17.  
  18.         Button btn0= (Button) findComponentById(ResourceTable.Id_btn_1); 
  19.         btn0.setClickedListener(component -> { 
  20.             Intent intent1 = new Intent(); 
  21.             Operation operation = new Intent.OperationBuilder() 
  22.                     .withAction("action.dydrawnode.slice"
  23.                     .build(); 
  24.             intent1.setOperation(operation); 
  25.             startAbilityForResult(intent1, 1); 
  26.         }); 
  27.  
  28.         Button btn2= (Button) findComponentById(ResourceTable.Id_btn_2); 
  29.         btn2.setClickedListener(component -> { 
  30.             Intent intent1 = new Intent(); 
  31.             Operation operation = new Intent.OperationBuilder() 
  32.                     .withAction("action.showrow.slice"
  33.                     .build(); 
  34.             intent1.setOperation(operation); 
  35.             startAbilityForResult(intent1, 1); 
  36.         }); 
  37.  
  38.         Button btn1= (Button) findComponentById(ResourceTable.Id_btn_3); 
  39.         btn1.setClickedListener(component -> { 
  40.             Intent intent1 = new Intent(); 
  41.             Operation operation = new Intent.OperationBuilder() 
  42.                     .withAction("action.inflate.slice"
  43.                     .build(); 
  44.             intent1.setOperation(operation); 
  45.             startAbilityForResult(intent1, 1); 
  46.         }); 
  47.  
  48.     } 
  49.  
  50.     @Override 
  51.     public void onActive() { 
  52.         super.onActive(); 
  53.     } 
  54.  
  55.     @Override 
  56.     public void onForeground(Intent intent) { 
  57.         super.onForeground(intent); 
  58.     } 
  59.  
  60.  

第一个演示界面

这里yoga向我们展示了动态布局的能力,效果图如下:

实现的代码如下:

  1. public class DynamicsDrawNodeSlice extends AbilitySlice { 
  2.  
  3.  
  4.     private static final int VIEW_WIDTH = 200; 
  5.     private static final int VIEW_HEIGHT = 200; 
  6.  
  7.     private ArrayList<Component> mViewList = new ArrayList<>(); 
  8.     private ArrayList<YogaNode> mYogaNodeList = new ArrayList<>(); 
  9.  
  10.     private int[][] colors = new int[][]{ 
  11.             new int[]{0xff6200ea, 0xff651fff, 0xff7c4dff, 0xffb388ff}, 
  12.             new int[]{0xffd50000, 0xffff1744, 0xffff5252, 0xffff8a80}, 
  13.             new int[]{0xffc51162, 0xfff50057, 0xffff4081, 0xffff80ab}, 
  14.             new int[]{0xffaa00ff, 0xffd500f9, 0xffe040fb, 0xffea80fc} 
  15.     }; 
  16.  
  17.     @Override 
  18.     protected void onStart(Intent intent) { 
  19.         super.onStart(intent); 
  20.         PositionLayout container = new PositionLayout(this); 
  21.         DisplayAttributes displayAttributes = DisplayManager.getInstance().getDefaultDisplay(this).get().getAttributes(); 
  22.         float screenWidth = displayAttributes.width; 
  23.         float screenHeight = displayAttributes.height; 
  24.         YogaNode root = new YogaNodeJNIFinalizer(); 
  25.         root.setWidth(screenWidth); 
  26.         root.setHeight(screenHeight); 
  27.         root.setFlexDirection(YogaFlexDirection.COLUMN); 
  28.  
  29.         createRowNodeAndView(root, 0); 
  30.         createRowNodeAndView(root, 1); 
  31.         createRowNodeAndView(root, 2); 
  32.         createRowNodeAndView(root, 3); 
  33.  
  34.         root.calculateLayout(screenWidth, screenHeight); 
  35.  
  36.         for (int i = 0; i < mViewList.size(); i++) { 
  37.             Component component = mViewList.get(i); 
  38.             YogaNode yogaNode = mYogaNodeList.get(i); 
  39.             YogaNode yogaNodeOwner = yogaNode.getOwner(); 
  40.             component.setTranslationX(yogaNodeOwner.getLayoutX() + yogaNodeOwner.getLayoutX()); 
  41.             component.setTranslationY(yogaNodeOwner.getLayoutY() + yogaNodeOwner.getLayoutY()); 
  42.             component.setLeft((int) (yogaNodeOwner.getLayoutX() + yogaNode.getLayoutX())); 
  43.             component.setTop((int) (yogaNodeOwner.getLayoutY() + yogaNode.getLayoutY())); 
  44.             container.addComponent(component); 
  45.         } 
  46.  
  47.         super.setUIContent(container); 
  48.     } 
  49.  
  50.     private void createRowNodeAndView(YogaNode root, int index) { 
  51.         YogaNode row = new YogaNodeJNIFinalizer(); 
  52.         row.setHeight(VIEW_HEIGHT); 
  53.         row.setWidth(VIEW_WIDTH * 4); 
  54.         row.setFlexDirection(YogaFlexDirection.ROW); 
  55.         row.setMargin(YogaEdge.ALL, 20); 
  56.  
  57.         for (int i = 0; i < 4; i++) { 
  58.             YogaNode yogaNode = new YogaNodeJNIFinalizer(); 
  59.             yogaNode.setWidth(VIEW_WIDTH); 
  60.             yogaNode.setHeight(VIEW_HEIGHT); 
  61.             Component component = createView(colors[index][i]); 
  62.             row.addChildAt(yogaNode, i); 
  63.             mYogaNodeList.add(yogaNode); 
  64.             mViewList.add(component); 
  65.         } 
  66.  
  67.         root.addChildAt(row, index); 
  68.     } 
  69.  
  70.     private Component createView(int color) { 
  71.         Component view = new Component(this); 
  72.         ShapeElement background = new ShapeElement(); 
  73.         background.setRgbColor(convertColor(color)); 
  74.         view.setBackground(background); 
  75.         ComponentContainer.LayoutConfig layoutConfig = new AdaptiveBoxLayout.LayoutConfig(VIEW_WIDTH, VIEW_HEIGHT); 
  76.         view.setLayoutConfig(layoutConfig); 
  77.         return view
  78.     } 
  79.  
  80.     /** 
  81.      *  转换颜色 
  82.      * @param color 
  83.      * @return RgbColor 
  84.      */ 
  85.     public RgbColor convertColor(int color) { 
  86.         int colorInt = color; 
  87.         int red = (colorInt & 0xff0000) >> 16; 
  88.         int green = (colorInt & 0x00ff00) >> 8; 
  89.         int blue = (colorInt & 0x0000ff); 
  90.         return new RgbColor(red, green, blue); 
  91.     } 

代码中定义了一个root根布局,宽高为屏幕的宽高,接着定义了四个行布局,并向每个行布局里添加4个子布局,最重要的是在调用root.calculateLayout(screenWidth, screenHeight)后,便将每个子布局的位置给确定了下来,然后根据获取到的每个布局的参数,给每个Component设置位置。该演示只是借助yoga组件来确定每个Component位置,真正使渲染生效的还是基于鸿蒙的原生控件。

第二个演示界面

接下来展示如何使用yoga组件在xml里通过填写属性来控制item位置的能力,效果图如下:

代码如下:

  1. <?xml version="1.0" encoding="utf-8" ?> 
  2. <com.facebook.yoga.openharmony.YogaLayout 
  3.        xmlns:ohos="http://schemas.huawei.com/res/ohos" 
  4.        xmlns:yoga="http://schemas.huawei.com/apk/res-auto" 
  5.        ohos:height="match_parent" 
  6.        ohos:width="match_parent" 
  7.  
  8.    <com.facebook.yoga.openharmony.YogaLayout 
  9.            ohos:height="60vp" 
  10.            ohos:width="match_content" 
  11.            yoga:yg_alignItems="center" 
  12.            yoga:yg_flexDirection="row" 
  13.            yoga:yg_marginHorizontal="15" 
  14.            yoga:yg_marginStart="15" 
  15.            yoga:yg_marginTop="50" 
  16.            ohos:background_element="$graphic:item_element" 
  17.    > 
  18.  
  19.  
  20.        <Text 
  21.                ohos:height="50vp" 
  22.                ohos:width="50vp" 
  23.                ohos:background_element="$media:icon" 
  24.                yoga:yg_flex="0" 
  25.                yoga:yg_marginStart="15" 
  26.        /> 
  27.  
  28.  
  29.        <Text 
  30.                ohos:height="50vp" 
  31.                ohos:width="220vp" 
  32.                ohos:text="Hello.  I am Yoga!" 
  33.                ohos:text_color="#000000" 
  34.                yoga:yg_flex="1" 
  35.                yoga:yg_marginStart="15" 
  36.                ohos:text_size="20fp" 
  37.        /> 
  38.    </com.facebook.yoga.openharmony.YogaLayout> 
  39.  
  40.    <com.facebook.yoga.openharmony.YogaLayout 
  41.            ohos:background_element="$graphic:item_element" 
  42.            ohos:height="60vp" 
  43.            ohos:width="match_content" 
  44.            yoga:yg_alignItems="center" 
  45.            yoga:yg_flexDirection="row" 
  46.            yoga:yg_marginHorizontal="15" 
  47.            yoga:yg_marginTop="20" 
  48.            yoga:yg_marginStart="15" 
  49.    > 
  50.  
  51.  
  52.        <Text 
  53.                ohos:height="50vp" 
  54.                ohos:width="50vp" 
  55.                ohos:background_element="$media:icon" 
  56.                yoga:yg_flex="0" 
  57.                yoga:yg_marginStart="15" 
  58.        /> 
  59.  
  60.        <Text 
  61.                ohos:height="50vp" 
  62.                ohos:width="250vp" 
  63.                ohos:text="I am a layout engine!" 
  64.                ohos:text_color="#000000" 
  65.                yoga:yg_flex="1" 
  66.                yoga:yg_marginStart="15" 
  67.                ohos:text_size="20fp" 
  68.        /> 
  69.    </com.facebook.yoga.openharmony.YogaLayout> 
  70.  
  71.    <com.facebook.yoga.openharmony.YogaLayout 
  72.            ohos:background_element="$graphic:item_element" 
  73.            ohos:height="60vp" 
  74.            ohos:width="match_content" 
  75.            yoga:yg_alignItems="center" 
  76.            yoga:yg_flexDirection="row" 
  77.            yoga:yg_marginHorizontal="15" 
  78.            yoga:yg_marginTop="20" 
  79.    > 
  80.  
  81.        <Text 
  82.                ohos:height="50vp" 
  83.                ohos:width="50vp" 
  84.                ohos:background_element="$media:icon" 
  85.                yoga:yg_flex="0" 
  86.                yoga:yg_marginStart="15" 
  87.        /> 
  88.  
  89.        <Text 
  90.                ohos:height="50vp" 
  91.                ohos:width="250vp" 
  92.                ohos:text="I run natively." 
  93.                ohos:text_color="#000000" 
  94.                yoga:yg_flex="1" 
  95.                yoga:yg_marginStart="15" 
  96.                ohos:text_size="20fp" 
  97.        /> 
  98.    </com.facebook.yoga.openharmony.YogaLayout> 
  99.  
  100.    <com.facebook.yoga.openharmony.YogaLayout 
  101.            ohos:background_element="$graphic:item_element" 
  102.            ohos:height="60vp" 
  103.            ohos:width="match_content" 
  104.            yoga:yg_alignItems="center" 
  105.            yoga:yg_flexDirection="row" 
  106.            yoga:yg_marginHorizontal="15" 
  107.            yoga:yg_marginTop="20" 
  108.    > 
  109.  
  110.        <Text 
  111.                ohos:height="50vp" 
  112.                ohos:width="50vp" 
  113.                ohos:background_element="$media:icon" 
  114.                yoga:yg_flex="0" 
  115.        /> 
  116.  
  117.        <Text 
  118.                ohos:height="50vp" 
  119.                ohos:width="200vp" 
  120.                ohos:text="So I\'m fast." 
  121.                ohos:text_color="#000000" 
  122.                yoga:yg_flex="1" 
  123.                yoga:yg_marginStart="15" 
  124.                ohos:text_size="20fp" 
  125.        /> 
  126.    </com.facebook.yoga.openharmony.YogaLayout> 
  127.  
  128.    <com.facebook.yoga.openharmony.YogaLayout 
  129.            ohos:background_element="$graphic:item_element" 
  130.            ohos:height="60vp" 
  131.            ohos:width="match_content" 
  132.            yoga:yg_alignItems="center" 
  133.            yoga:yg_flexDirection="row" 
  134.            yoga:yg_marginHorizontal="15" 
  135.            yoga:yg_marginTop="20" 
  136.    > 
  137.  
  138.        <Text 
  139.                ohos:height="50vp" 
  140.                ohos:width="50vp" 
  141.                ohos:background_element="$media:icon" 
  142.                yoga:yg_flex="0" 
  143.        /> 
  144.  
  145.        <Text 
  146.                ohos:height="50vp" 
  147.                ohos:width="200vp" 
  148.                ohos:text="Who are you?" 
  149.                ohos:text_color="#000000" 
  150.                yoga:yg_flex="1" 
  151.                yoga:yg_marginStart="15" 
  152.                ohos:text_size="20fp" 
  153.        /> 
  154.    </com.facebook.yoga.openharmony.YogaLayout> 
  155.  
  156. </com.facebook.yoga.openharmony.YogaLayout> 

这里YogaLayout其实可以看成FlexBox(详情请参考附录:FlexBox科普),可以通过参数调节子布局位置,我们可以使用YogaLayout上的yoga:yg_alignItems="center"属性使得item居中显示,并通过yoga:yg_flexDirection="row"属性使得之item横向排列。子item也可以通过设置yoga:yg_flex="1"来调整自己的权重。更多属性的使用大家也可以下载项目亲自体验。

集成方式

自行编译工程entity、yoga、yoga_layout、fb生成libyoga.so、libfb.so、libyogacore.so。

将其添加到要集成的libs文件夹内,在entity的gradle内添加如下代码。

方式一:

通过library生成har包,添加har包到libs文件夹内。

在entry的gradle内添加如下代码:

  1. implementation fileTree(dir:'libs', include:['*.jar','*.har']) 

方式二:

  1. allprojects{ 
  2.     repositories{ 
  3.         mavenCentral() 
  4.     } 
  5. implementation 'io.openharmony.tpc.thirdlib:yoga-layout:1.0.0' 
  6. implementation 'io.openharmony.tpc.thirdlib:yoga-yoga:1.0.0' 
  7. implementation 'io.openharmony.tpc.thirdlib:yoga-fb:1.0.0' 

附录1:FlexBox科普

布局的传统解决方案,基于盒状模型,依赖display属性,position属性,float属性。它对于那些特殊布局非常不方便,比如,垂直居中就不容易实现。2009年,W3C提出了一种新的方案:flex。可以简便、完整、响应式地实现各种界面布局。目前,该方案已经得到了所有浏览器的支持。采用Flex布局的元素,称为Flex容器(flex container),简称“容器”。它的所有子元素置动成为容器成员,称为Flex项目(flex item),简称“项目”。

容器默认存在两根轴:水平的主轴(main axis)和垂直的交叉轴(cross axis)。主轴的开始位置(与边框的交叉点)叫main start,结束位置叫main end;交叉轴的开始位置叫cross start,结束位置叫cross end。项目默认沿主轴排列。单个项目占据的主轴空间叫main size,占据的交叉轴空间叫cross size。

附录2:相关资料

项目地址:https://gitee.com/openharmony-tpc/yoga

IDE官方下载地址:https://developer.harmonyos.com/cn/develop/deveco-studio

阮一峰的flex布局教程:http://www.ruanyifeng.com/blog/2015/07/flex-grammar.html

想了解更多内容,请访问:

51CTO和华为官方合作共建的鸿蒙技术社区

https://harmonyos.51cto.com

 

责任编辑:jianghua 来源: 鸿蒙社区
相关推荐

2021-08-02 14:54:50

鸿蒙HarmonyOS应用

2021-08-09 10:24:49

鸿蒙HarmonyOS应用

2021-03-10 15:03:40

鸿蒙HarmonyOS应用

2021-04-29 14:32:24

鸿蒙HarmonyOS应用

2021-04-28 15:07:06

鸿蒙HarmonyOS应用

2021-08-03 12:47:58

鸿蒙HarmonyOS应用

2021-03-24 09:30:49

鸿蒙HarmonyOS应用

2021-07-28 09:40:04

鸿蒙HarmonyOS应用

2021-03-03 09:42:26

鸿蒙HarmonyOS图片裁剪

2021-08-03 10:07:41

鸿蒙HarmonyOS应用

2021-07-06 18:21:31

鸿蒙HarmonyOS应用

2021-04-20 15:06:42

鸿蒙HarmonyOS应用

2021-08-05 15:06:30

鸿蒙HarmonyOS应用

2021-04-08 14:57:52

鸿蒙HarmonyOS应用

2021-08-30 17:55:58

鸿蒙HarmonyOS应用

2021-03-01 14:00:11

鸿蒙HarmonyOS应用

2021-11-17 15:37:43

鸿蒙HarmonyOS应用

2021-08-04 14:16:41

鸿蒙HarmonyOS应用

2021-07-20 15:20:40

鸿蒙HarmonyOS应用

2021-04-15 17:47:38

鸿蒙HarmonyOS应用
点赞
收藏

51CTO技术栈公众号