『牛角书』鸿蒙HarmonyOS简易计算器

wx639d8eb916f78
发布于 2022-12-17 18:01
浏览
1收藏

代码结构

『牛角书』鸿蒙HarmonyOS简易计算器-鸿蒙开发者社区
image.png

1 slice MainAbilitySlice:计算器交互界面,用于完成界面元素渲染、按钮事件绑定动作。
2 utils
MathUtil:用于计算表达式结果的工具类,封装有运算符权重获取、表达式计算等方法。
3 resources resources\base\layout:存放xml布局文件。resources\base\media:存放图片资源文件。resources\base\graphic:存放xml样式文件。
4 MainAbility:HAP的入口ability,由DevEco Studio自动生成。
5 MyApplication:表示HAP的类,由DevEco Studio自动生成。
6 config.json:应用的配置文件,由DevEco Studio自动生成。

应用初始化:

计算器应用使用的是自定义的按键,为避免用户在输入数学表达式时,系统自动弹出软键盘,本应用在初始化时,会在入口类MainAbility禁止应用弹出软键盘。
getWindow().setLayoutFlags(WindowManager.LayoutConfig.MARK_ALT_FOCUSABLE_IM,
WindowManager.LayoutConfig.MARK_ALT_FOCUSABLE_IM);
随后会在计算器交互界面类MainAbilitySlice中,完成界面渲染和按键点击事件绑定。
@Override
public void onStart(Intent intent) {
super.onStart(intent);
super.setUIContent(ResourceTable.Layout_ability_main);

initView(); 
initListener(); 

}

主页面布局

<?xml version=“1.0” encoding=“utf-8”?>
<DirectionalLayout
xmlns:ohos=“http://schemas.huawei.com/res/ohos
ohos:height=“match_parent”
ohos:orientation=“vertical”
ohos:alignment=“bottom”
ohos:background_element=“$graphic:background_ability_main”
ohos:width=“match_parent”>

<DirectionalLayout
    ohos:id="$+id:result_console"
    ohos:height="match_content"
    ohos:left_margin="15vp"
    ohos:right_margin="15vp"
    ohos:bottom_margin="50vp"
    ohos:width="match_parent">

    <TextField
        ohos:id="$+id:input_text"
        ohos:height="60vp"
        ohos:width="match_parent"
        ohos:text_size="48vp"
        ohos:bubble_height="0"
        ohos:multiple_lines="false"
        ohos:text_alignment="right"/>

    <Text
        ohos:id="$+id:pre_result"
        ohos:margin="3vp"
        ohos:height="42vp"
        ohos:width="match_parent"
        ohos:text_size="36vp"
        ohos:text_color="#555555"
        ohos:scrollable="true"
        ohos:text_alignment="right"/>

</DirectionalLayout>

