Layout_ability_main.xml布局:
background_button1.xml背景样式:
background_button2.xml背景样式:
background_button3.xml背景样式:
嗯,编写布局页面不难、稍微难点的是电视、车载设备、Pad、手机、手表五个端的屏幕适配。
界面编写完,发现各个端的屏幕高度还没有做适配,一开始认为Android与HarmonyOS用Java语言都可以编写,HarmonyOS也可以使用Android的相关框架,便想着如何在HarmonyOS上去使用Android的屏幕适配方案,在用了今日头条的屏幕适配方案开刀后,发现压根行不通,今日头条的屏幕适配方案用的单位是dp,这个单位在HarmonyOS上并没有,只有类似的vp,看来还是我太天真了。
Android屏幕单位有dp、in、mm、pt、px、sp,HarmonOS屏幕单位有fp、px、vp。
其中两者相同的单位是px,Android的dp与HarmonOS的vp都是为各自设备量身打造的单位,若想要搞一个两者都可以用的屏幕适配框架,也许,只能从px找突破口。今日头条的屏幕适配方案用的单位虽然是HarmonyOS所没有的dp,但其实它最终都是要拿dp来转换成px的喔~
继承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();
}
/**
* 各个按钮点击事件
* @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("")) {
// 如果没有输入公式
utils.toast(this, "还没输入公式呢");
return;
} else if (!utils.isNumEnd(formula)) {
// 如果公式的最后一位数非数字
utils.toast(this, "计算器表示没见过这样的数学公式,运算不出来");
return;
}
String[] split;
if (!utils.isContains(formula, ".")) {
// 计算整数
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 {
// 计算小数
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);
}
/**
* 根据不同设备调整高度
*/
private void highlyAdaptive() {
if (DeviceInfo.getDeviceType().equals("phone")) {
// 手机设备
ComponentContainer.LayoutConfig layoutConfig = new ComponentContainer.LayoutConfig();
layoutConfig.height = 1100;
content.setLayoutConfig(layoutConfig);
} else if (DeviceInfo.getDeviceType().equals("tablet")) {
// 平板设备
ComponentContainer.LayoutConfig layoutConfig = new ComponentContainer.LayoutConfig();
layoutConfig.height = 1200;
content.setLayoutConfig(layoutConfig);
} else if (DeviceInfo.getDeviceType().equals("tv")) {
// TV设备
ComponentContainer.LayoutConfig layoutConfig = new ComponentContainer.LayoutConfig();
layoutConfig.height = 160;
content.setLayoutConfig(layoutConfig);
} else if (DeviceInfo.getDeviceType().equals("wearable")) {
// 可穿戴设备
ComponentContainer.LayoutConfig layoutConfig = new ComponentContainer.LayoutConfig();
layoutConfig.height = 150;
content.setLayoutConfig(layoutConfig);
} else if (DeviceInfo.getDeviceType().equals("car")) {
// 车载设备
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时屏幕适配只能做到宽度适配或高度适配,没办法在一个xml界面同时适配宽度与高度,为此写了一个highlyAdaptive
方法处理xml没能完成的高度适配,方法通过DeviceInfo.getDeviceType()
来得到设备的类型,根据不同的设备去修改它的高度,也算是实现了高度适配。
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();
}
/**
* 判断最后一位是否数字
* @param content
*/
public boolean isNumber(String content){
char[] chars = content.substring(content.length() - 1, content.length()).toCharArray();
return Character.isDigit(chars[0]);
}
/**
* 判断是否是小数
*/
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;
}
/**
* 判断一位数是否是小数
*/
public boolean isDecimal(String str) {
if (isContains(str, "."))
return true;
else
return false;
}
/**
* 是否包含某一个运算符
*/
public boolean isContains(String value, String contain) {
if (value.indexOf(contain) == -1)
return false;
else
return true;
}
/**
* 最后一个值是数字就加符号,不是数字则替换它
* @param str 符号
*/
public String isNum(String content,String str) {
if (isNumEnd(content)) content += str;
else content = content.substring(0, content.length() - 1) + str;
return content;
}
/**
* 第一个值是0,输入整数则替换掉
*/
public String isZero(String content,String str) {
if (content.equals("0")) {
content = str;
} else {
content += str;
}
return content;
}
/**
* 得到第一个值是否是数字
*/
public boolean isNumStart(String str) {
if (str.startsWith("+") || str.startsWith("x") || str.startsWith("÷") || str.startsWith("%") || str.equals("")) {
return false;
}
return true;
}
/**
* 得到最后一个值是否是数字
*/
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;
}
}
Phone 设备实现效果
Pad 设备实现效果
TV 设备实现效果
Wearable 设备实现效果
目前所有设备中,Wearable是几个设备中最不好适配、最难适配的设备,但,想实现也并非不可能。
如果继续适配Wearable,目前能想到Wearable屏幕适配的方法有三种:
1、需要将背景换成一个圆,按钮都放进一个自动换行的组件。只是,这个想法不是很现实,Android的RecycleView
组件也只是一行固定多少个才会换行,HarmonyOS的ListContainer
组件能否实现效果还是个未知数。
2、使用他人开源的屏幕适配框架。不过,这个很遗憾,截止至发稿,还未能了解到有相关的适配框架。
3、另外写一个适配Wearable的布局。在onState
方法执行super.setUIContent
前更换专门为Wearable而写的xml,如:
@Override
public void onStart(Intent intent) {
super.onStart(intent);
// wearable设备换一个布局
if (DeviceInfo.getDeviceType().equals("wearable")){
super.setUIContent(Wearable布局);
}else{
super.setUIContent(ResourceTable.Layout_ability_main);
}
}
截止至发稿,Car还没有开放对应的机型,没能使用远程真机进行测试查看最终效果。这个效果图也只是点击Previewer进行查看的样式及效果。
1、点击Previewer查看xml,偶尔点击xml的一些样式并不会有响应,需要关闭Previewer并重新打开。
2、Previewer展示的样式不会显示ToastDialog等对话框、不会打印日志、不能点击Debug进行测试,还是使用真机测试真机测试香。
此次是我自HarmonyOS的DevEco Studio开发工具发布以来第一次开发的APP,身为一个Android开发工程师,做起HarmonyOS开发并不是很难,其中有很多东西都类似。DevEco Studio的远程真机测试与Previewer,效果杠杠的,要知道网上很多远程真机测试可都是收费制,且按使用时间收费,这一功能的出现可降低了不少开发费用。