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

頁面資源
- 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 默認(rèn)形狀
- │ │ background_table_layout_down.xml #下面的 grid 形狀
- │ │ background_table_layout_up.xml #上面的 grid 形狀
- │ │
- │ ├─layout
- │ │ ability_main.xml #主顯示頁面
- │ │ app_bar_layout.xml #app工具欄布局頁面
- │ │ grid_item.xml #單個(gè)grid item布局頁面
- │ │
- │ ├─media
- │ │ 5G.png
- │ │ back.png
- │ │ back_white.png
頁面布局
ability_main.xml #主顯示頁
此頁面由DirectionalLayout、StackLayout、DependentLayout 布局構(gòu)成,整體布局是上下布局。
上面時(shí)app工具欄,使用了StackLayout布局,通過includ標(biāo)簽引入到主頁面。
下面是支持拖拽的GridView,由DependentLayout 和DirectionalLayout布局組成,使用的組件有ScrollView、GridView、Text、Button、Image。

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

grid_item.xml #單個(gè)grid item布局頁面

后臺邏輯
1.初始化上面的GridView
先構(gòu)建item模擬數(shù)據(jù)列表,將構(gòu)建好的數(shù)據(jù)傳遞給GridAdapter 初始化item組件列表,通過GridView.setAdapter方法給每個(gè)item組件綁定長按事件,并設(shè)置GridView的TAG屬性(TAG就是指上面的GridView還是下面的GridView)。
- /**
- * 初始化上面的Grid item
- */
- private void initUpListItem() {
- //構(gòu)建item模擬數(shù)據(jù)列表
- 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);
- //將構(gòu)建好的數(shù)據(jù)傳遞給GridAdapter 初始化item組件列表
- GridAdapter adapter = new GridAdapter(slice.getContext(), upperItemList);
- //通過GridView.setAdapter方法給每個(gè)item組件綁定長按事件
- gridView.setAdapter(adapter, longClickListener);
- //設(shè)置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++) {
- //隨意取的圖標(biāo)
- 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.初始化底部的按鈕
這個(gè)地方做了一個(gè)屏幕適配,就是根據(jù)屏幕的寬度、邊距來設(shè)置按鈕的寬度,
同時(shí)添加了按鈕的監(jiān)聽事件,點(diǎn)擊按鈕 關(guān)閉當(dāng)前Ability。
- /**
- * Calculating button width based on screen width.
- * The actual width is the screen width minus the margin of the buttons.
- * 設(shè)置底部 2個(gè)按鈕的寬度
- */
- private void initBottomItem() {
- int screenWidth = AppUtils.getScreenInfo(slice.getContext()).getPointXToInt();
- //計(jì)算按鈕寬度
- int buttonWidth = (screenWidth - AttrHelper.vp2px(80, slice.getContext())) / 2;
- Component leftButton = slice.findComponentById(ResourceTable.Id_bottom_left_button);
- leftButton.setWidth(buttonWidth);
- //關(guān)閉Ability
- leftButton.setClickedListener(component -> slice.terminateAbility());
- Component rightButton = slice.findComponentById(ResourceTable.Id_bottom_right_button);
- rightButton.setWidth(buttonWidth);
- //關(guān)閉Ability
- rightButton.setClickedListener(component -> slice.terminateAbility());
- }
4.初始化app工具欄
這個(gè)沒做什么,似乎是想根據(jù)本地化信息,設(shè)置返回箭頭的方向,因?yàn)橛械恼Z言是從右往左看的。
- /**
- * 檢查指定 Locale 的文本布局是否從右到左。
- * 設(shè)置返回箭頭的方向
- */
- private void initAppBar() {
- if (TextTool.isLayoutRightToLeft(Locale.getDefault())) {
- Image appBackImg = (Image) slice.findComponentById(ResourceTable.Id_left_arrow);
- appBackImg.setRotation(180);
- }
- }
5.初始化監(jiān)聽事件
包括返回按鈕的返回事件、ScrollView的touch事件。
touch事件包含大量的細(xì)節(jié)操作,如拖拽時(shí)有一個(gè)陰影效果,滾動條的處理,拖拽交換結(jié)束的處理,過渡效果,上下grid 有效區(qū)域的計(jì)算,拖拽完成將拖拽的組件添加到對應(yīng)grid的操作等,參照著拿來用吧。
- /**
- * 初始化監(jiān)聽事件,包括返回按鈕返回事件、ScrollView的touch事件
- */
- private void initEventListener() {
- //‘返回按鈕’的監(jiān)聽事件
- 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事件監(jiān)聽,拿來用就可以了
- 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();
- //獲取指針?biāo)饕鄬τ谄莆恢玫?nbsp;x 和 y 坐標(biāo)。
- MmiPoint downPoint = touchEvent.getPointerPosition(touchEvent.getIndex());
- scrollViewTop = (int) downScreenPoint.getY() - (int) downPoint.getY();
- scrollViewLeft = (int) downScreenPoint.getX() - (int) downPoint.getX();
- return true;
- //表示最后一個(gè)手指從屏幕上抬起。這表示交互結(jié)束
- case TouchEvent.PRIMARY_POINT_UP:
- //恢復(fù)下面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;
- }
- );
- }
復(fù)制歸納總結(jié)
1.自定義組件在構(gòu)造函數(shù)中傳遞slice
這樣的目的是便于獲取頁面的其它組件。
- Component itemLayout=LayoutScatter.getInstance(slice.getContext())
- .parse(ResourceTable.Layout_grid_item, null, false);
需要注意的是slice指代的是頁面,但是自定義組件往往是有自己的布局文件的,一般不在slice中,所以不要通過slice獲取自定義組件的子組件,獲取不到,不過可以通過LayoutScatter獲取
- //錯(cuò)誤的方式
- Component gridItem= slice.findComponentById(ResourceTable.Layout_grid_item);
- //正確的方式
- Component gridItem = LayoutScatter.getInstance(context).parse(ResourceTable.Layout_grid_item, null, false);
2.單位轉(zhuǎn)換vp2px
java組件對象寬高、邊距的單位默認(rèn)時(shí)px,
從element中獲取的值需要進(jìn)行單位轉(zhuǎn)換,可以使用AttrHelper.vp2px 將vp轉(zhuǎn)換為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.子組件的獲取
獲取一個(gè)組件對象后,可以使用該組件對象的findComponentById方法繼續(xù)獲取內(nèi)部的子組件
- 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 提供了用于對齊和排列組件的接口,以在帶有表格的組件中顯示組件。 排列方式、行列數(shù)、元件位置均可配置。
例如 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++) {
- //為組件中的長按事件注冊一個(gè)監(jiān)聽器(組件被點(diǎn)擊并按?。?nbsp;
- adapter.getComponentList().get(i).setLongClickedListener(longClickedListener);
- //將組件添加到容器中
- addComponent(adapter.getComponentList().get(i));
- }
- }
效果展示
示例代碼模擬了一下手機(jī)控制中心,編輯快捷開關(guān)的效果
原效果模擬效果
文章相關(guān)附件可以點(diǎn)擊下面的原文鏈接前往下載
原文鏈接:https://harmonyos.51cto.com/posts/4776
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)