<DirectionalLayout
    ohos:id="$+id:calculate_keyboard"
    ohos:height="match_content"
    ohos:padding="8vp"
    ohos:margin="8vp"
    ohos:background_element="$graphic:background_keyboard"
    ohos:width="match_parent">

    <DirectionalLayout
        ohos:height="match_content"
        ohos:width="match_parent"
        ohos:alignment="center"
        ohos:orientation="horizontal">

        <Text
            ohos:id="$+id:number_cancel"
            ohos:text_alignment="center"
            ohos:height="70vp"
            ohos:width="70vp"
            ohos:text_size="30vp"
            ohos:text_color="blue"
            ohos:background_element="$graphic:background_round"
            ohos:margin="8vp"
            ohos:text="C"/>

        <Text
            ohos:id="$+id:divide"
            ohos:text_alignment="center"
            ohos:height="70vp"
            ohos:width="70vp"
            ohos:text_size="35vp"
            ohos:text_color="blue"
            ohos:background_element="$graphic:background_round"
            ohos:margin="8vp"
            ohos:text="÷"/>

        <Text
            ohos:id="$+id:multiply"
            ohos:text_alignment="center"
            ohos:height="70vp"
            ohos:width="70vp"
            ohos:text_size="35vp"
            ohos:text_color="blue"
            ohos:background_element="$graphic:background_round"
            ohos:margin="8vp"
            ohos:text="×"/>

        <Image
            ohos:id="$+id:delete"
            ohos:text_alignment="center"
            ohos:height="70vp"
            ohos:width="70vp"
            ohos:text_size="28vp"
            ohos:background_element="$graphic:background_round"
            ohos:margin="8vp"
            ohos:image_src="$media:brief_del_bt"
            />
    </DirectionalLayout>

    <DirectionalLayout
        ohos:height="match_content"
        ohos:width="match_parent"
        ohos:alignment="center"
        ohos:orientation="horizontal">

        <Text
            ohos:id="$+id:seven"
            ohos:text_alignment="center"
            ohos:height="70vp"
            ohos:width="70vp"
            ohos:text_size="30vp"
            ohos:background_element="$graphic:background_round"
            ohos:margin="8vp"
            ohos:text="7"/>

        <Text
            ohos:id="$+id:eight"
            ohos:text_alignment="center"
            ohos:height="70vp"
            ohos:width="70vp"
            ohos:text_size="30vp"
            ohos:background_element="$graphic:background_round"
            ohos:margin="8vp"
            ohos:text="8"/>

        <Text
            ohos:id="$+id:nine"
            ohos:text_alignment="center"
            ohos:height="70vp"
            ohos:width="70vp"
            ohos:text_size="30vp"
            ohos:background_element="$graphic:background_round"
            ohos:margin="8vp"
            ohos:text="9"/>

        <Text
            ohos:id="$+id:minus"
            ohos:text_alignment="center"
            ohos:height="70vp"
            ohos:width="70vp"
            ohos:text_color="blue"
            ohos:text_size="30vp"
            ohos:background_element="$graphic:background_round"
            ohos:margin="8vp"
            ohos:text="-"/>
    </DirectionalLayout>

    <DirectionalLayout
        ohos:height="match_content"
        ohos:width="match_parent"
        ohos:alignment="center"
        ohos:orientation="horizontal">

        <Text
            ohos:id="$+id:four"
            ohos:text_alignment="center"
            ohos:height="70vp"
            ohos:width="70vp"
            ohos:text_size="30vp"
            ohos:background_element="$graphic:background_round"
            ohos:margin="8vp"
            ohos:text="4"/>

        <Text
            ohos:id="$+id:five"
            ohos:text_alignment="center"
            ohos:height="70vp"
            ohos:width="70vp"
            ohos:text_size="30vp"
            ohos:background_element="$graphic:background_round"
            ohos:margin="8vp"
            ohos:text="5"/>

        <Text
            ohos:id="$+id:six"
            ohos:text_alignment="center"
            ohos:height="70vp"
            ohos:width="70vp"
            ohos:text_size="30vp"
            ohos:background_element="$graphic:background_round"
            ohos:margin="8vp"
            ohos:text="6"/>

        <Text
            ohos:id="$+id:plus"
            ohos:text_alignment="center"
            ohos:height="70vp"
            ohos:width="70vp"
            ohos:text_color="blue"
            ohos:text_size="30vp"
            ohos:background_element="$graphic:background_round"
            ohos:margin="8vp"
            ohos:text="+"/>
    </DirectionalLayout>

    <DirectionalLayout
        ohos:height="match_content"
        ohos:width="match_parent"
        ohos:orientation="horizontal"
        ohos:alignment="center">
        <DirectionalLayout
            ohos:height="match_content"
            ohos:width="match_content">

            <DirectionalLayout
                ohos:height="match_content"
                ohos:width="match_parent"
                ohos:alignment="center"
                ohos:orientation="horizontal">

                <Text
                    ohos:id="$+id:one"
                    ohos:text_alignment="center"
                    ohos:height="70vp"
                    ohos:width="70vp"
                    ohos:text_size="30vp"
                    ohos:background_element="$graphic:background_round"
                    ohos:margin="8vp"
                    ohos:text="1"/>

                <Text
                    ohos:id="$+id:two"
                    ohos:text_alignment="center"
                    ohos:height="70vp"
                    ohos:width="70vp"
                    ohos:text_size="30vp"
                    ohos:background_element="$graphic:background_round"
                    ohos:margin="8vp"
                    ohos:text="2"/>

                <Text
                    ohos:id="$+id:three"
                    ohos:text_alignment="center"
                    ohos:height="70vp"
                    ohos:width="70vp"
                    ohos:text_size="30vp"
                    ohos:background_element="$graphic:background_round"
                    ohos:margin="8vp"
                    ohos:text="3"/>
            </DirectionalLayout>

            <DirectionalLayout
                ohos:height="match_content"
                ohos:width="match_parent"
                ohos:alignment="center"
                ohos:orientation="horizontal">

                <Text
                    ohos:id="$+id:divide_remainder"
                    ohos:text_alignment="center"
                    ohos:height="70vp"
                    ohos:width="70vp"
                    ohos:text_size="30vp"
                    ohos:background_element="$graphic:background_round"
                    ohos:margin="8vp"
                    ohos:text="%"/>

                <Text
                    ohos:id="$+id:zero"
                    ohos:text_alignment="center"
                    ohos:height="70vp"
                    ohos:width="70vp"
                    ohos:text_size="30vp"
                    ohos:background_element="$graphic:background_round"
                    ohos:margin="8vp"
                    ohos:text="0"/>

                <Text
                    ohos:id="$+id:radix_point"
                    ohos:text_alignment="center"
                    ohos:height="70vp"
                    ohos:width="70vp"
                    ohos:text_size="30vp"
                    ohos:background_element="$graphic:background_round"
                    ohos:margin="8vp"
                    ohos:text="."/>
            </DirectionalLayout>

        </DirectionalLayout>

        <Text
            ohos:id="$+id:equal_sign"
            ohos:text_alignment="center"
            ohos:height="150vp"
            ohos:width="70vp"
            ohos:text_size="35vp"
            ohos:text_color="#FFFFFF"
            ohos:background_element="$graphic:background_denghao"
            ohos:margin="8vp"
            ohos:text="="/>
    </DirectionalLayout>

