鴻蒙HarmonyOS官方模板學習 之 Grid Ability(Java)
Grid Ability(Java)
介紹
使用Java語言開發,用于Phone設備的Feature Ability模板,使用XML布局,顯示內容為兩部分網格表,網格每行顯示4個項目,網格內元素可進行拖拽排序。
搭建環境
安裝DevEco Studio,詳情請參考DevEco Studio下載。
設置DevEco Studio開發環境,DevEco Studio開發環境需要依賴于網絡環境,需要連接上網絡才能確保工具的正常使用,可以根據如下兩種情況來配置開發環境:
如果可以直接訪問Internet,只需進行下載HarmonyOS SDK操作。
如果網絡不能直接訪問Internet,需要通過代理服務器才可以訪問,請參考配置開發環境。
代碼結構解讀
注意:'#'代表注釋
后臺功能
- gridabilityjava
- │ MainAbility.java
- │ MyApplication.java
- │
- ├─component
- │ DragLayout.java #自定義的拖拽功能組件
- │ GridView.java #自定義的Grid視圖組件,extends TableLayout
- │
- ├─model
- │ GridItemInfo.java #Grid item 模型
- │
- ├─provider
- │ GridAdapter.java #給Grid提供實例化好的item 組件列表;提供了計算單個item的寬度的方法
- │
- ├─slice
- │ MainAbilitySlice.java #主能力頁,負責實例化自定義的DragLayout拖拽組件
- │
- └─utils
- AppUtils.java #工具類,提供了從element資源中中獲取value;獲取屏幕的坐標的方法
這是幾個java類之間的關系

頁面資源
- resources
- ├─base
- │ ├─element
- │ │ color.json
- │ │ float.json
- │ │ integer.json
- │ │ string.json
- │ │
- │ ├─graphic
- │ │ background_bottom_button.xml #頁面底部按鈕形狀
- │ │ background_bottom_layout.xml #頁面底部布局形狀
- │ │ background_item_active_button.xml #grid item 激活形狀
- │ │ background_item_button.xml #grid item 默認形狀
- │ │ background_table_layout_down.xml #下面的 grid 形狀
- │ │ background_table_layout_up.xml #上面的 grid 形狀
- │ │
- │ ├─layout
- │ │ ability_main.xml #主顯示頁面
- │ │ app_bar_layout.xml #app工具欄布局頁面
- │ │ grid_item.xml #單個grid item布局頁面
- │ │
- │ ├─media
- │ │ 5G.png
- │ │ back.png
- │ │ back_white.png
頁面布局
ability_main.xml #主顯示頁
此頁面由DirectionalLayout、StackLayout、DependentLayout 布局構成,整體布局是上下布局。
上面時app工具欄,使用了StackLayout布局,通過includ標簽引入到主頁面。
下面是支持拖拽的GridView,由DependentLayout 和DirectionalLayout布局組成,使用的組件有ScrollView、GridView、Text、Button、Image。

app_bar_layout.xml #app工具欄布局頁面

grid_item.xml #單個grid item布局頁面

