自学HarmonyOS应用开发(67)- 自定义布局(2)

网友投稿 644 2022-09-06

自学HarmonyOS应用开发(67)- 自定义布局(2)

自学HarmonyOS应用开发(67)- 自定义布局(2)

布局文件示例

接下来使用一个实际的布局为例,介绍动态调整组件高度的实现方法。布局内容如下:

DynamicLayout正常动作需要几个必要条件:

需要进行调整的组件通过高度固定的LayoutSeparator组件分隔。这里高度固定是一个必要条件。LayoutSeparator上下组件高度的指定方式必须相同:要么都是直接用height属性指定高度,要么都是用weight指定占比。

除了这两个限制之外,调整对象组件的类型/个数,分隔的组件的高度都可以任意指定。够灵活了吧。

LayoutSeparator组件

public class LayoutSeparator extends Component { public LayoutSeparator(Context context, AttrSet attrSet) { super(context, attrSet); }}

这个组件没有特别实现任何功能,只是获得了一个用于表明拖动对象的类型。它会在后面的说明中用到。

处理拖动动作

下面是DynamicLayout中实现的DraggedListener:

onDragDown方法用于处理拖动按下操作,内容是找到按下动作对象的LayoutSeparator并改变其颜色,如果按下对象不是LayoutSeparator,就当什么也没发生。

onDragStart方法调用DynamicLayout的onSeparatorDragStart方法并记录拖动的开始位置。接下来的onDragUpdate会在调用DynamicLayout的onSeparatorDragUpdate时使用这个开始位置信息。