</DirectionalLayout>

</DirectionalLayout>
我们从布局文件结构·可以看出来 多层线性布局嵌套来实现了整个 计算器键盘的布局 以及输入框和显示结果的text
逻辑实现
package com.example.counter_demo.slice;
import com.example.counter_demo.utils.MathUtil;
import com.example.counter_demo.ResourceTable;
import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.content.Intent;
import ohos.agp.components.Component;
import ohos.agp.components.Text;
import ohos.agp.components.TextField;
import java.util.EmptyStackException;
import java.util.regex.Pattern;

/**

  • MainAbilitySlice

/
public class MainAbilitySlice extends AbilitySlice {
/
*
* number component
*/
private static int[] numberComponentIds = {ResourceTable.Id_seven, ResourceTable.Id_eight, ResourceTable.Id_nine,
ResourceTable.Id_four, ResourceTable.Id_five, ResourceTable.Id_six, ResourceTable.Id_one,
ResourceTable.Id_two, ResourceTable.Id_three, ResourceTable.Id_zero, ResourceTable.Id_radix_point
};

/**
 * operator component
 */
private static int[] operatorComponentIds = {ResourceTable.Id_divide, ResourceTable.Id_multiply,
        ResourceTable.Id_minus, ResourceTable.Id_divide_remainder, ResourceTable.Id_plus
};

private TextField inputText;
private Text preResult;

@Override
public void onStart(Intent intent) {
    super.onStart(intent);
    super.setUIContent(ResourceTable.Layout_ability_main);
    initView();
    initListener();
}

private void initView() {
    if (findComponentById(ResourceTable.Id_input_text) instanceof TextField) {
        inputText = (TextField) findComponentById(ResourceTable.Id_input_text);
        inputText.requestFocus();
    }
    if (findComponentById(ResourceTable.Id_pre_result) instanceof Text) {
        preResult = (Text) findComponentById(ResourceTable.Id_pre_result);
    }
}

