一、前言
1024 一起專注游戲是在屏幕上畫上 N x N 個(gè)方格(如 4x4 共 16 個(gè)),格子內(nèi)任意填寫上從1開始順序生成的數(shù)字(如 1 ~ 16 共 16 個(gè)數(shù)字)。游戲時(shí),要求玩家用手指按從小到大(如 1 ~ 16 )的順序依次指出其位置,按完所有數(shù)字后,顯示所用的時(shí)間(秒)。所用時(shí)間越短,注意力水平越高。能夠培養(yǎng)注意力集中、分配、控制能力;拓展視幅;加快視頻;提高視覺的穩(wěn)定性、辨別人、定向搜索能力。此游戲?yàn)樽詈唵?,最有效也是最科學(xué)的注意力訓(xùn)練方法。尋找目標(biāo)數(shù)字時(shí),注意力是需要極度集中的,把這短暫的高強(qiáng)度的集中精力過程反復(fù)練習(xí),大腦的集中注意力功能就會不斷的加固,提高。注意水平越來越高。
同時(shí),1024 一起專注游戲使用了鴻蒙分布式協(xié)同技術(shù),在訓(xùn)練小孩子專注力和耐力時(shí),大人也可以一起陪伴訓(xùn)練,只要兩臺鴻蒙系統(tǒng)手機(jī)或一臺手機(jī)一臺平板,大人,小孩就可以同時(shí)一起在玩一個(gè)游戲,比如大人在其中一臺手機(jī)上按了一部份小數(shù)字,然后點(diǎn)擊分布式協(xié)同圖標(biāo),拉起另一臺手機(jī)的 1024 一起專注游戲,小孩可以接著按大人沒有按完的數(shù)字,最終顯示出所用的時(shí)間。
二、 實(shí)現(xiàn)效果
- 開發(fā)工具環(huán)境下視頻:
https://www.bilibili.com/video/BV1B34y1m7M5?spm_id_from=333.999.0.0
- 手機(jī) + 手機(jī)環(huán)境下視頻:
https://www.bilibili.com/video/BV1kh411b7QM?spm_id_from=333.999.0.0
- 手機(jī) + 平板環(huán)境下視頻:
https://www.bilibili.com/video/BV1ov411M7sq?spm_id_from=333.999.0.0
三、創(chuàng)建工程
在這當(dāng)作你已經(jīng)安裝好最新版本 DevEco-Studio 開發(fā)工具, 點(diǎn)擊 File
-> New
-> New Project…
彈出 Create HarmonyOS Project
窗口, 這里我選擇空白 Java 模板創(chuàng)建, 上一個(gè)視頻播放實(shí)例是用 JS 寫的界面,這個(gè)游戲界面就用 Java 來寫,還是 JS 寫界面快,調(diào)試也快些。
四、主界面開發(fā)
在展示源代碼之前,先介紹一下使用到了 JAVA 哪些組件: DirectionalLayout, TableLayout, DependentLayout, Button, Image, Text, ListContainer, CommonDialog,通過查看 Java UI 參考文檔,就可以做出你喜歡的應(yīng)用了。
先介紹公共類 Java 代碼,有了這些公共類,以后做類似功能的應(yīng)用,可以直接復(fù)制公共類文件可以使用:
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設(shè)備選擇對話框:
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) {
// 創(chuàng)建一個(gè)公共對話框
commonDialog = new CommonDialog(context);
// 設(shè)置對齊方式居中
commonDialog.setAlignment(LayoutAlignment.CENTER);
// 設(shè)置對話框尺寸
commonDialog.setSize(DIALOG_WIDTH, DIALOG_HEIGHT);
// 設(shè)置對話框自動關(guān)閉
commonDialog.setAutoClosable(true);
// 加載XML布局文件
Component dialogLayout =
LayoutScatter.getInstance(context).parse(ResourceTable.Layout_dialog_select_device, null, false);
// 設(shè)置對話框內(nèi)容
commonDialog.setContentCustomComponent(dialogLayout);
// 查找到列表容器
if (dialogLayout.findComponentById(ResourceTable.Id_list_devices) instanceof ListContainer) {
// 獲取列表容器對象
ListContainer devicesListContainer =
(ListContainer) dialogLayout.findComponentById(ResourceTable.Id_list_devices);
// 設(shè)備列表適配器
DevicesListAdapter devicesListAdapter = new DevicesListAdapter(devices, context);
// 設(shè)置設(shè)備列表容器項(xiàng)提供者
devicesListContainer.setItemProvider(devicesListAdapter);
// 設(shè)置設(shè)備列表項(xiàng)單擊事件
devicesListContainer.setItemClickedListener((listContainer, component, position, id) -> {
// 回調(diào)選擇的設(shè)備信息
listener.callBack(devices.get(position));
// 關(guān)閉對話框
commonDialog.hide();
});
}
dialogLayout.findComponentById(ResourceTable.Id_cancel).setClickedListener(component -> {
// 關(guān)閉對話框
commonDialog.hide();
});
}
// 顯示對話框
public void show() {
commonDialog.show();
}
/**
* 內(nèi)部接口, 選擇設(shè)備后回調(diào)事件
*/
public interface SelectResultListener {
void callBack(DeviceInfo deviceInfo);
}
}
DevicesListAdapter設(shè)備列表適配器:
public class DevicesListAdapter extends BaseItemProvider {
// 開始下標(biāo)從0開始
private static final int SUBSTRING_START = 0;
// 結(jié)束下標(biāo)為4
private static final int SUBSTRING_END = 4;
// 設(shè)備信息列表
private List<DeviceInfo> deviceInfoList;
// 當(dāng)前上下文
private Context context;
// 帶參構(gòu)造方法
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) {
// 定義設(shè)備視圖內(nèi)部類
ViewHolder viewHolder = null;
// 定義組件
Component mComponent = component;
// 組件為空時(shí)
if (mComponent == null) {
// 查找設(shè)備列表項(xiàng)布局XML
mComponent = LayoutScatter.getInstance(context).parse(ResourceTable.Layout_item_device_list, null, false);
// 初始化設(shè)備視圖類
viewHolder = new ViewHolder();
// 判斷組件布局里是否包含設(shè)備名稱文本組件
if (mComponent.findComponentById(ResourceTable.Id_device_name) instanceof Text) {
// 獲取設(shè)備列表項(xiàng)布局XML的設(shè)備名稱文件組件,并賦值給內(nèi)部類設(shè)備視圖設(shè)備名稱屬性緩存
viewHolder.devicesName = (Text) mComponent.findComponentById(ResourceTable.Id_device_name);
}
// 判斷組件布局里是否包含設(shè)備Id文本組件
if (mComponent.findComponentById(ResourceTable.Id_device_id) instanceof Text) {
// 獲取設(shè)備列表項(xiàng)布局XML的設(shè)備Id文件組件,并賦值給內(nèi)部類設(shè)備視圖設(shè)備Id屬性緩存
viewHolder.devicesId = (Text) mComponent.findComponentById(ResourceTable.Id_device_id);
}
mComponent.setTag(viewHolder);
} else {
// 如果組件不為空, 并且標(biāo)簽包含內(nèi)部類設(shè)備視圖
if (mComponent.getTag() instanceof ViewHolder) {
// 從組件標(biāo)簽獲取出設(shè)備視圖
viewHolder = (ViewHolder) mComponent.getTag();
}
}
// 設(shè)備視圖不為空時(shí)
if (viewHolder != null) {
// 設(shè)置設(shè)備名稱內(nèi)容
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);
// 設(shè)置設(shè)備名稱Id
viewHolder.devicesId.setText(deviceId);
}
return mComponent;
}
/**
* 內(nèi)部類, 設(shè)備視圖
*/
private static class ViewHolder {
// 設(shè)備名稱
private Text devicesName;
// 設(shè)備Id
private Text devicesId;
}
}
MainAbilitySlice主界面功能講解 :
主界面主要功能就是用表格布局生成 3x3, 4x4, 5x5, 6x6, 7x7, 8x8, 9x9 七個(gè)按鈕,點(diǎn)擊后跳轉(zhuǎn)游戲界面,初始化相應(yīng)的數(shù)字按鈕,用到了 Slice 之間跳轉(zhuǎn)傳參數(shù), 源碼都有詳細(xì)注釋,有興趣小伙伴可以到 gitee 查看源碼。
PlayAbilitySlice游戲界面功能講解:
游戲界面主要功能也是用表格布局生成相應(yīng)主界面?zhèn)鬟^來的參數(shù)按鈕,數(shù)字顯示順序隨機(jī), 分布式協(xié)同拉起 GameServiceAbility 游戲服務(wù),并且在點(diǎn)擊每個(gè)數(shù)字按鈕時(shí),通過訂閱 Event,把當(dāng)前點(diǎn)到哪個(gè)數(shù)字,相關(guān)變量都接收到,然后更新相應(yīng)的數(shù)據(jù), 源碼都有詳細(xì)注釋,有興趣小伙伴可以到 gitee 查看源碼。
GameServiceAbility游戲服務(wù)講解:
游戲服務(wù)主要功能是如果請求是 Ability 的,接收到參數(shù)后,再流轉(zhuǎn)到其它界面?zhèn)鲄?;如果是其它請求,接收到參?shù)后,通過公共事件發(fā)布出去,讓訂閱了此事件的 Ability 更新數(shù)據(jù),源碼都有詳細(xì)注釋,有興趣小伙伴可以到 gitee 查看源碼。
講解到此了,不要忘記了 config.json
文件的權(quán)限配置哦,在 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"
}
]
同時(shí),在游戲界面入口也是要提供動態(tài)授權(quán):
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);
}
}
}
五、總結(jié)
有興趣的小伙伴可以下載源碼查看, 項(xiàng)目代碼基本都有注釋了,游戲規(guī)則很簡單,就是在界面按順序點(diǎn)擊數(shù)字,時(shí)間越短,說明注意力越集中。 源碼同步到 gitee 碼云。
源碼在這:
https://gitee.com/army16/qin-hong-jun-board