美文网首页
鸿蒙 简易计算器实现案例教程

鸿蒙 简易计算器实现案例教程

作者: xq9527 | 来源:发表于2021-08-22 12:39 被阅读0次

    前言:

    各位同学大家好,有段时间没有给大家更新文章了,具体多久我也不记得哈,今天是周日有时间就撸了一个鸿蒙的简易计算器的案例分享给大家 希望能帮助到各位同学学习和工作 那么废话不多说我们正式开始

    准备工作

    华为鸿蒙系统开发初体验 :[https://www.jianshu.com/p/f94c847c7fdc]

    效果图:

    image.png

    具体实现

    • 代码结构
    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(); 
    }
    
    • 主页面布局

    布局文件 ability_main.xml

    <?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>
    

    布局预览效果

    image.png
    我们从布局文件结构·可以看出来 多层线性布局嵌套来实现了整个 计算器键盘的布局 以及输入框和显示结果的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
     *
     * @since 2021年8月22日11:55:18
     *
     */
    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里面数据学运算的设计 有兴趣的同学可以自己看一下这里不展开讲
    到此简易计算器demo已经讲完了。

    最后总结

    这一章主要是讲解鸿蒙里面一些常用的案例 由于鸿蒙采用了java js作为基础语言来开发 非常成熟的语言那么我们在java以及js里面比较成熟的 数学运算设计可以直接借鉴的 我们配合上UI 布局就可以实现类似的简易计算器,有兴趣的可以私下多研究看看用js ui如何实现 这里就不展开讲了. 最后希望我的文章能帮助到各位解决问题 ,以后我还会贡献更多有用的代码分享给大家。各位同学如果觉得文章还不错 ,麻烦给关注和star,小弟在这里谢过啦

    项目地址

    码云 :https://gitee.com/qiuyu123/counter_demo

    相关文章

      网友评论

          本文标题:鸿蒙 简易计算器实现案例教程

          本文链接:https://www.haomeiwen.com/subject/ttbciltx.html