详情请访问:Harmonyos.51cto.com定义流布局组件FlowLayout,实现鸿蒙的功能迁移重构。代码已经开源(https://gitee.com/isrc_ohos/flow-layout_ohos),欢迎广大开发者下载使用并提出宝贵意见!背景流布局也叫百分比布局,有指定的对齐方式,水平间距和垂直间距,特别适合多标签显示。可以实现组件中标签的水平对齐,也可以在多个标签的总宽度超过组件宽度时自动换行。是移动端开发中经常使用的布局方式之一。我们可以在很多应用场景中看到流式布局的使用,比如商品分类展示、搜索记录展示等。组件效果展示组件应用只有一个展示页面。为了呈现流畅布局的效果,我们在页面布局中添加了多个标签,如“java”、“kotlin”、“ohos”、“Deveco-studio”、“app”等作为子组件在布局。具体展示效果如图1所示。图1组件效果展示Sample解析FlowLayout_ohos已经将组件的主要功能封装在Library中。将label放入FlowLayout_ohos组件中,当多个label的总宽度超过组件宽度时,会自动水平对齐并自动换行。因此,在Sample中我们只需要添加标签内容,并使用流式布局来展示标签内容即可。在标签显示过程中,我们可以调用Library暴露的一些接口来设置子组件的显示特性,比如组件显示的最大行数。下面将详细讲解FlowLayout_ohos组件的使用方法,分为5步:Step1.导入相关类Step2.初始化流布局和数据容器Step3.添加标签内容到数据容器Step4.添加labelcontentintoLayoutstep5.相关功能设置下面我们来看看每个步骤涉及的详细操作。(1)导入相关类在MainAbilitySlice文件中,通过import关键字导入FlowAdapter类和FlowLayout类。FlowLayout类用??于显示组件,FlowAdapter类用于为组件设置标签。importcom.huawei.mylibrary.FlowAdapter;importcom.huawei.mylibrary.FlowLayout;(2)初始化流布局和数据容器实例化FlowLayout类的对象mFlowLayout,然后创建一个元素为String类型的列表mContentList作为添加标签的容器,如下我们称之为数据容器。privateFlowLayoutmFlowLayout;privateListmContentList=newArrayList<>();@OverridepublicvoidonStart(Intentintent){......mFlowLayout=newFlowLayout(this);}(3)通过add()方法向数据容器添加标签内容给数据在容器mContentList中添加自己想要展示的标签,通过四次for循环将5个不同的标签一一放入容器中,总共形成20个需要在页面展示的标签。for(inti=0;i<4;i++){mContentList.add("java");mContentList.add("kotlin");mContentList.add("ohos");mContentList.add("Deveco-studio");mContentList.add("app");}(4)将标签内容添加到布局中并实例化FlowAdapter类的对象适配器,将数据容器mContentList作为FlowAdapter类构造方法的参数。然后通过setAdapter()方法将标签内容添加到组件中。//SetAdapterFlowAdapteradapter=newFlowAdapter(this,mContentList);//给组件添加标签内容mFlowLayout.setAdapter(adapter);(5)给组件添加标签内容mFlowLayout.setAdapter(adapter);(6)相关特性设置mFlowLayout可以调用Library暴露的一些接口,实现流式布局的特性设置。这里我们设置组件布局中显示的最大行数。//设置最大显示行数mFlowLayout.setMaxLines(9);库解析流式布局使用比较广泛,但是鸿蒙官方并没有给出相应的布局方式,所以流式布局只能通过自定义来实现,本节主要介绍自定义布局的步骤。实现一个自定义布局,需要完成以下三个步骤:1)流式布局的FlowLayout类需要继承ComponentContainer类,增加一个构造方法。2)实现ComponentContainer.EstimateSizeListener接口,重写onEstimateSize()方法,确定FlowLayout_ohos组件的宽高。3)实现Component.LayoutRefreshedListener接口,重写onRefreshed()方法,对子组件进行排列,并确定子组件的位置。步骤1)的操作比较简单,这里不再赘述。本节主要介绍步骤2)和3)的原理。(1)重写onEstimateSize方法。根据onEstimateSize(intwidthMeasureSpec,intheightMeasureSpec)方法传入的参数,选择测量组件宽高的方式,获取组件宽高的具体值,通过setEstimateSize()方法。具体步骤如下:1.获取组件的测量方式和父组件的宽高调用EstimateSpec.getMode(widthMeasureSpec)方法,传入widthMeasureSpec参数,获取组件宽度的测量方式.调用EstimateSpec.getMode(heightMeasureSpec)方法,传入heightMeasureSpec参数,获取组件高度的测量模式。调用EstimateSpec.getSize(widthMeasureSpec)方法,传入widthMeasureSpec参数,获取父组件的宽度。调用EstimateSpec.getSize(heightMeasureSpec)方法,传入heightMeasureSpec参数,获取父组件的高度。intwidthSize=EstimateSpec.getSize(widthMeasureSpec);//父组件的宽度intwidthMode=EstimateSpec.getMode(widthMeasureSpec);//组件宽度的测量模式intheightSize=EstimateSpec.getSize(heightMeasureSpec);//组件的高度父组件中的ightMode=EstimateSpec。getMode(heightMeasureSpec);//组件高度的测量方式2.确定组件宽高的具体值Calculation.PRECISE模式:在此模式下,组件将其宽度和高度设置为MATCH_PARENT。NOT_EXCEED模式:在此模式下,组件将其宽度和高度设置为MATCH_CONTENT。在PRECISE模式下,组件的宽高与父组件保持一致,这种计算方式比较简单。但是在NOT_EXCEED模式下,组件的宽高是根据子组件的宽高来决定的。这时候就需要遍历每个子组件,对每个子组件进行测量,将宽高求和,计算出最终组件的宽高。子组件的遍历过程是通过helper()方法实现的。int[]a=helper(widthSize);intmeasuredHeight=0;//组件的高度值if(heightMode==EstimateSpec.PRECISE){//PRECISE模式measuredHeight=heightSize;}elseif(heightMode==EstimateSpec.NOT_EXCEED){//NOT_EXCEED模式measuredHeight=a[0];//遍历每个子组件后得到的组件高度}intmeasuredWidth=0;//组件的宽度值if(widthMode==EstimateSpec.PRECISE){//PRECISE模式measuredWidth=widthSize;}elseif(widthMode==EstimateSpec.NOT_EXCEED){//NOT_EXCEED模式measuredWidth=a[1];//遍历每个子组件后得到的组件宽度}3.给组件设置测量的高宽值。通过setEstimatedSize()方法将第2步得到的组件宽高值设置到组件中。setEstimatedSize(测量宽度,测量高度);(2)重写onRefreshed方法onRefreshed()方法主要用于判断子组件的放置。在helper()方法中已经获取到position,并保存在mChildrenPositionList中。mChildrenPositionList是一个列表,其元素类型为Rect,每个元素代表一个子组件的位置信息。因此,在确定子组件的放置位置时,只需要调用mChildrenPositionList中的元素信息,分配给各个子组件即可。@OverridepublicvoidonRefreshed(Componentcomponent){intn=Math.min(getChildCount(),mChildrenPositionList.size());for(inti=0;iwidthSize){//需要换行height+=maxHeight;//增加一行的高度width=getPaddingLeft();//获取新行已经占用的宽度maxHeight=childHeight;isOneRow=false;currLine++;//行数+1if(currLine>mMaxLines){//超过设置的最大显示行数,退出break;}}3)保存子组件的位置信息根据当前宽高位置,并将位置信息作为参数传入Rect类对象实例化过程中,使用Rect类对象识别子组件的位置信息,并将这些信息一一放入List中,并在onRefreshed()方法中使用它们。Rectrect=newRect(width+child.getMarginLeft(),height+child.getMarginTop(),width+childWidth-child.getMarginRight(),height+childHeight-child.getMarginBottom());mChildrenPositionList.add(rect);想了解更多请访问:https://harmonyos.51cto.com,与华为合作搭建的鸿蒙技术社区