private void initListener() {
    findComponentById(ResourceTable.Id_number_cancel).setClickedListener(new Component.ClickedListener() {
        @Override
        public void onClick(Component component) {
            preResult.setText("");
            inputText.setText("");
        }
    });

    findComponentById(ResourceTable.Id_delete).setClickedListener(new Component.ClickedListener() {
        @Override
        public void onClick(Component component) {
            if (inputText.getText().isEmpty()) {
                return;
            }
            inputText.setText(inputText.getText().substring(0, inputText.getText().length() - 1));
        }
    });

    findComponentById(ResourceTable.Id_equal_sign).setClickedListener(new Component.ClickedListener() {
        @Override
        public void onClick(Component component) {
            calculateResult(inputText.getText(), false);
        }
    });

    for (int componentId : numberComponentIds) {
        findComponentById(componentId).setClickedListener(new Component.ClickedListener() {
            @Override
            public void onClick(Component component) {
                bindNumberClickListener(componentId);
            }
        });
    }

    for (int componentId : operatorComponentIds) {
        findComponentById(componentId).setClickedListener(new Component.ClickedListener() {
            @Override
            public void onClick(Component component) {
                bindOperatorClickListener(componentId);
            }
        });
    }
}

private void bindNumberClickListener(int componentId) {
    String oldValue = inputText.getText();
    String inputValue = "";
    if (findComponentById(componentId) instanceof Text) {
        Text text = (Text) findComponentById(componentId);
        inputValue = text.getText();
    }

    if (oldValue.isEmpty() && ".".equals(inputValue)) {
        return;
    }
    if ("0".equals(oldValue) && !".".equals(inputValue)) {
        inputText.setText(inputValue);
    } else {
        inputText.append(inputValue);
    }
    calculateResult(inputText.getText(), true);
}

private void bindOperatorClickListener(int componentId) {
    String oldValue = inputText.getText();
    String inputValue = "";
    if (findComponentById(componentId) instanceof Text) {
        Text text = (Text) findComponentById(componentId);
        inputValue = text.getText();
    }
    if (oldValue.isEmpty()) {
        inputText.setText(inputValue);
    } else if (MathUtil.isOperator(oldValue.substring(oldValue.length() - 1))
            && MathUtil.isOperator(inputValue)) {
        String newValue = oldValue.substring(0, oldValue.length() - 1) + inputValue;
        inputText.setText(newValue);
    } else {
        inputText.append(inputValue);
    }
    calculateResult(inputText.getText(), true);
}

private void calculateResult(String exp, Boolean isAutoCalculate) {
    if (exp.isEmpty()) {
        return;
    }
    // 只有数字 不计算
    String pattern = "(\\d*\\.?\\d*)|(0\\.\\d*[1-9])";
    boolean isMatch = Pattern.matches(pattern, exp);
    if (isMatch) {
        return;
    }
    // 末位是运算符 不计算
    if (MathUtil.isOperator(exp.substring(exp.length() - 1))) {
        return;
    }

    String resultValue;
    try {
        resultValue = MathUtil.getResultString(exp);
    } catch (NumberFormatException | ArithmeticException | EmptyStackException e) {
        preResult.setText("错误");
        return;
    }

    if (isAutoCalculate) {
        preResult.setText(resultValue);
        return;
    }
    preResult.setText("");
    inputText.setText(resultValue);
}

@Override
public void onActive() {
    super.onActive();
}

@Override
public void onForeground(Intent intent) {
    super.onForeground(intent);
}

}
我们定义了2个数组来统一初始化我们的运算键位控件和我们数字控件 并且添加点击事件,然后配合我们MathUtil 工具类来处理结果

cancel 清零复位键处理

findComponentById(ResourceTable.Id_number_cancel).setClickedListener(new Component.ClickedListener() {
@Override
public void onClick(Component component) {
preResult.setText(“”);
inputText.setText(“”);
}
});
当用户点击到cancel键的时候我们将输入框的内容和显示文本上的内容全部都set成空字符串

delete删除键处理

findComponentById(ResourceTable.Id_delete).setClickedListener(new Component.ClickedListener() {
        @Override
        public void onClick(Component component) {
            if (inputText.getText().isEmpty()) {
                return;
            }
            inputText.setText(inputText.getText().substring(0, inputText.getText().length() - 1));
        }
    });

当用户点击到delete我们采用的 末尾删除 这个时候我们只需要通过字符串截取即可实现

