布局的實(shí)現(xiàn)
Layout_ability_main.xml布局:
< ?xml version="1.0" encoding="utf-8"? >
< DirectionalLayout
xmlns:ohos="http://schemas.huawei.com/res/ohos"
ohos:height="match_parent"
ohos:width="match_parent"
ohos:alignment="center"
ohos:orientation="vertical" >
< TextField
ohos:id="$+id:content"
ohos:height="0vp"
ohos:weight="1"
ohos:width="match_parent"
ohos:enabled="false"
ohos:padding="20vp"
ohos:text="0"
ohos:text_alignment="center"
ohos:text_size="30"/ >
< ScrollView
ohos:height="0vp"
ohos:weight="1.8"
ohos:width="match_parent" >
< DirectionalLayout
ohos:height="match_content"
ohos:width="match_parent"
ohos:alignment="horizontal_center"
ohos:orientation="vertical" >
< DirectionalLayout
ohos:height="match_content"
ohos:width="match_parent"
ohos:orientation="horizontal" >
< /DirectionalLayout >
< DirectionalLayout
ohos:height="match_content"
ohos:width="match_parent"
ohos:orientation="horizontal" >
< /DirectionalLayout >
< DirectionalLayout
ohos:height="match_content"
ohos:width="match_parent"
ohos:orientation="horizontal" >
< /DirectionalLayout >
< DirectionalLayout
ohos:height="match_content"
ohos:width="match_parent"
ohos:orientation="horizontal" >
< /DirectionalLayout >
< DirectionalLayout
ohos:height="match_content"
ohos:width="match_parent"
ohos:orientation="horizontal" >
< /DirectionalLayout >
< /DirectionalLayout >
< /ScrollView >
< /DirectionalLayout >
< /DirectionalLayout >
< /DirectionalLayout >
background_button1.xml背景樣式:
< ?xml version="1.0" encoding="utf-8"? >
< shape
xmlns:ohos="http://schemas.huawei.com/res/ohos"
ohos:shape="rectangle" >
< solid ohos:color="#CCF1F1F1"/ >
< corners ohos:radius="20vp"/ >
< /shape >
< /shape >
< /shape >
background_button2.xml背景樣式:
< shape
xmlns:ohos="http://schemas.huawei.com/res/ohos"
ohos:shape="rectangle" >
< solid ohos:color="#FFE4F2FE"/ >
< corners ohos:radius="20vp"/ >
< /shape >
< /shape >
< /shape >
background_button3.xml背景樣式:
< ?xml version="1.0" encoding="utf-8"? >
< shape
xmlns:ohos="http://schemas.huawei.com/res/ohos"
ohos:shape="rectangle" >
< solid ohos:color="#FF007CFD"/ >
< corners ohos:radius="20vp"/ >
< /shape >
< /shape >
< /shape >
嗯,編寫布局頁(yè)面不難、稍微難點(diǎn)的是電視、車載設(shè)備、Pad、手機(jī)、手表五個(gè)端的屏幕適配。
界面編寫完,發(fā)現(xiàn)各個(gè)端的屏幕高度還沒(méi)有做適配,一開(kāi)始認(rèn)為Android與HarmonyOS用Java語(yǔ)言都可以編寫,HarmonyOS也可以使用Android的相關(guān)框架,便想著如何在HarmonyOS上去使用Android的屏幕適配方案,在用了今日頭條的屏幕適配方案開(kāi)刀后,發(fā)現(xiàn)壓根行不通,今日頭條的屏幕適配方案用的單位是dp,這個(gè)單位在HarmonyOS上并沒(méi)有,只有類似的vp,看來(lái)還是我太天真了。
Android屏幕單位有dp、in、mm、pt、px、sp,HarmonOS屏幕單位有fp、px、vp。
其中兩者相同的單位是px,Android的dp與HarmonOS的vp都是為各自設(shè)備量身打造的單位,若想要搞一個(gè)兩者都可以用的屏幕適配框架,也許,只能從px找突破口。今日頭條的屏幕適配方案用的單位雖然是HarmonyOS所沒(méi)有的dp,但其實(shí)它最終都是要拿dp來(lái)轉(zhuǎn)換成px的喔~
Java代碼邏輯
繼承AbilitySlice的MainAbilitySlice類:
public class MainAbilitySlice extends AbilitySlice implements Component.ClickedListener {
private Utils utils = Utils.getInstance();
private TextField content;
private String formula = "";
@Override
public void onStart(Intent intent) {
super.onStart(intent);
super.setUIContent(ResourceTable.Layout_ability_main);
initView();
highlyAdaptive();
}
/**
* 各個(gè)按鈕點(diǎn)擊事件
* @param component
*/
@Override
public void onClick(Component component) {
switch (component.getId()) {
case ResourceTable.Id_one:
if (utils.isNumStart(formula)) formula = utils.isZero(formula, "1");
else formula = "1";
break;
case ResourceTable.Id_two:
if (utils.isNumStart(formula)) formula = utils.isZero(formula, "2");
else formula = "2";
break;
case ResourceTable.Id_three:
if (utils.isNumStart(formula)) formula = utils.isZero(formula, "3");
else formula = "3";
break;
case ResourceTable.Id_four:
if (utils.isNumStart(formula)) formula = utils.isZero(formula, "4");
else formula = "4";
break;
case ResourceTable.Id_five:
if (utils.isNumStart(formula)) formula = utils.isZero(formula, "5");
else formula = "5";
break;
case ResourceTable.Id_six:
if (utils.isNumStart(formula)) formula = utils.isZero(formula, "6");
else formula = "6";
break;
case ResourceTable.Id_seven:
if (utils.isNumStart(formula)) formula = utils.isZero(formula, "7");
else formula = "7";
break;
case ResourceTable.Id_eight:
if (utils.isNumStart(formula)) formula = utils.isZero(formula, "8");
else formula = "8";
break;
case ResourceTable.Id_nine:
if (utils.isNumStart(formula)) formula = utils.isZero(formula, "9");
else formula = "9";
break;
case ResourceTable.Id_zero:
if (utils.isNumStart(formula)) formula = utils.isZero(formula, "0");
else formula = "0";
break;
case ResourceTable.Id_reset:
formula = "0";
break;
case ResourceTable.Id_except:
if (utils.isNumEnd(formula)) formula += "÷";
else if (!formula.substring(formula.length() - 1, formula.length()).equals("."))
formula = formula.substring(0, formula.length() - 1) + "÷";
break;
case ResourceTable.Id_ride:
formula = utils.isNum(formula, "x");
break;
case ResourceTable.Id_percentage:
formula = utils.isNum(formula, "%");
break;
case ResourceTable.Id_decimal_point:
if (utils.isNumEnd(formula) && !utils.isDecimals(formula)) formula += ".";
break;
case ResourceTable.Id_delete:
if (!formula.equals("") && !formula.equals("0")) {
formula = formula.substring(0, formula.length() - 1);
if (formula.equals("")) formula = "0";
}
break;
case ResourceTable.Id_reduce:
if (utils.isNumEnd(formula)) formula += "-";
else formula = formula.substring(0, formula.length() - 1) + "-";
break;
case ResourceTable.Id_add:
if (utils.isNumEnd(formula)) formula += "+";
else formula =
formula.substring(0, formula.length() - 1) + "+";
break;
case ResourceTable.Id_equal:
equal();
break;
default:
break;
}
if (component.getId() != ResourceTable.Id_equal) {
content.setText(formula);
}
}
private void equal() {
if (formula.equals("")) {
// 如果沒(méi)有輸入公式
utils.toast(this, "還沒(méi)輸入公式呢");
return;
} else if (!utils.isNumEnd(formula)) {
// 如果公式的最后一位數(shù)非數(shù)字
utils.toast(this, "計(jì)算器表示沒(méi)見(jiàn)過(guò)這樣的數(shù)學(xué)公式,運(yùn)算不出來(lái)");
return;
}
String[] split;
if (!utils.isContains(formula, ".")) {
// 計(jì)算整數(shù)
if (utils.isContains(formula, "-")) {
// 減法
split = formula.split("-");
if (split.length > 1)
result((Integer.parseInt(split[0]) + Integer.parseInt(split[1])) + "");
} else if (utils.isContains(formula, "+")) {
// 加法
split = formula.split("+");
if (split.length > 1)
result((Integer.parseInt(split[0]) + Integer.parseInt(split[1])) + "");
} else if (utils.isContains(formula, "x")) {
// 乘法
split = formula.split("x");
if (split.length > 1)
result((Integer.parseInt(split[0]) + Integer.parseInt(split[1])) + "");
} else if (utils.isContains(formula, "÷")) {
// 除法
split = formula.split("÷");
if (split.length > 1)
result((Integer.parseInt(split[0]) + Integer.parseInt(split[1])) + "");
} else if (utils.isContains(formula, "%")) {
// 取余
split = formula.split("%");
if (split.length > 1)
result((Integer.parseInt(split[0]) + Integer.parseInt(split[1])) + "");
}
} else {
// 計(jì)算小數(shù)
if (utils.isContains(formula, "-")) {
// 減法
split = formula.split("-");
if (split.length > 1)
result((Double.parseDouble(split[0]) - Double.parseDouble(split[1])) + "");
} else if (utils.isContains(formula, "+")) {
// 加法
split = formula.split("+");
if (split.length > 1)
result((Double.parseDouble(split[0]) - Double.parseDouble(split[1])) + "");
} else if (utils.isContains(formula, "x")) {
// 乘法
split = formula.split("x");
if (split.length > 1)
result((Double.parseDouble(split[0]) - Double.parseDouble(split[1])) + "");
} else if (utils.isContains(formula, "÷")) {
// 除法`
split = formula.split("÷");
if (split.length > 1)
result((Double.parseDouble(split[0]) - Double.parseDouble(split[1])) + "");
} else if (utils.isContains(formula, "%")) {
// 取余
split = formula.split("%");
if (split.length > 1)
result((Double.parseDouble(split[0]) - Double.parseDouble(split[1])) + "");
}
}
}
private void result(String value) {
formula = value;
content.setText(value);
}
/**
* 根據(jù)不同設(shè)備調(diào)整高度
*/
private void highlyAdaptive() {
if (DeviceInfo.getDeviceType().equals("phone")) {
// 手機(jī)設(shè)備
ComponentContainer.LayoutConfig layoutConfig = new ComponentContainer.LayoutConfig();
layoutConfig.height = 1100;
content.setLayoutConfig(layoutConfig);
} else if (DeviceInfo.getDeviceType().equals("tablet")) {
// 平板設(shè)備
ComponentContainer.LayoutConfig layoutConfig = new ComponentContainer.LayoutConfig();
layoutConfig.height = 1200;
content.setLayoutConfig(layoutConfig);
} else if (DeviceInfo.getDeviceType().equals("tv")) {
// TV設(shè)備
ComponentContainer.LayoutConfig layoutConfig = new ComponentContainer.LayoutConfig();
layoutConfig.height = 160;
content.setLayoutConfig(layoutConfig);
} else if (DeviceInfo.getDeviceType().equals("wearable")) {
// 可穿戴設(shè)備
ComponentContainer.LayoutConfig layoutConfig = new ComponentContainer.LayoutConfig();
layoutConfig.height = 150;
content.setLayoutConfig(layoutConfig);
} else if (DeviceInfo.getDeviceType().equals("car")) {
// 車載設(shè)備
ComponentContainer.LayoutConfig layoutConfig = new ComponentContainer.LayoutConfig();
layoutConfig.height = 500;
content.setLayoutConfig(layoutConfig);
}
}
/**
* 初始化xml布局控件
*/
private void initView() {
content = (TextField) findComponentById(ResourceTable.Id_content);
((Button) findComponentById(ResourceTable.Id_one)).setClickedListener(this);
((Button) findComponentById(ResourceTable.Id_two)).setClickedListener(this);
((Button) findComponentById(ResourceTable.Id_three)).setClickedListener(this);
((Button) findComponentById(ResourceTable.Id_four)).setClickedListener(this);
((Button) findComponentById(ResourceTable.Id_five)).setClickedListener(this);
((Button) findComponentById(ResourceTable.Id_six)).setClickedListener(this);
((Button) findComponentById(ResourceTable.Id_seven)).setClickedListener(this);
((Button) findComponentById(ResourceTable.Id_eight)).setClickedListener(this);
((Button) findComponentById(ResourceTable.Id_nine)).setClickedListener(this);
((Button) findComponentById(ResourceTable.Id_zero)).setClickedListener(this);
((Button) findComponentById(ResourceTable.Id_reset)).setClickedListener(this);
((Button) findComponentById(ResourceTable.Id_except)).setClickedListener(this);
((Button) findComponentById(ResourceTable.Id_ride)).setClickedListener(this);
((Button) findComponentById(ResourceTable.Id_delete)).setClickedListener(this);
((Button) findComponentById(ResourceTable.Id_reduce)).setClickedListener(this);
((Button) findComponentById(ResourceTable.Id_add)).setClickedListener(this);
((Button) findComponentById(ResourceTable.Id_equal)).setClickedListener(this);
((Button) findComponentById(ResourceTable.Id_decimal_point)).setClickedListener(this);
((Button) findComponentById(ResourceTable.Id_percentage)).setClickedListener(this);
}
}
}
}
由于在編寫xml UI時(shí)屏幕適配只能做到寬度適配或高度適配,沒(méi)辦法在一個(gè)xml界面同時(shí)適配寬度與高度,為此寫了一個(gè)highlyAdaptive
方法處理xml沒(méi)能完成的高度適配,方法通過(guò)DeviceInfo.getDeviceType()
來(lái)得到設(shè)備的類型,根據(jù)不同的設(shè)備去修改它的高度,也算是實(shí)現(xiàn)了高度適配。
Utils類:
public class Utils {
private static Utils utils = new Utils();
private static ToastDialog toastDialog;
private String[] symbol = new String[]{"+", "-", "x", "÷", "%"};
public static Utils getInstance() {
return utils;
}
public void toast(Context context, String text) {
if (toastDialog == null) {
toastDialog = new ToastDialog(context);
}
toastDialog.setAlignment(LayoutAlignment.CENTER);
toastDialog.setText(text);
toastDialog.show();
}
/**
* 判斷最后一位是否數(shù)字
* @param content
*/
public boolean isNumber(String content){
char[] chars = content.substring(content.length() - 1, content.length()).toCharArray();
return Character.isDigit(chars[0]);
}
/**
* 判斷是否是小數(shù)
*/
public boolean isDecimals(String str) {
if (isDecimal(str)) {
for (String s : symbol) {
if (isContains(str, s)) {
String[] split = str.split(s);
if (split != null){
if (!isDecimal(split[split.length - 1])) {
return false;
} else {
return true;
}
}
}
}
return true;
}
return false;
}
/**
* 判斷一位數(shù)是否是小數(shù)
*/
public boolean isDecimal(String str) {
if (isContains(str, "."))
return true;
else
return false;
}
/**
* 是否包含某一個(gè)運(yùn)算符
*/
public boolean isContains(String value, String contain) {
if (value.indexOf(contain) == -1)
return false;
else
return true;
}
/**
* 最后一個(gè)值是數(shù)字就加符號(hào),不是數(shù)字則替換它
* @param str 符號(hào)
*/
public String isNum(String content,String str) {
if (isNumEnd(content)) content += str;
else content = content.substring(0, content.length() - 1) + str;
return content;
}
/**
* 第一個(gè)值是0,輸入整數(shù)則替換掉
*/
public String isZero(String content,String str) {
if (content.equals("0")) {
content = str;
} else {
content += str;
}
return content;
}
/**
* 得到第一個(gè)值是否是數(shù)字
*/
public boolean isNumStart(String str) {
if (str.startsWith("+") || str.startsWith("x") || str.startsWith("÷") || str.startsWith("%") || str.equals("")) {
return false;
}
return true;
}
/**
* 得到最后一個(gè)值是否是數(shù)字
*/
public boolean isNumEnd(String str) {
char[] chars = str.substring(str.length() - 1, str.length()).toCharArray();
if (!Character.isDigit(chars[chars.length - 1])) {
return false;
}
return true;
}
}
}
}
GIF演示實(shí)現(xiàn)效果
- Phone 設(shè)備實(shí)現(xiàn)效果
- Pad 設(shè)備實(shí)現(xiàn)效果
- TV 設(shè)備實(shí)現(xiàn)效果
- Wearable 設(shè)備實(shí)現(xiàn)效果
目前所有設(shè)備中,Wearable是幾個(gè)設(shè)備中最不好適配、最難適配的設(shè)備,但,想實(shí)現(xiàn)也并非不可能。
如果繼續(xù)適配Wearable,目前能想到Wearable屏幕適配的方法有三種:
1、需要將背景換成一個(gè)圓,按鈕都放進(jìn)一個(gè)自動(dòng)換行的組件。只是,這個(gè)想法不是很現(xiàn)實(shí),Android的RecycleView
組件也只是一行固定多少個(gè)才會(huì)換行,HarmonyOS的ListContainer
組件能否實(shí)現(xiàn)效果還是個(gè)未知數(shù)。
2、使用他人開(kāi)源的屏幕適配框架。不過(guò),這個(gè)很遺憾,截止至發(fā)稿,還未能了解到有相關(guān)的適配框架。
3、另外寫一個(gè)適配Wearable的布局。在onState
方法執(zhí)行super.setUIContent
前更換專門為Wearable而寫的xml,如:
@Override
public void onStart(Intent intent) {
super.onStart(intent);
// wearable設(shè)備換一個(gè)布局
if (DeviceInfo.getDeviceType().equals("wearable")){
super.setUIContent(Wearable布局);
}else{
super.setUIContent(ResourceTable.Layout_ability_main);
}
}
}
}
Car 實(shí)現(xiàn)效果
截止至發(fā)稿,Car還沒(méi)有開(kāi)放對(duì)應(yīng)的機(jī)型,沒(méi)能使用遠(yuǎn)程真機(jī)進(jìn)行測(cè)試查看最終效果。這個(gè)效果圖也只是點(diǎn)擊Previewer進(jìn)行查看的樣式及效果。
Previewer注意事項(xiàng):
1、點(diǎn)擊Previewer查看xml,偶爾點(diǎn)擊xml的一些樣式并不會(huì)有響應(yīng),需要關(guān)閉Previewer并重新打開(kāi)。
2、Previewer展示的樣式不會(huì)顯示ToastDialog等對(duì)話框、不會(huì)打印日志、不能點(diǎn)擊Debug進(jìn)行測(cè)試,還是使用真機(jī)測(cè)試真機(jī)測(cè)試香。
此次是我自HarmonyOS的DevEco Studio開(kāi)發(fā)工具發(fā)布以來(lái)第一次開(kāi)發(fā)的APP,身為一個(gè)Android開(kāi)發(fā)工程師,做起HarmonyOS開(kāi)發(fā)并不是很難,其中有很多東西都類似。DevEco Studio的遠(yuǎn)程真機(jī)測(cè)試與Previewer,效果杠杠的,要知道網(wǎng)上很多遠(yuǎn)程真機(jī)測(cè)試可都是收費(fèi)制,且按使用時(shí)間收費(fèi),這一功能的出現(xiàn)可降低了不少開(kāi)發(fā)費(fèi)用。
審核編輯 黃宇
-
測(cè)試
+關(guān)注
關(guān)注
8文章
5269瀏覽量
126599 -
開(kāi)發(fā)
+關(guān)注
關(guān)注
0文章
370瀏覽量
40836 -
鴻蒙
+關(guān)注
關(guān)注
57文章
2339瀏覽量
42805 -
HarmonyOS
+關(guān)注
關(guān)注
79文章
1973瀏覽量
30143
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論