Component.DraggedListener dragListener = new Component.DraggedListener(){ Point dragStart = null; LayoutSeparator draggedSeparator = null; @Override public void onDragDown(Component component, DragInfo dragInfo) { //HiLog.info(LABEL, "DynamicLayout.onDragDown!"); draggedSeparator = null; dragStart = null; for (int idx = 1; idx < getChildCount()-1; idx++) { Component childView = DynamicLayout.this.getComponentAt(idx); if (childView instanceof LayoutSeparator) { LayoutSeparator separator = (LayoutSeparator) childView; Rect visibleRect = new Rect(separator.getLeft(), separator.getTop(), separator.getRight(), separator.getBottom()); if(visibleRect.isInclude(dragInfo.downPoint)){ draggedSeparator = separator; ShapeElement bg = new ShapeElement(); bg.setRgbColor(RgbPalette.GREEN); bg.setShape(ShapeElement.RECTANGLE); draggedSeparator.setBackground(bg); } } } } @Override public void onDragStart(Component component, DragInfo dragInfo) { if(draggedSeparator != null){ DynamicLayout.this.onSeparatorDragStart(draggedSeparator); dragStart = dragInfo.startPoint; } } @Override public void onDragUpdate(Component component, DragInfo dragInfo) { if(draggedSeparator != null) { Size offset = new Size((int) (dragInfo.updatePoint.getPointX() - dragStart.getPointX()), (int) (dragInfo.updatePoint.getPointY() - dragStart.getPointY())); DynamicLayout.this.onSeparatorDragUpdate(draggedSeparator, offset); } } @Override public void onDragEnd(Component component, DragInfo dragInfo) { //HiLog.info(LABEL, "DynamicLayout.onDragEnd!"); if(draggedSeparator != null){ ShapeElement bg = new ShapeElement(); bg.setRgbColor(RgbPalette.LIGHT_GRAY); bg.setShape(ShapeElement.RECTANGLE); draggedSeparator.setBackground(bg); } draggedSeparator = null; } @Override public void onDragCancel(Component component, DragInfo dragInfo) { //HiLog.info(LABEL, "DynamicLayout.onDragCancel!"); draggedSeparator = null; invalidate(); } @Override public boolean onDragPreAccept(Component component, int dragDirection) { return true; }};

onDragEnd负责最后恢复LayoutSeparator的颜色。

处理开始拖动动作

以下是开始拖动时的处理:

public void onSeparatorDragStart(LayoutSeparator separator){ up_height = -1; down_height = -1; up_weight = -1; down_weight = -1; for (int idx = 1; idx < getChildCount()-1; idx++) { Component childView = getComponentAt(idx); if(childView == separator) { Component comp_up = getComponentAt(idx - 1); DynamicLayout.LayoutConfig lc_up = (DynamicLayout.LayoutConfig)comp_up.getLayoutConfig(); Component comp_down = getComponentAt(idx + 1); DynamicLayout.LayoutConfig lc_down = (DynamicLayout.LayoutConfig)comp_down.getLayoutConfig(); if(lc_up.height >= 0 && lc_down.height >= 0) { up_height = comp_up.getHeight(); down_height = comp_down.getHeight(); } up_weight = lc_up.weight; down_weight = lc_down.weight; } }}

内容很简单:就是找到LayoutSeparator上下都组件并记录它们的高度信息。

处理拖动过程

拖动过程中就是根据DraggedListener传来的拖动距离信息调整LayoutSeparator相邻组件的高度。无论是使用height属性还是weight属性表示高度,都会保证调整前后的合计值不变。这样可以保证位置调整不会影响其他组件。

public void onSeparatorDragUpdate(LayoutSeparator separator, Size offset){ //HiLog.info(LABEL, "DynamicLayout.onSeparatorDragUpdate!offset.height=%{public}d", offset.height); if((up_height > 0 && (up_height + offset.height) >= 0) && (down_height >0 && (down_height - offset.height) >= 0)) { for (int idx = 1; idx < getChildCount() - 1; idx++) { Component childView = getComponentAt(idx); if (childView == separator) { adjustHeight(getComponentAt(idx - 1), getComponentAt(idx + 1), offset.height); break; } } }}void adjustHeight(Component up, Component down, int offset){ DynamicLayout.LayoutConfig lc_up = (DynamicLayout.LayoutConfig)up.getLayoutConfig(); DynamicLayout.LayoutConfig lc_down = (DynamicLayout.LayoutConfig)down.getLayoutConfig(); if(lc_up.height > 0 && lc_down.height > 0){ lc_up.height = up_height + offset; lc_down.height = down_height - offset; } else if(lc_up.height == 0 && lc_down.height==0 && weight_rate > 0){ offset = (int)(offset / weight_rate); lc_up.weight = up_weight + offset; lc_down.weight = down_weight - offset; } else{ //do nothing. } arrange();}

arrange的功能是主动发起布局调整,这个方法使用了之前的布局计算过程中预留的数据

public void arrange(){ onEstimateSize(lastConfigWidth, lastConfigHeight); onArrange(layoutLeft, layoutTop, layoutWidth, layoutHeight);}

作者没有找到更简单的发起布局计算的方法,先用这种方法将就一下吧。

参考资料

自定义布局

​​的标准GUI 工具包tkinter,通过可执行的示例对23 个设计模式逐个进行说明。这样一方面可以使读者了解真实的软件开发工作中每个设计模式的运用场景和想要解决的问题;另一方面通过对这些问题的解决过程进行说明,让读者明白在编写代码时如何判断使用设计模式的利弊,并合理运用设计模式。

对设计模式感兴趣而且希望随学随用的读者通过本书可以快速跨越从理解到运用的门槛;希望学习Python GUI 编程的读者可以将本书中的示例作为设计和开发的参考;使用Python 语言进行图像分析、数据处理工作的读者可以直接以本书中的示例为基础,迅速构建自己的系统架构。

觉得本文有帮助?请分享给更多人。

面向对象开发,面向对象思考!

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:必须会的SQL语句(一) 创建数据库与删除数据库(基本的sql语句)
下一篇:对抗模型 attack and defence
相关文章

 发表评论

暂时没有评论,来抢沙发吧~