后臺邏輯
1.初始化上面的GridView
先構建item模擬數據列表,將構建好的數據傳遞給GridAdapter 初始化item組件列表,通過GridView.setAdapter方法給每個item組件綁定長按事件,并設置GridView的TAG屬性(TAG就是指上面的GridView還是下面的GridView)。
- /**
- * 初始化上面的Grid item
- */
- private void initUpListItem() {
- //構建item模擬數據列表
- List<GridItemInfo> upperItemList = new ArrayList<>();
- for (int i = 0; i < UP_ITEM_COUNT; i++) {
- int iconId = icons[i];
- String text = texts[i];
- upperItemList.add(new GridItemInfo(text, iconId, UP_GRID_TAG));
- }
- GridView gridView = (GridView) slice.findComponentById(ResourceTable.Id_grid_view_up);
- //將構建好的數據傳遞給GridAdapter 初始化item組件列表
- GridAdapter adapter = new GridAdapter(slice.getContext(), upperItemList);
- //通過GridView.setAdapter方法給每個item組件綁定長按事件
- gridView.setAdapter(adapter, longClickListener);
- //設置GridView的TAG屬性
- gridView.setTag(UP_GRID_TAG);
- }
2.初始化下面的GridView
邏輯同上
- /**
- * 初始化下面的Grid item
- */
- private void initDownListItem() {
- String itemText = AppUtils.getStringResource(slice.getContext(), ResourceTable.String_grid_item_text);
- List<GridItemInfo> lowerItemList = new ArrayList<>();
- for (int i = 0; i < DOWN_ITEM_COUNT; i++) {
- //隨意取的圖標
- int iconId = icons[i + 5];
- String text = texts[i + 5];
- lowerItemList.add(new GridItemInfo(text, iconId, DOWN_GRID_TAG));
- }
- if (slice.findComponentById(ResourceTable.Id_grid_view_down) instanceof GridView) {
- GridView gridView = (GridView) slice.findComponentById(ResourceTable.Id_grid_view_down);
- GridAdapter adapter = new GridAdapter(slice.getContext(), lowerItemList);
- gridView.setAdapter(adapter, longClickListener);
- gridView.setTag(DOWN_GRID_TAG);
- }
- }
3.初始化底部的按鈕
這個地方做了一個屏幕適配,就是根據屏幕的寬度、邊距來設置按鈕的寬度,
同時添加了按鈕的監聽事件,點擊按鈕 關閉當前Ability。
- /**
- * Calculating button width based on screen width.
- * The actual width is the screen width minus the margin of the buttons.
- * 設置底部 2個按鈕的寬度
- */
- private void initBottomItem() {
- int screenWidth = AppUtils.getScreenInfo(slice.getContext()).getPointXToInt();
- //計算按鈕寬度
- int buttonWidth = (screenWidth - AttrHelper.vp2px(80, slice.getContext())) / 2;
- Component leftButton = slice.findComponentById(ResourceTable.Id_bottom_left_button);
- leftButton.setWidth(buttonWidth);
- //關閉Ability
- leftButton.setClickedListener(component -> slice.terminateAbility());
- Component rightButton = slice.findComponentById(ResourceTable.Id_bottom_right_button);
- rightButton.setWidth(buttonWidth);
- //關閉Ability
- rightButton.setClickedListener(component -> slice.terminateAbility());
- }
4.初始化app工具欄
這個沒做什么,似乎是想根據本地化信息,設置返回箭頭的方向,因為有的語言是從右往左看的。
- /**
- * 檢查指定 Locale 的文本布局是否從右到左。
- * 設置返回箭頭的方向
- */
- private void initAppBar() {
- if (TextTool.isLayoutRightToLeft(Locale.getDefault())) {
- Image appBackImg = (Image) slice.findComponentById(ResourceTable.Id_left_arrow);
- appBackImg.setRotation(180);
- }
- }
5.初始化監聽事件
包括返回按鈕的返回事件、ScrollView的touch事件。
touch事件包含大量的細節操作,如拖拽時有一個陰影效果,滾動條的處理,拖拽交換結束的處理,過渡效果,上下grid 有效區域的計算,拖拽完成將拖拽的組件添加到對應grid的操作等,參照著拿來用吧。
- /**
- * 初始化監聽事件,包括返回按鈕返回事件、ScrollView的touch事件
- */
- private void initEventListener() {
- //‘返回按鈕’的監聽事件
- if (slice.findComponentById(ResourceTable.Id_left_arrow) instanceof Image) {
- Image backIcon = (Image) slice.findComponentById(ResourceTable.Id_left_arrow);
- //
- backIcon.setClickedListener(component -> slice.terminateAbility());
- }
- //ScrollView的 Touch事件監聽,拿來用就可以了
- scrollView.setTouchEventListener(
- (component, touchEvent) -> {
- //按下屏幕的位置
- MmiPoint downScreenPoint = touchEvent.getPointerScreenPosition(touchEvent.getIndex());
- switch (touchEvent.getAction()) {
- //表示第一根手指觸摸屏幕。這表示交互的開始
- case TouchEvent.PRIMARY_POINT_DOWN:
- currentDragX = (int) downScreenPoint.getX();
- currentDragY = (int) downScreenPoint.getY();
- //獲取指針索引相對于偏移位置的 x 和 y 坐標。
- MmiPoint downPoint = touchEvent.getPointerPosition(touchEvent.getIndex());
- scrollViewTop = (int) downScreenPoint.getY() - (int) downPoint.getY();
- scrollViewLeft = (int) downScreenPoint.getX() - (int) downPoint.getX();
- return true;
- //表示最后一個手指從屏幕上抬起。這表示交互結束
- case TouchEvent.PRIMARY_POINT_UP:
- //恢復下面grid的描述
- changeTableLayoutDownDesc(ResourceTable.String_down_grid_layout_desc_text);
- case TouchEvent.CANCEL:
- if (isViewOnDrag) {
- selectedView.setScale(1.0f, 1.0f);
- selectedView.setAlpha(1.0f);
- selectedView.setVisibility(Component.VISIBLE);
- isViewOnDrag = false;
- isScroll = false;
- return true;
- }
- break;
- //表示手指在屏幕上移動
- case TouchEvent.POINT_MOVE:
- if (!isViewOnDrag) {
- break;
- }
- int pointX = (int) downScreenPoint.getX();
- int pointY = (int) downScreenPoint.getY();
- this.exchangeItem(pointX, pointY);
- if (UP_GRID_TAG.equals(selectedView.getTag())) {
- this.swapItems(pointX, pointY);
- }
- this.handleScroll(pointY);
- return true;
- }
- return false;
- }
- );
- }
復制歸納總結
1.自定義組件在構造函數中傳遞slice
這樣的目的是便于獲取頁面的其它組件。
- Component itemLayout=LayoutScatter.getInstance(slice.getContext())
- .parse(ResourceTable.Layout_grid_item, null, false);
需要注意的是slice指代的是頁面,但是自定義組件往往是有自己的布局文件的,一般不在slice中,所以不要通過slice獲取自定義組件的子組件,獲取不到,不過可以通過LayoutScatter獲取
- //錯誤的方式
- Component gridItem= slice.findComponentById(ResourceTable.Layout_grid_item);
- //正確的方式
- Component gridItem = LayoutScatter.getInstance(context).parse(ResourceTable.Layout_grid_item, null, false);
2.單位轉換vp2px
java組件對象寬高、邊距的單位默認時px,
從element中獲取的值需要進行單位轉換,可以使用AttrHelper.vp2px 將vp轉換為px。
- if (gridItem.findComponentById(ResourceTable.Id_grid_item_text) instanceof Text) {
- Text textItem = (Text) gridItem.findComponentById(ResourceTable.Id_grid_item_text);
- textItem.setText(item.getItemText());
- textItem.setTextSize(AttrHelper.fp2px(10, context));
- }
3.子組件的獲取
獲取一個組件對象后,可以使用該組件對象的findComponentById方法繼續獲取內部的子組件
- Component gridItem = LayoutScatter.getInstance(context).parse(ResourceTable.Layout_grid_item, null, false);
- Image imageItem = (Image) gridItem.findComponentById(ResourceTable.Id_grid_item_image);
4.TableLayout的使用
TableLayout繼承自ComponentContainer,提供用于在帶有表格的組件中排列組件的布局。
TableLayout 提供了用于對齊和排列組件的接口,以在帶有表格的組件中顯示組件。 排列方式、行列數、元件位置均可配置。
例如 removeAllComponents();可以用來清除 ComponentContainer 管理的所有組件,addComponent 用來將組件添加到ComponentContainer 容器中。示例中GridView就是繼承自TableLayout。
- /**
- * The setAdapter
- *
- * @param adapter adapter
- * @param longClickedListener longClickedListener
- */
- void setAdapter(GridAdapter adapter, LongClickedListener longClickedListener) {
- //清除 ComponentContainer 管理的所有組件
- removeAllComponents();
- //遍歷item組件列表
- for (int i = 0; i < adapter.getComponentList().size(); i++) {
- //為組件中的長按事件注冊一個監聽器(組件被點擊并按住)
- adapter.getComponentList().get(i).setLongClickedListener(longClickedListener);
- //將組件添加到容器中
- addComponent(adapter.getComponentList().get(i));
- }
- }
效果展示
示例代碼模擬了一下手機控制中心,編輯快捷開關的效果
原效果模擬效果
文章相關附件可以點擊下面的原文鏈接前往下載
原文鏈接:https://harmonyos.51cto.com/posts/4776