<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          鴻蒙OS制作小游戲:數(shù)字華容道(自定義組件踩坑記錄)

          共 1144字,需瀏覽 3分鐘

           ·

          2021-10-28 14:03

          點擊上方藍(lán)色字體,關(guān)注我們

          前兩天看到HarmonyOS開發(fā)者官網(wǎng)上發(fā)布的一個挑戰(zhàn)HarmonyOS分布式趣味應(yīng)用的帖子,然后有個想法想搞一個小游戲出來,結(jié)果三天的時間都卡在了自定義組件上,使用了各種方式方法去實現(xiàn)功能,但是還是沒有達(dá)到預(yù)期的效果,暫時先做個小總結(jié),其實坑有的時候真的很深......


          1


          效果演示


          小應(yīng)用其實也挺簡單,以前也見到過,叫做數(shù)字華容道,當(dāng)你把所在的數(shù)字以順序放置完成后游戲結(jié)束。

          其實屬于益智類的小游戲了;

          最終實現(xiàn)效果:

          當(dāng)前實現(xiàn)效果:



          2


          實現(xiàn)過程


          暫時說一下現(xiàn)在的進(jìn)度,每一個方塊可以表示一個棋子,棋子的名稱也就是3*3的九宮格,1-9的數(shù)字,只是最后一個數(shù)字單獨設(shè)置為空白。點擊空白周圍的棋子可以與這個空白棋子做一次位置調(diào)換,直到將所有棋子順序排列完成為止。

          這里先說一個這個棋子,棋子有兩個東西需要被記住,一個是棋子的坐標(biāo)就是在九宮格里面的位置,另一個就是棋子的名稱;所以選擇使用自定義組件的方式將坐標(biāo)和名稱進(jìn)行一個綁定。

          Position.java

          /**
          ?*?定義棋子的位置
          ?*/

          public?class?Position?{
          ????public?int?sizeX;?//?總列數(shù)
          ????public?int?sizeY;?//?總行數(shù)
          ????public?int?x;?//?橫坐標(biāo)
          ????public?int?y;?//?縱坐標(biāo)

          ????public?Position()?{
          ????}

          ????public?Position(int?sizeX,?int?sizeY)?{
          ????????this.sizeX?=?sizeX;
          ????????this.sizeY?=?sizeY;
          ????}

          ????public?Position(int?sizeX,?int?sizeY,?int?x,?int?y)?{
          ????????this.sizeX?=?sizeX;
          ????????this.sizeY?=?sizeY;
          ????????this.x?=?x;
          ????????this.y?=?y;
          ????}

          ????public?Position(Position?orig)?{
          ????????this(orig.sizeX,?orig.sizeY,?orig.x,?orig.y);
          ????}

          ????/**
          ?????*?移動到下一個位置
          ?????*/

          ????public?boolean?moveToNextPosition()?{
          ????????if?(x?1)?{
          ????????????x++;
          ????????}?else?if?(y?1)?{
          ????????????x?=?0;
          ????????????y++;
          ????????}?else?{
          ????????????return?false;
          ????????}
          ????????return?true;
          ????}

          ????@Override
          ????public?String?toString()?{
          ????????return?"Position{"?+
          ????????????????"x="?+?x?+
          ????????????????",?y="?+?y?+
          ????????????????'}';
          ????}
          }

          CubeView.java

          public?class?CubeView?extends?ComponentContainer?{

          ????private?Position?mPosition;
          ????private?int?mNumber;

          ????private?Text?mTextCub;
          ????private?int?mTextSize?=?20;

          ????public?CubeView(Context?context)?{
          ????????super(context);
          ????????init();
          ????}

          ????public?CubeView(Context?context,?AttrSet?attrSet)?{
          ????????super(context,?attrSet);
          ????????init();
          ????}

          ????private?void?init(){
          ????????Component?component?=?LayoutScatter.getInstance(getContext()).parse(ResourceTable.Layout_cube_view_item,?this,?false);
          ????????mTextCub?=?(Text)?component.findComponentById(ResourceTable.Id_tv_item);
          ????????mTextCub.setTextSize(mTextSize,?Text.TextSizeType.VP);
          ????}

          ????public?void?setNumber(int?n)?{
          ????????mNumber?=?n;
          ????????mTextCub.setText(String.valueOf(n));
          ????}


          ????public?int?getNumber()?{
          ????????return?mNumber;
          ????}

          ????public?Position?getPosition()?{
          ????????return?mPosition;
          ????}

          ????public?void?setPosition(Position?position)?{
          ????????this.mPosition?=?position;
          ????}

          ????@Override
          ????public?String?toString()?{
          ????????return?"CubeView{"?+
          ????????????????"mPosition="?+?mPosition?+
          ????????????????",?mNumber="?+?mNumber?+
          ????????????????'}';
          ????}
          }

          cube_view_item.xml


          <DirectionalLayout
          ????xmlns:ohos="http://schemas.huawei.com/res/ohos"
          ????ohos:height="match_content"
          ????ohos:width="match_content">

          ????<Text
          ????????ohos:id="$+id:tv_item"
          ????????ohos:height="100vp"
          ????????ohos:width="100vp"
          ????????ohos:background_element="$graphic:cube_view_bg"
          ????????ohos:text="1"
          ????????ohos:text_alignment="center"
          ????????ohos:text_color="$color:cubeViewStroke"
          ????????ohos:text_size="20vp">

          ????????>Text>
          DirectionalLayout>

          到這問題就來了,因為在代碼中只是使用到了setText()方法,那么有人會問我為什么不直接繼承Text組件,多寫一個布局有點麻煩了不是?

          第一個坑


          這里就是第一個坑了,因為在以前寫Android自定義控件的時候,對于簡單的組件來說直接繼承它的組件名稱就可以了,不用去繼承公共類然后再去使用布局去定位到里面的組件。原本我也是這么寫的,CubeView直接繼承Text沒有毛病可以使用,可以看到兩者間并無差別。

          public?class?CubeView?extends?Text?{

          ????private?Position?mPosition;
          ????private?int?mNumber;


          ????public?CubeView(Context?context)?{
          ????????super(context);
          ????????init();
          ????}

          ????public?CubeView(Context?context,?AttrSet?attrSet)?{
          ????????super(context,?attrSet);
          ????????init();
          ????}

          ????private?void?init(){
          ????????
          ????}

          ????public?void?setNumber(int?n)?{
          ????????mNumber?=?n;
          ????????setText(String.valueOf(n));
          ????}


          ????public?int?getNumber()?{
          ????????return?mNumber;
          ????}

          ????public?Position?getPosition()?{
          ????????return?mPosition;
          ????}

          ????public?void?setPosition(Position?position)?{
          ????????this.mPosition?=?position;
          ????}

          ????@Override
          ????public?String?toString()?{
          ????????return?"CubeView{"?+
          ????????????????"mPosition="?+?mPosition?+
          ????????????????",?mNumber="?+?mNumber?+
          ????????????????'}';
          ????}
          }

          但是在調(diào)用組件的時候出現(xiàn)了問題,因為我需要把這個棋子的組件添加到我的棋盤布局中,那么就需要先引入這個組件。引入組件后出問題了,布局報錯(在原來Android引入自定義組件的時候,單個組件也是可以直接引入的);報錯原因是,我最外層沒有放置布局導(dǎo)致不能直接識別單個組件,但是如果我加上一個布局的話,文件不會報錯,但是在我的棋盤上不能拿到這個棋子的組件;

          為此我只能將棋子的自定義組件寫成了布局引入方式。

          到這里,棋子的開發(fā)工作也就基本做完了,下面要對棋盤進(jìn)行布局。還是選擇自定義組件的方式;

          cube_view.xml


          <com.example.codelabs_games_hrd.CubeView
          ????xmlns:ohos="http://schemas.huawei.com/res/ohos"
          ????ohos:background_element="$graphic:cube_view_bg"
          ????ohos:height="100vp"
          ????ohos:width="100vp"
          ????ohos:id="$+id:title_bar_left"
          ????ohos:text="1"
          ????ohos:text_alignment="center"
          ????ohos:text_color="$color:cubeViewStroke"
          ????ohos:text_size="20vp"

          ????>

          com.example.codelabs_games_hrd.CubeView>

          ability_game.xml


          <StackLayout
          ????xmlns:ohos="http://schemas.huawei.com/res/ohos"
          ????ohos:height="match_parent"
          ????ohos:width="match_parent"
          ????ohos:background_element="$color:cubeViewBg">


          ????<com.example.codelabs_games_hrd.BoardView
          ????????ohos:id="$+id:board"
          ????????ohos:height="300vp"
          ????????ohos:width="300vp"
          ????????ohos:layout_alignment="center"
          ????????ohos:background_element="$color:boardViewBg">

          ????com.example.codelabs_games_hrd.BoardView>

          ????<Text
          ????????ohos:id="$+id:tvCheat"
          ????????ohos:height="10vp"
          ????????ohos:width="10vp">
          Text>

          ????<Text
          ????????ohos:id="$+id:mask"
          ????????ohos:height="match_parent"
          ????????ohos:width="match_parent"
          ????????ohos:background_element="$color:cubeViewBg"
          ????????ohos:text="123456789"
          ????????ohos:text_size="48vp">
          Text>

          StackLayout>

          BoardView.java

          public?class?BoardView?extends?ComponentContainer?implements?ComponentContainer.EstimateSizeListener,?ComponentContainer.ArrangeListener?{
          ????private?static?final?String?TAG?=?"BoardView";
          ????/**
          ?????*?每一行有多少個棋子
          ?????*/

          ????private?int?mSizeX?=?3;
          ????/**
          ?????*?有多少行棋子
          ?????*/

          ????private?int?mSizeY?=?3;


          ????private?int?maxWidth?=?0;

          ????private?int?maxHeight?=?0;

          ????private?int?mChildSize;

          ????private?Position?mBlankPos;
          ????private?CubeView[]?mChildren;

          ????private?OnFinishListener?mFinishListener;

          ????private?int?xx?=?0;

          ????private?int?yy?=?0;

          ????private?int?lastHeight?=?0;

          ????//?子組件索引與其布局?jǐn)?shù)據(jù)的集合
          ????private?final?Map?axis?=?new?HashMap<>();

          ????//位置及大小
          ????private?static?class?Layout?{
          ????????int?positionX?=?0;
          ????????int?positionY?=?0;
          ????????int?width?=?0;
          ????????int?height?=?0;
          ????}


          ????private?void?invalidateValues()?{
          ????????xx?=?0;
          ????????yy?=?0;
          ????????maxWidth?=?0;
          ????????maxHeight?=?0;
          ????????axis.clear();
          ????}

          ????public?BoardView(Context?context)?{
          ????????super(context);
          ????}

          ????public?BoardView(Context?context,?AttrSet?attrs)?{
          ????????super(context,?attrs);
          ????????setEstimateSizeListener(this);
          ????????setArrangeListener(this);
          ????????init();
          ????}

          ????private?void?init()?{
          ????????mChildSize?=?mSizeX?*?mSizeY?-?1;
          ????????mChildren?=?new?CubeView[mChildSize];
          ????????Position?p?=?new?Position(mSizeX,?mSizeY);
          ????????for?(int?i?=?0;?i?????????//添加棋子
          ????????????CubeView?view?=?(CubeView)?LayoutScatter.getInstance(getContext()).parse(ResourceTable.Layout_cube_view,?this,?false);
          ????????????view.setPosition(new?Position(p));
          ????????????view.setClickedListener(component?->?moveChildToBlank(view));
          ????????????addComponent(view);
          ????????????p.moveToNextPosition();
          ????????????mChildren[i]?=?view;
          ????????}
          ????????//最后一個空白棋子
          ????????mBlankPos?=?new?Position(mSizeX,?mSizeY,?mSizeX?-?1,?mSizeY?-?1);
          ????}



          ????public?void?setData(List?data)?{
          ????????for?(int?i?=?0;?i?????????????CubeView?view?=?(CubeView)?getComponentAt(i);
          ????????????view.setNumber(data.get(i));
          ????????}
          ????}

          ????//測量監(jiān)聽方法
          ????@Override
          ????public?boolean?onEstimateSize(int?widthEstimatedConfig,?int?heightEstimatedConfig)?{
          ????????invalidateValues();
          ????????//測量子組件的大小
          ????????measureChildren(?widthEstimatedConfig,??heightEstimatedConfig);
          ???????//關(guān)聯(lián)子組件的索引與其布局?jǐn)?shù)據(jù)
          ????????for?(int?idx?=?0;?idx?????????????CubeView?childView?=?(CubeView)?getComponentAt(idx);
          ????????????addChild(childView,?idx,?EstimateSpec.getSize(widthEstimatedConfig));
          ????????}
          ????????//測量本身大小
          ????????setEstimatedSize(?widthEstimatedConfig,??heightEstimatedConfig);


          ????????return?true;
          ????}

          ????private?void?measureChildren(int?widthEstimatedConfig,?int?heightEstimatedConfig)?{
          ????????for?(int?idx?=?0;?idx?????????????CubeView?childView?=?(CubeView)?getComponentAt(idx);
          ????????????if?(childView?!=?null)?{
          ????????????????LayoutConfig?lc?=?childView.getLayoutConfig();
          ????????????????int?childWidthMeasureSpec;
          ????????????????int?childHeightMeasureSpec;
          ????????????????if?(lc.width?==?LayoutConfig.MATCH_CONTENT)?{
          ????????????????????childWidthMeasureSpec?=?EstimateSpec.getSizeWithMode(lc.width,?EstimateSpec.NOT_EXCEED);
          ????????????????}?else?if?(lc.width?==?LayoutConfig.MATCH_PARENT)?{
          ????????????????????int?parentWidth?=?EstimateSpec.getSize(widthEstimatedConfig);
          ????????????????????int?childWidth?=?parentWidth?-?childView.getMarginLeft()?-?childView.getMarginRight();
          ????????????????????childWidthMeasureSpec?=?EstimateSpec.getSizeWithMode(childWidth,?EstimateSpec.PRECISE);
          ????????????????}?else?{
          ????????????????????childWidthMeasureSpec?=?EstimateSpec.getSizeWithMode(lc.width,?EstimateSpec.PRECISE);
          ????????????????}

          ????????????????if?(lc.height?==?LayoutConfig.MATCH_CONTENT)?{
          ????????????????????childHeightMeasureSpec?=?EstimateSpec.getSizeWithMode(lc.height,?EstimateSpec.NOT_EXCEED);
          ????????????????}?else?if?(lc.height?==?LayoutConfig.MATCH_PARENT)?{
          ????????????????????int?parentHeight?=?EstimateSpec.getSize(heightEstimatedConfig);
          ????????????????????int?childHeight?=?parentHeight?-?childView.getMarginTop()?-?childView.getMarginBottom();
          ????????????????????childHeightMeasureSpec?=?EstimateSpec.getSizeWithMode(childHeight,?EstimateSpec.PRECISE);
          ????????????????}?else?{
          ????????????????????childHeightMeasureSpec?=?EstimateSpec.getSizeWithMode(lc.height,?EstimateSpec.PRECISE);
          ????????????????}
          ????????????????childView.estimateSize(childWidthMeasureSpec,?childHeightMeasureSpec);
          ????????????}
          ????????}
          ????}


          ????private?void?measureSelf(int?widthEstimatedConfig,?int?heightEstimatedConfig)?{
          ????????int?widthSpce?=?EstimateSpec.getMode(widthEstimatedConfig);
          ????????int?heightSpce?=?EstimateSpec.getMode(heightEstimatedConfig);
          ????????int?widthConfig?=?0;
          ????????switch?(widthSpce)?{
          ????????????case?EstimateSpec.UNCONSTRAINT:
          ????????????case?EstimateSpec.PRECISE:
          ????????????????int?width?=?EstimateSpec.getSize(widthEstimatedConfig);
          ????????????????widthConfig?=?EstimateSpec.getSizeWithMode(width,?EstimateSpec.PRECISE);
          ????????????????break;
          ????????????case?EstimateSpec.NOT_EXCEED:
          ????????????????widthConfig?=?EstimateSpec.getSizeWithMode(maxWidth,?EstimateSpec.PRECISE);
          ????????????????break;
          ????????????default:
          ????????????????break;
          ????????}

          ????????int?heightConfig?=?0;
          ????????switch?(heightSpce)?{
          ????????????case?EstimateSpec.UNCONSTRAINT:
          ????????????case?EstimateSpec.PRECISE:
          ????????????????int?height?=?EstimateSpec.getSize(heightEstimatedConfig);
          ????????????????heightConfig?=?EstimateSpec.getSizeWithMode(height,?EstimateSpec.PRECISE);
          ????????????????break;
          ????????????case?EstimateSpec.NOT_EXCEED:
          ????????????????heightConfig?=?EstimateSpec.getSizeWithMode(maxHeight,?EstimateSpec.PRECISE);
          ????????????????break;
          ????????????default:
          ????????????????break;
          ????????}
          ????????setEstimatedSize(widthConfig,?heightConfig);
          ????}



          ????//每個棋子組件的位置及大小
          ????@Override
          ????public?boolean?onArrange(int?l,?int?t,?int?r,?int?b)?{

          ????????for?(int?idx?=?0;?idx?????????????Component?childView?=?getComponentAt(idx);
          ????????????Layout?layout?=?axis.get(idx);
          ????????????if?(layout?!=?null)?{
          ????????????????childView.arrange(layout.positionX,?layout.positionY,?layout.width,?layout.height);
          ????????????}
          ????????}
          ????????return?true;
          ????}


          ????private?void?addChild(CubeView?component,?int?id,?int?layoutWidth)?{
          ????????Layout?layout?=?new?Layout();
          ????????layout.positionX?=?xx?+?component.getMarginLeft();
          ????????layout.positionY?=?yy?+?component.getMarginTop();
          ????????layout.width?=?component.getEstimatedWidth();
          ????????layout.height?=?component.getEstimatedHeight();
          ????????if?((xx?+?layout.width)?>?layoutWidth)?{
          ????????????xx?=?0;
          ????????????yy?+=?lastHeight;
          ????????????lastHeight?=?0;
          ????????????layout.positionX?=?xx?+?component.getMarginLeft();
          ????????????layout.positionY?=?yy?+?component.getMarginTop();
          ????????}
          ????????axis.put(id,?layout);
          ????????lastHeight?=?Math.max(lastHeight,?layout.height?+?component.getMarginBottom());
          ????????xx?+=?layout.width?+?component.getMarginRight();
          ????????maxWidth?=?Math.max(maxWidth,?layout.positionX?+?layout.width?+?component.getMarginRight());
          ????????maxHeight?=?Math.max(maxHeight,?layout.positionY?+?layout.height?+?component.getMarginBottom());
          ????}
          ????
          ????//點擊棋子后進(jìn)行位置切換
          ????public?void?moveChildToBlank(@org.jetbrains.annotations.NotNull?CubeView?child)?{
          ????????Position?childPos?=?child.getPosition();
          ????????Position?dstPos?=?mBlankPos;
          ????????if?(childPos.x?==?dstPos.x?&&?Math.abs(childPos.y?-?dstPos.y)?==?1?||
          ????????????????childPos.y?==?dstPos.y?&&?Math.abs(childPos.x?-?dstPos.x)?==?1)?{
          ????????????child.setPosition(dstPos);
          ????????????//component中沒有對組件進(jìn)行物理平移的方法
          ????????????//setTranslationX(),setTranslationY()兩個方法沒有
          ????????????child.setTranslationX(dstPos.x?*?xx);
          ????????????child.setTranslationY(dstPos.y?*?yy);

          ????????????mBlankPos?=?childPos;
          ????????????mStepCounter.add();
          ????????}
          ????????checkPosition();
          ????}

          ????/**
          ?????*?檢查所有格子位置是否正確
          ?????*/

          ????private?void?checkPosition()?{
          ????????if?(mBlankPos.x?!=?mSizeX?-?1?||?mBlankPos.y?!=?mSizeY?-?1)?{
          ????????????return;
          ????????}

          ????????for?(CubeView?child?:?mChildren)?{
          ????????????int?num?=?child.getNumber();
          ????????????int?x?=?child.getPosition().x;
          ????????????int?y?=?child.getPosition().y;
          ????????????if?(y?*?mSizeX?+?x?+?1?!=?num)?{
          ????????????????return;
          ????????????}
          ????????}

          ????????if?(mFinishListener?!=?null)?{
          ????????????mFinishListener.onFinished(mStepCounter.step);
          ????????}
          ????????for?(CubeView?child?:?mChildren)?{
          ????????????child.setClickable(false);
          ????????}
          ????}

          ????public?void?setOnFinishedListener(OnFinishListener?l)?{
          ????????mFinishListener?=?l;
          ????}

          ????public?interface?OnFinishListener?{
          ????????void?onFinished(int?step);
          ????}

          ????public?int?getSizeX()?{
          ????????return?mSizeX;
          ????}

          ????public?int?getSizeY()?{
          ????????return?mSizeY;
          ????}

          ????/**
          ?????*?步數(shù)統(tǒng)計
          ?????*/

          ????class?StepCounter?{
          ????????private?int?step?=?0;

          ????????void?add()?{
          ????????????step++;
          ????????}

          ????????void?clear()?{
          ????????????step?=?0;
          ????????}
          ????}

          ????private?StepCounter?mStepCounter?=?new?StepCounter();

          }

          棋盤的自定義布局也完成了。棋盤的布局稍微復(fù)雜一點,因為需要根據(jù)棋盤的大小計算每一個棋子的大小,還需要對棋子進(jìn)行綁定,尤其是需要對最后一個棋子做空白處理。

          然后點擊棋子進(jìn)行棋子的平移,平移后與其位置進(jìn)行互換。


          第二個坑


          [外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-PvmUPB0c-1634810943992)(C:\Users\HHCH\AppData\Roaming\Typora\typora-user-images\image-20211021175237912.png)]

          點擊棋子進(jìn)行位置平移,因為在API里面沒有找到component公共組件下的平移方法,setTranslationX()/setTranslationY()方法,沒有辦法做到組件的物理位置平移,導(dǎo)致大家看到開頭演示的效果,點擊后與空白位置了切換但是重新對其進(jìn)行物理位置賦值的時候沒有辦法去賦值,這個問題困擾了我兩天。

          現(xiàn)在還是沒有解決掉,試著想想是不是可以使用TouchEvent事件一個滑動處理,不做點擊事件做滑動事件。

          最終現(xiàn)在項目的結(jié)構(gòu)如下:



          3


          總結(jié)


          后面還會繼續(xù)去完善,以至于到整個功能可以正常去使用,踩坑還是要踩的,總會有收獲的時候.....


          往期推薦



          點擊閱讀原文,更精彩~
          瀏覽 118
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  在线免费日韩 | 国产ss视频在线观看 | 特级特黄特色大片免费看 | 国产A片免费领取 | 日本精品人妻无码免费大全 |