鴻蒙分布式1024一起玩專注游戲
一, 前言
1024一起專注游戲是在屏幕上畫上N x N個方格(如4x4共16個),格子內任意填寫上從1開始順序生成的數字(如1 ~ 16共16個數字)。游戲時,要求玩家用手指按從小到大(如1 ~ 16)的順序依次指出其位置,按完所有數字后,顯示所用的時間(秒)。所用時間越短,注意力水平越高。能夠培養注意力集中、分配、控制能力;拓展視幅;加快視頻;提高視覺的穩定性、辨別人、定向搜索能力。此游戲為最簡單,最有效也是最科學的注意力訓練方法。尋找目標數字時,注意力是需要極度集中的,把這短暫的高強度的集中精力過程反復練習,大腦的集中注意力功能就會不斷的加固,提高。注意水平越來越高。
同時,1024一起專注游戲使用了鴻蒙分布式協同技術,在訓練小孩子專注力和耐力時,大人也可以一起陪伴訓練,只要兩臺鴻蒙系統手機或一臺手機一臺平板,大人,小孩就可以同時一起在玩一個游戲,比如大人在其中一臺手機上按了一部份小數字,然后點擊分布式協同圖標,拉起另一臺手機的1024一起專注游戲,小孩可以接著按大人沒有按完的數字,最終顯示出所用的時間。
二, 實現效果
開發工具環境下視頻:https://www.bilibili.com/video/BV1B34y1m7M5?spm_id_from=333.999.0.0
手機+手機環境下視頻:https://www.bilibili.com/video/BV1kh411b7QM?spm_id_from=333.999.0.0
手機+平板環境下視頻:https://www.bilibili.com/video/BV1ov411M7sq?spm_id_from=333.999.0.0


三, 創建工程
在這當作你已經安裝好最新版本DevEco-Studio開發工具, 點擊File -> New -> New Project… 彈出Create HarmonyOS Project窗口, 這里我選擇空白Java模板創建, 上一個視頻播放實例是用JS寫的界面,這個游戲界面就用Java來寫,還是JS寫界面快,調試也快些.



