yoga-像写网页一样写客户端界面

这几年大前端化越演越烈,越来越有融合的趋势,前端和客户端越来越傻傻分不清了,今天介绍一个款facebook开源的布局算法库yoga,让大家可以在客户端用前端网页常用的Flex布局,绝对布局完成布局效果,也许你会问我客户端元素的ConstrainLayout,AutoLayout这类的不香吗,对于原生开发者当然香,但让前端开发来写就不那么香了,也需你会反过来问我,元素开发写网页布局方式就香吗?答案是确实香,因为前端的那套Flex布局,一句话总结就是简单,简单,很简单

Flex布局多容易理解,参考阮一峰老师的Flex介绍,我们来讲讲怎么在android客户端用这个布局方式,因为这么好的东西,网上竟然找不到太多android使用的方式,连官方都没有维护一套,那我就手撸一个。

如何使用yoga

话不多说,先建立一个android 工程,然后引入最新的yoga库

compile 'com.facebook.yoga.android:yoga-layout:1.16.0'

简单写个布局

<?xml version="1.0" encoding="utf-8"?>
<com.facebook.yoga.android.YogaLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:yoga="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    yoga:yg_flexDirection="column"
    yoga:yg_justifyContent="space_between"
    yoga:yg_alignItems="center"
    >

    <TextView
        android:id="@+id/absolute"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        yoga:yg_positionType="absolute"
        android:background="@android:color/darker_gray"
        android:textColor="@android:color/white"
        android:padding="5dp"
        android:gravity="center"
        yoga:yg_positionTop="10dp"
        yoga:yg_positionLeft="10dp"
        />

    <TextView
        android:id="@+id/top"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@android:color/darker_gray"
        android:textColor="@android:color/white"
        android:padding="5dp"
        yoga:yg_marginTop="60dp"
        />
    <com.facebook.yoga.android.YogaLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        yoga:yg_flexDirection="row"
        yoga:yg_justifyContent="flex_start"
        yoga:yg_alignItems="center"
        yoga:yg_alignSelf="center"
        >
        <TextView
            android:id="@+id/middle_left"
            android:layout_width="wrap_content"
            android:layout_height="50dp"
            android:background="@android:color/darker_gray"
            android:textColor="@android:color/white"
            android:padding="5dp"
            yoga:yg_alignSelf="center"
            android:gravity="center"
            />
        <TextView
            android:id="@+id/middle"
            android:layout_width="wrap_content"
            android:layout_height="60dp"
            android:background="@android:color/darker_gray"
            android:textColor="@android:color/white"
            android:padding="5dp"
            yoga:yg_alignSelf="center"
            android:gravity="center"
            />
        <TextView
            android:id="@+id/middle_right"
            android:layout_width="wrap_content"
            android:layout_height="70dp"
            android:background="@android:color/darker_gray"
            android:textColor="@android:color/white"
            android:padding="5dp"
            yoga:yg_alignSelf="center"
            android:gravity="center"
            />
    </com.facebook.yoga.android.YogaLayout>

    <TextView
        android:id="@+id/bottom"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@android:color/darker_gray"
        android:textColor="@android:color/white"
        android:padding="5dp"
        />


</com.facebook.yoga.android.YogaLayout>

看看效果

当然了只写xml其实是不够的,还需要在Application引入下yoga的使用

public class YogaApplication extends Application{

public void onCreate(){
super.onCreate();
SoLoader.init(this, false);
LayoutInflater.from(this).setFactory(YogaViewLayoutFactory.getInstance());
}
}

整个demo代码我上传到GitHub地址

yoga细节初探

上面我们大概介绍了这个yoga的使用,现在我们来看看一些细节使用

属性值

这部分相对简单,yoga定义给android xml的属性跟flex本身的很类似

yoga:yg_flexDirection="column"