具体 MathUtil 工具类实现

package com.example.counter_demo.utils;
import java.math.BigDecimal;
import java.util.Stack;
/**

  • MathUtil

  • @since 2021年8月22日11:55:02

  • 类说明:数学运算工具类
    */
    public class MathUtil {
    // 数字栈中分隔数字时使用
    private static String separateStr = “@”;

    // 保留小数位数
    private static final int DECIMAL_DIGIT = 6;

    // 不包含指定字符
    private static final int NOT_CONTAIN = -1;

    // 高优先级
    private static final int HIGH_PRIORITY = 2;

    // 低优先级
    private static final int LOW_PRIORITY = 1;

    private MathUtil() {
    }

    public static String getResultString(String exp) {
    Stack<String> numStack = new Stack<>();
    numStack.push(separateStr); // 数字用@分开,数字栈中@号 为了便于区分小数
    Stack<String> oprStack = new Stack<>();

     String[] strings = exp.split("");
     for (String singleStr : strings) {
         if (isOperator(singleStr)) {
             spiltExp(numStack, oprStack, singleStr);
         } else {
             numStack.push(singleStr);
         }
     }
     while (!oprStack.isEmpty()) {
         combineString(numStack);
         compute(numStack, oprStack);
     }
     numStack.pop();
     String resultValue = numStack.peek();
     return resultValue;
    

    }

    private static void spiltExp(Stack<String> numStack, Stack<String> oprStack, String singleStr) {
    // 运算符间的字符拼接成一个数字
    combineString(numStack);
    if (!oprStack.isEmpty()) {
    // 先处理优先级高的运算符
    while (!oprStack.isEmpty() && priority(singleStr) <= priority(oprStack.peek())) {
    combineString(numStack);
    compute(numStack, oprStack);
    }
    }
    oprStack.push(singleStr);
    }

    private static void compute(Stack<String> numStack, Stack<String> oprStack) {
    BigDecimal result = null;

     numStack.pop();
     BigDecimal rightNumber = new BigDecimal(numStack.pop());
    
     numStack.pop();
     BigDecimal leftNumber = new BigDecimal(numStack.pop());
    
     String operator = oprStack.pop();
     switch (operator) {
         case "-":
             result = leftNumber.subtract(rightNumber);
             break;
         case "+":
             result = leftNumber.add(rightNumber);
             break;
         case "%":
             result = leftNumber.divideAndRemainder(rightNumber)[1];
             break;
         case "×":
             result = leftNumber.multiply(rightNumber);
             break;
         case "÷":
             result = leftNumber.divide(rightNumber, DECIMAL_DIGIT, BigDecimal.ROUND_HALF_UP);
             break;
         default:
             break;
     }
     numStack.push(result.stripTrailingZeros().toPlainString());
     numStack.push(separateStr);
    

    }

    private static void combineString(Stack<String> stack) {
    if (separateStr.equals(stack.peek())) {
    return;
    }
    StringBuilder numberBuilder = new StringBuilder();
    while (true) {
    String string = stack.peek();
    if (separateStr.equals(string)) {
    break;
    }
    numberBuilder.insert(0, string);
    stack.pop();
    }
    stack.push(numberBuilder.toString());
    stack.push(separateStr);
    numberBuilder.delete(0, numberBuilder.length());
    }

    /**

    • Determines whether a string is an operator.
    • @param singleStr Character string to be judged
    • @return Judgment Result
      */
      public static boolean isOperator(String singleStr) {
      String operators = “-+×÷%”;
      if (operators.indexOf(singleStr) > NOT_CONTAIN) {
      return true;
      }
      return false;
      }

    private static int priority(String str) {
    String highOperator = “×÷%”;
    if (highOperator.indexOf(str) > NOT_CONTAIN) {
    return HIGH_PRIORITY;
    }
    return LOW_PRIORITY;
    }
    }

总结

由于鸿蒙采用了java js作为基础语言来开发 非常成熟的语言那么我们在java以及js里面比较成熟的 数学运算设计可以直接借鉴的 配合上UI 布局就可以实现类似的简易计算器

标签
收藏 1
回复
举报
回复
    相关推荐