四, 主界面開發
在展示源代碼之前,先介紹一下使用到了JAVA哪些組件:
DirectionalLayout, TableLayout, DependentLayout, Button, Image, Text, ListContainer, CommonDialog,通過查看Java UI參考文檔,就可以做出你喜歡的應用了。
先介紹公共類Java代碼,有了這些公共類,以后做類似功能的應用,可以直接復制公共類文件可以使用:
LogUtil 日志打印類:
- public class LogUtil {
- private static final HiLogLabel LABEL_LOG = new HiLogLabel(3, 0xD000F00, "1024Game");
- private static final String LOG_FORMAT = "%{public}s: %{public}s";
- private LogUtil() {
- }
- public static void debug(String className, String msg) {
- HiLog.debug(LABEL_LOG, LOG_FORMAT, className, msg);
- }
- public static void info(String className, String msg) {
- HiLog.info(LABEL_LOG, LOG_FORMAT, className, msg);
- }
- public static void info(Class<?> classType, final String format, Object... args) {
- String buffMsg = String.format(Locale.ROOT, format, args);
- HiLog.info(LABEL_LOG, LOG_FORMAT, classType == null ? "null" : classType.getSimpleName(), buffMsg);
- }
- public static void error(String tag, String msg) {
- HiLog.error(LABEL_LOG, LOG_FORMAT, tag, msg);
- }
- }
SelectDeviceDialog設備選擇對話框:
- public class SelectDeviceDialog {
- private static final int DIALOG_WIDTH = 840;
- private static final int DIALOG_HEIGHT = 900;
- private CommonDialog commonDialog;
- public SelectDeviceDialog(Context context, List<DeviceInfo> devices, SelectResultListener listener) {
- initView(context, devices, listener);
- }
- private void initView(Context context, List<DeviceInfo> devices, SelectResultListener listener) {
- // 創建一個公共對話框
- commonDialog = new CommonDialog(context);
- // 設置對齊方式居中
- commonDialog.setAlignment(LayoutAlignment.CENTER);
- // 設置對話框尺寸
- commonDialog.setSize(DIALOG_WIDTH, DIALOG_HEIGHT);
- // 設置對話框自動關閉
- commonDialog.setAutoClosable(true);
- // 加載XML布局文件
- Component dialogLayout =
- LayoutScatter.getInstance(context).parse(ResourceTable.Layout_dialog_select_device, null, false);
- // 設置對話框內容
- commonDialog.setContentCustomComponent(dialogLayout);
- // 查找到列表容器
- if (dialogLayout.findComponentById(ResourceTable.Id_list_devices) instanceof ListContainer) {
- // 獲取列表容器對象
- ListContainer devicesListContainer =
- (ListContainer) dialogLayout.findComponentById(ResourceTable.Id_list_devices);
- // 設備列表適配器
- DevicesListAdapter devicesListAdapter = new DevicesListAdapter(devices, context);
- // 設置設備列表容器項提供者
- devicesListContainer.setItemProvider(devicesListAdapter);
- // 設置設備列表項單擊事件
- devicesListContainer.setItemClickedListener((listContainer, component, position, id) -> {
- // 回調選擇的設備信息
- listener.callBack(devices.get(position));
- // 關閉對話框
- commonDialog.hide();
- });
- }
- dialogLayout.findComponentById(ResourceTable.Id_cancel).setClickedListener(component -> {
- // 關閉對話框
- commonDialog.hide();
- });
- }
- // 顯示對話框
- public void show() {
- commonDialog.show();
- }
- /**
- * 內部接口, 選擇設備后回調事件
- */
- public interface SelectResultListener {
- void callBack(DeviceInfo deviceInfo);
- }
- }
DevicesListAdapter設備列表適配器:
- public class DevicesListAdapter extends BaseItemProvider {
- // 開始下標從0開始
- private static final int SUBSTRING_START = 0;
- // 結束下標為4
- private static final int SUBSTRING_END = 4;
- // 設備信息列表
- private List<DeviceInfo> deviceInfoList;
- // 當前上下文
- private Context context;
- // 帶參構造方法
- public DevicesListAdapter(List<DeviceInfo> deviceInfoList, Context context) {
- this.deviceInfoList = deviceInfoList;
- this.context = context;
- }
- @Override
- public int getCount() {
- return deviceInfoList == null ? 0 : deviceInfoList.size();
- }
- @Override
- public Object getItem(int i) {
- return Optional.of(deviceInfoList.get(i));
- }
- @Override
- public long getItemId(int i) {
- return i;
- }
- @Override
- public Component getComponent(int i, Component component, ComponentContainer componentContainer) {
- // 定義設備視圖內部類
- ViewHolder viewHolder = null;
- // 定義組件
- Component mComponent = component;
- // 組件為空時
- if (mComponent == null) {
- // 查找設備列表項布局XML
- mComponent = LayoutScatter.getInstance(context).parse(ResourceTable.Layout_item_device_list, null, false);
- // 初始化設備視圖類
- viewHolder = new ViewHolder();
- // 判斷組件布局里是否包含設備名稱文本組件
- if (mComponent.findComponentById(ResourceTable.Id_device_name) instanceof Text) {
- // 獲取設備列表項布局XML的設備名稱文件組件,并賦值給內部類設備視圖設備名稱屬性緩存
- viewHolder.devicesName = (Text) mComponent.findComponentById(ResourceTable.Id_device_name);
- }
- // 判斷組件布局里是否包含設備Id文本組件
- if (mComponent.findComponentById(ResourceTable.Id_device_id) instanceof Text) {
- // 獲取設備列表項布局XML的設備Id文件組件,并賦值給內部類設備視圖設備Id屬性緩存
- viewHolder.devicesId = (Text) mComponent.findComponentById(ResourceTable.Id_device_id);
- }
- mComponent.setTag(viewHolder);
- } else {
- // 如果組件不為空, 并且標簽包含內部類設備視圖
- if (mComponent.getTag() instanceof ViewHolder) {
- // 從組件標簽獲取出設備視圖
- viewHolder = (ViewHolder) mComponent.getTag();
- }
- }
- // 設備視圖不為空時
- if (viewHolder != null) {
- // 設置設備名稱內容
- viewHolder.devicesName.setText(deviceInfoList.get(i).getDeviceName());
- String deviceId = deviceInfoList.get(i).getDeviceId();
- deviceId = deviceId.substring(SUBSTRING_START, SUBSTRING_END) + "******"
- + deviceId.substring(deviceId.length() - SUBSTRING_END);
- // 設置設備名稱Id
- viewHolder.devicesId.setText(deviceId);
- }
- return mComponent;
- }
- /**
- * 內部類, 設備視圖
- */
- private static class ViewHolder {
- // 設備名稱
- private Text devicesName;
- // 設備Id
- private Text devicesId;
- }
- }
MainAbilitySlice主界面功能講解 :
主界面主要功能就是用表格布局生成3x3, 4x4, 5x5, 6x6, 7x7, 8x8, 9x9 七個按鈕,點擊后跳轉游戲界面,初始化相應的數字按鈕,用到了Slice之間跳轉傳參數, 源碼都有詳細注釋,有興趣小伙伴可以到gitee查看源碼。
PlayAbilitySlice游戲界面功能講解:
游戲界面主要功能也是用表格布局生成相應主界面傳過來的參數按鈕,數字顯示順序隨機, 分布式協同拉起GameServiceAbility游戲服務,并且在點擊每個數字按鈕時,通過訂閱Event,把當前點到哪個數字,相關變量都接收到,然后更新相應的數據, 源碼都有詳細注釋,有興趣小伙伴可以到gitee查看源碼。
GameServiceAbility游戲服務講解:
游戲服務主要功能是如果請求是Ability的,接收到參數后,再流轉到其它界面傳參;如果是其它請求,接收到參數后,通過公共事件發布出去,讓訂閱了此事件的Ability更新數據,源碼都有詳細注釋,有興趣小伙伴可以到gitee查看源碼。
講解到此了,不要忘記了config.json文件的權限配置哦,在module下添加。
- "reqPermissions": [
- {
- "name": "ohos.permission.GET_DISTRIBUTED_DEVICE_INFO"
- },
- {
- "name": "ohos.permission.DISTRIBUTED_DATASYNC"
- },
- {
- "name": "ohos.permission.DISTRIBUTED_DEVICE_STATE_CHANGE"
- },
- {
- "name": "ohos.permission.READ_USER_STORAGE"
- },
- {
- "name": "ohos.permission.WRITE_USER_STORAGE"
- },
- {
- "name": "ohos.permission.GET_BUNDLE_INFO"
- }
- ]
同時,在游戲界面入口也是要提供動態授權:
- private static void grantPermission(Context context) {
- LogUtil.info(TAG, "grantPermission");
- if (context.verifySelfPermission(DISTRIBUTED_DATASYNC) != IBundleManager.PERMISSION_GRANTED) {
- if (context.canRequestPermission(DISTRIBUTED_DATASYNC)) {
- context.requestPermissionsFromUser(new String[] {DISTRIBUTED_DATASYNC}, PERMISSION_CODE);
- }
- }
- }
五, 總結
有興趣的小伙伴可以下載源碼查看, 項目代碼基本都有注釋了,游戲規則很簡單,就是在界面按順序點擊數字,時間越短,說明注意力越集中。 源碼同步到gitee碼云。