diff --git a/UI/CustomLayout/README.md b/UI/CustomLayout/README.md new file mode 100644 index 0000000000000000000000000000000000000000..1b4c7321e84e480ae4ca11e102037784eb955e55 --- /dev/null +++ b/UI/CustomLayout/README.md @@ -0,0 +1,14 @@ +# CustomLayout + +### Introduction + +This sample demonstrates layout customization of the Java UI framework. Child components are measured at different screen resolutions to determine their size. If the child components cannot be fit into the horizontal space of the screen, they will automatically wrap. + +### Usage + +The custom layout is displayed when you open your application. + +### Constraints + +This sample can only be run on standard-system devices. + diff --git a/UI/CustomLayout/README_zh.md b/UI/CustomLayout/README_zh.md new file mode 100644 index 0000000000000000000000000000000000000000..308c7a6ceff08717bd4ee48b2fe680e5ff927d4d --- /dev/null +++ b/UI/CustomLayout/README_zh.md @@ -0,0 +1,14 @@ +# 自定义布局 + +### 简介 + +本示例演示了Java UI框架提供的自定义布局能力。在不同屏幕分辨率下对子组件进行测量,确定子组件的大小,在屏幕横向空间不足的情况下,下一个子组件将进行自动换行。 + +### 使用说明 + +打开应用所展示的界面即为自定义的布局。 + +### 约束与限制 + +本示例仅支持在标准系统上运行。 + diff --git a/UI/CustomLayout/build.gradle b/UI/CustomLayout/build.gradle new file mode 100644 index 0000000000000000000000000000000000000000..1ce01d8202dede3a761267d58bd910197a647d83 --- /dev/null +++ b/UI/CustomLayout/build.gradle @@ -0,0 +1,36 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +apply plugin: 'com.huawei.ohos.app' + +ohos { + compileSdkVersion 5 + defaultConfig { + compatibleSdkVersion 4 + } +} + +buildscript { + repositories { + maven { + url 'https://repo.huaweicloud.com/repository/maven/' + } + maven { + url 'https://developer.huawei.com/repo/' + } + jcenter() + } + dependencies { + classpath 'com.huawei.ohos:hap:2.4.4.2' + } +} + +allprojects { + repositories { + maven { + url 'https://repo.huaweicloud.com/repository/maven/' + } + maven { + url 'https://developer.huawei.com/repo/' + } + jcenter() + } +} diff --git a/UI/CustomLayout/entry/build.gradle b/UI/CustomLayout/entry/build.gradle new file mode 100644 index 0000000000000000000000000000000000000000..83f59debbd68242b503a22270f4378d7843ca204 --- /dev/null +++ b/UI/CustomLayout/entry/build.gradle @@ -0,0 +1,19 @@ +apply plugin: 'com.huawei.ohos.hap' +ohos { + compileSdkVersion 5 + defaultConfig { + compatibleSdkVersion 4 + } + buildTypes { + release { + proguardOpt { + proguardEnabled false + rulesFiles 'proguard-rules.pro' + } + } + } +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar', '*.har']) +} \ No newline at end of file diff --git a/UI/CustomLayout/entry/src/main/config.json b/UI/CustomLayout/entry/src/main/config.json new file mode 100644 index 0000000000000000000000000000000000000000..c01cd6bc55a3585d5d1b20be0b19e763846253b6 --- /dev/null +++ b/UI/CustomLayout/entry/src/main/config.json @@ -0,0 +1,46 @@ +{ + "app": { + "bundleName": "ohos.samples.customlayout", + "version": { + "code": 1000000, + "name": "1.0" + } + }, + "deviceConfig": {}, + "module": { + "package": "ohos.samples.customlayout", + "name": ".MyApplication", + "reqCapabilities": [ + "video_support" + ], + "deviceType": [ + "default" + ], + "distro": { + "deliveryWithInstall": true, + "moduleName": "entry", + "moduleType": "entry", + "installationFree": false + }, + "abilities": [ + { + "skills": [ + { + "entities": [ + "entity.system.home" + ], + "actions": [ + "action.system.home" + ] + } + ], + "name": "ohos.samples.customlayout.MainAbility", + "icon": "$media:icon", + "description": "$string:mainability_description", + "label": "$string:entry_MainAbility", + "type": "page", + "launchType": "standard" + } + ] + } +} \ No newline at end of file diff --git a/UI/CustomLayout/entry/src/main/java/ohos/samples/customlayout/MainAbility.java b/UI/CustomLayout/entry/src/main/java/ohos/samples/customlayout/MainAbility.java new file mode 100644 index 0000000000000000000000000000000000000000..38f0525d152165f36225f8fd8679375a734c30f3 --- /dev/null +++ b/UI/CustomLayout/entry/src/main/java/ohos/samples/customlayout/MainAbility.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ohos.samples.customlayout; + +import ohos.aafwk.ability.Ability; +import ohos.aafwk.content.Intent; +import ohos.samples.customlayout.slice.MainAbilitySlice; + +/** + * MainAbility + * + * @since 2021-07-03 + */ +public class MainAbility extends Ability { + @Override + public void onStart(Intent intent) { + super.onStart(intent); + super.setMainRoute(MainAbilitySlice.class.getName()); + } +} diff --git a/UI/CustomLayout/entry/src/main/java/ohos/samples/customlayout/MyApplication.java b/UI/CustomLayout/entry/src/main/java/ohos/samples/customlayout/MyApplication.java new file mode 100644 index 0000000000000000000000000000000000000000..6a907bcbc745ebc3bb58a1d6ab5c22509a3fbba1 --- /dev/null +++ b/UI/CustomLayout/entry/src/main/java/ohos/samples/customlayout/MyApplication.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ohos.samples.customlayout; + +import ohos.aafwk.ability.AbilityPackage; + +/** + * MyApplication + * + * @since 2021-07-03 + */ +public class MyApplication extends AbilityPackage { + @Override + public void onInitialize() { + super.onInitialize(); + } +} diff --git a/UI/CustomLayout/entry/src/main/java/ohos/samples/customlayout/component/CustomLayout.java b/UI/CustomLayout/entry/src/main/java/ohos/samples/customlayout/component/CustomLayout.java new file mode 100644 index 0000000000000000000000000000000000000000..7f558cd37b71b49774e7d12a84a3a148629ef7a3 --- /dev/null +++ b/UI/CustomLayout/entry/src/main/java/ohos/samples/customlayout/component/CustomLayout.java @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ohos.samples.customlayout.component; + +import ohos.agp.components.AttrSet; +import ohos.agp.components.Component; +import ohos.agp.components.ComponentContainer; +import ohos.app.Context; + +import java.util.HashMap; +import java.util.Map; + +/** + * Custom layout + * + * @since 2021-07-03 + */ +public class CustomLayout extends ComponentContainer + implements ComponentContainer.EstimateSizeListener, ComponentContainer.ArrangeListener { + private final Map axis = new HashMap<>(); + + private int locationX; + + private int locationY; + + private int maxWidth; + + private int maxHeight; + + private int lastHeight; + + public CustomLayout(Context context, AttrSet attrSet) { + super(context, attrSet); + setEstimateSizeListener(this); + setArrangeListener(this); + } + + @Override + public boolean onEstimateSize(int widthEstimatedConfig, int heightEstimatedConfig) { + measureChildren(widthEstimatedConfig, heightEstimatedConfig); + int width = EstimateSpec.getSize(widthEstimatedConfig); + + for (int idx = 0; idx < getChildCount(); idx++) { + Component childView = getComponentAt(idx); + addChild(childView, idx, width); + } + setEstimatedSize(EstimateSpec.getChildSizeWithMode(maxWidth, widthEstimatedConfig, 0), + EstimateSpec.getChildSizeWithMode(maxHeight, heightEstimatedConfig, 0)); + return true; + } + + private void measureChildren(int widthEstimatedConfig, int heightEstimatedConfig) { + for (int idx = 0; idx < getChildCount(); idx++) { + Component childView = getComponentAt(idx); + if (childView != null) { + measureChild(childView, widthEstimatedConfig, heightEstimatedConfig); + } + } + } + + private void measureChild(Component child, int parentWidthMeasureSpec, int parentHeightMeasureSpec) { + LayoutConfig lc = child.getLayoutConfig(); + int childWidthMeasureSpec = + EstimateSpec.getChildSizeWithMode(lc.width, parentWidthMeasureSpec, EstimateSpec.UNCONSTRAINT); + int childHeightMeasureSpec = + EstimateSpec.getChildSizeWithMode(lc.height, parentHeightMeasureSpec, EstimateSpec.UNCONSTRAINT); + child.estimateSize(childWidthMeasureSpec, childHeightMeasureSpec); + } + + private void addChild(Component component, int id, int layoutWidth) { + Position position = new Position(); + position.positionX = locationX + component.getMarginLeft(); + position.positionY = locationY + component.getMarginTop(); + position.width = component.getEstimatedWidth(); + position.height = component.getEstimatedHeight(); + if ((locationX + position.width) > layoutWidth) { + locationX = 0; + locationY += lastHeight; + lastHeight = 0; + position.positionX = locationX + component.getMarginLeft(); + position.positionY = locationY + component.getMarginTop(); + } + axis.put(id, position); + lastHeight = Math.max(lastHeight, position.height + component.getMarginBottom()); + locationX += position.width + component.getMarginRight(); + maxWidth = Math.max(maxWidth, position.positionX + position.width); + maxHeight = Math.max(maxHeight, position.positionY + position.height); + } + + @Override + public boolean onArrange(int left, int top, int width, int height) { + for (int idx = 0; idx < getChildCount(); idx++) { + Component childView = getComponentAt(idx); + Position position = axis.get(idx); + if (position != null) { + childView.arrange(position.positionX, position.positionY, position.width, position.height); + } + } + return true; + } + + /** + * Layout + * + * @since 2021-05-08 + */ + private static class Position { + int positionX = 0; + + int positionY = 0; + + int width = 0; + + int height = 0; + } +} \ No newline at end of file diff --git a/UI/CustomLayout/entry/src/main/java/ohos/samples/customlayout/slice/MainAbilitySlice.java b/UI/CustomLayout/entry/src/main/java/ohos/samples/customlayout/slice/MainAbilitySlice.java new file mode 100644 index 0000000000000000000000000000000000000000..ef076e981ffe2c197b94e14874ec27252cd426db --- /dev/null +++ b/UI/CustomLayout/entry/src/main/java/ohos/samples/customlayout/slice/MainAbilitySlice.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ohos.samples.customlayout.slice; + +import ohos.aafwk.ability.AbilitySlice; +import ohos.aafwk.content.Intent; +import ohos.agp.colors.RgbColor; +import ohos.agp.components.Button; +import ohos.agp.components.Component; +import ohos.agp.components.DirectionalLayout; +import ohos.agp.components.element.ShapeElement; +import ohos.agp.utils.Color; +import ohos.samples.customlayout.ResourceTable; +import ohos.samples.customlayout.component.CustomLayout; + +/** + * MainAbilitySlice + * + * @since 2021-07-03 + */ +public class MainAbilitySlice extends AbilitySlice { + private static final int COMPONENT_COUNT = 15; + + @Override + public void onStart(Intent intent) { + super.onStart(intent); + DirectionalLayout directionalLayout = new DirectionalLayout(getContext()); + directionalLayout.setPadding(32, 32, 32, 32); + DirectionalLayout.LayoutConfig config = new DirectionalLayout.LayoutConfig( + DirectionalLayout.LayoutConfig.MATCH_PARENT, DirectionalLayout.LayoutConfig.MATCH_PARENT); + directionalLayout.setLayoutConfig(config); + + CustomLayout customLayout = new CustomLayout(this, null); + for (int index = 0; index < COMPONENT_COUNT; index++) { + customLayout.addComponent(getComponent(index + 1)); + } + ShapeElement shapeElement = new ShapeElement(); + shapeElement.setRgbColor(new RgbColor(100, 100, 100)); + customLayout.setBackground(shapeElement); + DirectionalLayout.LayoutConfig layoutConfig = new DirectionalLayout.LayoutConfig( + DirectionalLayout.LayoutConfig.MATCH_PARENT, DirectionalLayout.LayoutConfig.MATCH_CONTENT); + customLayout.setLayoutConfig(layoutConfig); + directionalLayout.addComponent(customLayout); + super.setUIContent(directionalLayout); + } + + private Component getComponent(int index) { + Button button = new Button(getContext()); + ShapeElement shapeElement = new ShapeElement(); + shapeElement.setRgbColor(new RgbColor(200, 200, 200)); + button.setBackground(shapeElement); + button.setTextColor(Color.WHITE); + DirectionalLayout.LayoutConfig layoutConfig = new DirectionalLayout.LayoutConfig(300, 100); + if (index == 1) { + layoutConfig = new DirectionalLayout.LayoutConfig(1080, 200); + button.setText(ResourceTable.String_size01); + } else if (index == 6) { + layoutConfig = new DirectionalLayout.LayoutConfig(500, 100); + button.setText(ResourceTable.String_size02); + } else if (index == 8) { + layoutConfig = new DirectionalLayout.LayoutConfig(600, 600); + button.setText(ResourceTable.String_size03); + } else { + button.setText("Item" + index); + } + layoutConfig.setMargins(10, 10, 10, 10); + button.setLayoutConfig(layoutConfig); + return button; + } +} \ No newline at end of file diff --git a/UI/CustomLayout/entry/src/main/resources/base/element/string.json b/UI/CustomLayout/entry/src/main/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..7ac100b442088c65b2e62e3e7b81ec2f9cfe0d4e --- /dev/null +++ b/UI/CustomLayout/entry/src/main/resources/base/element/string.json @@ -0,0 +1,72 @@ +{ + "string": [ + { + "name": "entry_MainAbility", + "value": "CustomLayout" + }, + { + "name": "mainability_description", + "value": "Custom Layout Example" + }, + { + "name": "size01", + "value": "1080 * 200" + }, + { + "name": "size02", + "value": "500 * 100" + }, + { + "name": "size03", + "value": "600 * 600" + }, + { + "name": "item2", + "value": "Item2" + }, + { + "name": "item3", + "value": "Item3" + }, + { + "name": "item4", + "value": "Item4" + }, + { + "name": "item5", + "value": "Item5" + }, + { + "name": "item7", + "value": "Item7" + }, + { + "name": "item9", + "value": "Item9" + }, + { + "name": "item10", + "value": "Item10" + }, + { + "name": "item11", + "value": "Item11" + }, + { + "name": "item12", + "value": "Item12" + }, + { + "name": "item13", + "value": "Item13" + }, + { + "name": "item14", + "value": "Item14" + }, + { + "name": "item15", + "value": "Item15" + } + ] +} \ No newline at end of file diff --git a/UI/CustomLayout/entry/src/main/resources/base/media/icon.png b/UI/CustomLayout/entry/src/main/resources/base/media/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ce307a8827bd75456441ceb57d530e4c8d45d36c Binary files /dev/null and b/UI/CustomLayout/entry/src/main/resources/base/media/icon.png differ diff --git a/UI/CustomLayout/entry/src/main/resources/en/element/string.json b/UI/CustomLayout/entry/src/main/resources/en/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..6b23105df3d1109c2bed1968faa6c255a851a626 --- /dev/null +++ b/UI/CustomLayout/entry/src/main/resources/en/element/string.json @@ -0,0 +1,12 @@ +{ + "string": [ + { + "name": "entry_MainAbility", + "value": "CustomLayout" + }, + { + "name": "mainability_description", + "value": "Custom Layout Example" + } + ] +} diff --git a/UI/CustomLayout/entry/src/main/resources/zh/element/string.json b/UI/CustomLayout/entry/src/main/resources/zh/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..4e1decce344df83fa3e3c049dd3c3da5705d5e15 --- /dev/null +++ b/UI/CustomLayout/entry/src/main/resources/zh/element/string.json @@ -0,0 +1,12 @@ +{ + "string": [ + { + "name": "entry_MainAbility", + "value": "自定义布局" + }, + { + "name": "mainability_description", + "value": "自定义布局示例" + } + ] +} \ No newline at end of file diff --git a/UI/CustomLayout/screenshots/device/Phone.png b/UI/CustomLayout/screenshots/device/Phone.png new file mode 100644 index 0000000000000000000000000000000000000000..bff5123ef857e835943b13479b06d4275eaf16dc Binary files /dev/null and b/UI/CustomLayout/screenshots/device/Phone.png differ diff --git a/UI/CustomLayout/screenshots/device/Tablet.png b/UI/CustomLayout/screenshots/device/Tablet.png new file mode 100644 index 0000000000000000000000000000000000000000..bb1fc03abe105e13e3a150cba21eca0f6e8c187e Binary files /dev/null and b/UI/CustomLayout/screenshots/device/Tablet.png differ diff --git a/UI/CustomLayout/settings.gradle b/UI/CustomLayout/settings.gradle new file mode 100644 index 0000000000000000000000000000000000000000..9aa1d209f76743b31f6c5e87c35de643c2ca27a4 --- /dev/null +++ b/UI/CustomLayout/settings.gradle @@ -0,0 +1 @@ +include ':entry' \ No newline at end of file