命名空间用yoga,具体属性值用yg _开头,然后加具体名称,yoga官方并没有提供所有属性值的写法,我们引入yoga之后可以在studio上通过jar直接查看到

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="yoga"><attr format="enum" name="yg_alignContent">
      <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 format="enum" name="yg_alignItems">
      <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 format="enum" name="yg_alignSelf">
      <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 format="float" name="yg_aspectRatio"/>
    <attr format="dimension" name="yg_borderLeft"/>
    <attr format="dimension" name="yg_borderTop"/>
    <attr format="dimension" name="yg_borderRight"/>
    <attr format="dimension" name="yg_borderBottom"/>
    <attr format="dimension" name="yg_borderStart"/>
    <attr format="dimension" name="yg_borderEnd"/>
    <attr format="dimension" name="yg_borderHorizontal"/>
    <attr format="dimension" name="yg_borderVertical"/>
    <attr format="dimension" name="yg_borderAll"/>
    <attr format="enum" name="yg_direction">
      <enum name="inherit" value="0"/>
      <enum name="ltr" value="1"/>
      <enum name="rtl" value="2"/>
    </attr><attr format="enum" name="yg_display">
      <enum name="flex" value="0"/>
      <enum name="none" value="1"/>
    </attr>
    <attr format="float" name="yg_flex"/>
    <attr format="float|string" name="yg_flexBasis"/>
    <attr format="enum" name="yg_flexDirection">
      <enum name="column" value="0"/>
      <enum name="column_reverse" value="1"/>
      <enum name="row" value="2"/>
      <enum name="row_reverse" value="3"/>
    </attr>
    <attr format="float" name="yg_flexGrow"/>
    <attr format="float" name="yg_flexShrink"/>
    <attr format="dimension|string" name="yg_height"/>
    <attr format="enum" name="yg_justifyContent">
      <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 format="dimension|string" name="yg_marginLeft"/>
    <attr format="dimension|string" name="yg_marginTop"/>
    <attr format="dimension|string" name="yg_marginRight"/>
    <attr format="dimension|string" name="yg_marginBottom"/>
    <attr format="dimension|string" name="yg_marginStart"/>
    <attr format="dimension|string" name="yg_marginEnd"/>
    <attr format="dimension|string" name="yg_marginHorizontal"/>
    <attr format="dimension|string" name="yg_marginVertical"/>
    <attr format="dimension|string" name="yg_marginAll"/>
    <attr format="dimension|string" name="yg_maxHeight"/>
    <attr format="dimension|string" name="yg_maxWidth"/>
    <attr format="dimension|string" name="yg_minHeight"/>
    <attr format="dimension|string" name="yg_minWidth"/>
    <attr format="enum" name="yg_overflow">
      <enum name="visible" value="0"/>
      <enum name="hidden" value="1"/>
      <enum name="scroll" value="2"/>
    </attr>
    <attr format="dimension|string" name="yg_paddingLeft"/>
    <attr format="dimension|string" name="yg_paddingTop"/>
    <attr format="dimension|string" name="yg_paddingRight"/>
    <attr format="dimension|string" name="yg_paddingBottom"/>
    <attr format="dimension|string" name="yg_paddingStart"/>
    <attr format="dimension|string" name="yg_paddingEnd"/>
    <attr format="dimension|string" name="yg_paddingHorizontal"/>
    <attr format="dimension|string" name="yg_paddingVertical"/>
    <attr format="dimension|string" name="yg_paddingAll"/>
    <attr format="dimension|string" name="yg_positionLeft"/>
    <attr format="dimension|string" name="yg_positionTop"/>
    <attr format="dimension|string" name="yg_positionRight"/>
    <attr format="dimension|string" name="yg_positionBottom"/>
    <attr format="dimension|string" name="yg_positionStart"/>
    <attr format="dimension|string" name="yg_positionEnd"/>
    <attr format="dimension|string" name="yg_positionHorizontal"/>
    <attr format="dimension|string" name="yg_positionVertical"/>
    <attr format="dimension|string" name="yg_positionAll"/>
    <attr format="enum" name="yg_positionType">
      <enum name="relative" value="0"/>
      <enum name="absolute" value="1"/>
    </attr><attr format="dimension|string" name="yg_width"/><attr format="enum" name="yg_wrap">
      <enum name="no_wrap" value="0"/>
      <enum name="wrap" value="1"/>
    </attr></declare-styleable>
</resources>

设置控件工场

SoLoader.init(this, false);
LayoutInflater.from(this).setFactory(YogaViewLayoutFactory.getInstance());

我们的demo在Application有两行的初始化操作,第一个是加载yoga的so库,第二步其实就是设置一个yoga的控件工场,里面的核心部分其实就是解析出yoga提供的YogaLayout和VirtualYogaLayout。

@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;
  }

setFactory也算是android对外提供的一个hook接口,让我们可以自定义对xml的一些操作,比如一些昼夜换肤方案,修改字体库等方案。

再来看看YogaLayout内部如何实现

YogaLayout其实就是一层封装,所有跟flex布局有关的子控件都被转成一个YogaNode

YogaNode的实质又只是一个YogaNodeJNIFinalizer,YogaNodeJNIFinalizer继承了YogaNodeJNIBase,内部都是native方法,所以其实Yoga的布局算法都是native实现的,这就保证了yoga的算法效率和跨平台特性,因此我们可以大胆放心地使用yoga库。

结尾

yoga本身的使用并不复杂,但最大的启发是利用c++的跨平台优势和性能优势做多端统一,同时对外给各个端提供简洁易于使用接近各自平台语言的开发方式,可以最大程度地推广自身,比如yoga其实就用在了像react-native,Lito,ComponentKit的底层框架上,笔者所在公司也有类似的使用,相信随着时间推移,整个大前端方向会有越来越多的类似方案出现。

关于chenzujie

非著名码农一枚,认真工作,快乐生活
此条目发表在Android分类目录,贴了标签。将固定链接加入收藏夹。

yoga-像写网页一样写客户端界面》有8条回应

  1. Thank you for every other informative blog.

  2. Sorguladığınız adreste altyapının varlığından bahsedebilmek için yerleşik operatörün ya da bir internet sağlayıcısının bu adreste hizmet veriyor olması gerekir.

  3. Yazınızı beğenerek sıkılmadan okudum. Teşekkür ederim

  4. Güvenilir Bahis Ve Canli Casino Sitesi Pit10bet pitonbet giriş

  5. Güvenilir casino siteleri alternatif siteleri inceleyin 2023 güvenilir casino siteleri

  6. Türkiye’nin En güvenilir Bahis ve Casino Sitesi pit10bet pitonbet giriş

  7. hglweb.com说:

    Sitenizin teması ve okuduğum içerik gerçekten çok güzelmiş beğendim bundan sonra sitenizi takip ediyoroum başarılar dilerim elinize saglik hglweb.com

  8. UP-X — Официальный сайт – сервис мгновенных игр Ап икс

发表评论

邮箱地址不会被公开。 必填项已用*标注