当前位置: 首页 > news >正文

制作一个简易的计算器app

github项目地址:https://github.com/13008451162/AndroidMoblieCalculator

1、Ui开发

笔者的Ui制作的制作的比较麻烦仅供参考,在这里使用了多个LinearLayout对屏幕进行了划分。不建议大家这样做最好使用GridLayout会更加快捷简单

笔者大致划分是这样的:

image-20230723165001763

使用了四个大框,在第四个大框里面有多个小框

image-20230723165045992
最终界面如下:
在这里插入图片描述

然后在每一个小框写计算机的按键。

代码如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"tools:context=".MainActivity"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"><ImageViewandroid:id="@+id/TwoArrows_Shrink"android:layout_width="30dp"android:layout_height="30dp"android:layout_marginTop="15dp"android:layout_marginLeft="10dp"android:src="@drawable/two_arrows_shrink"/><ImageViewandroid:id="@+id/FourSquares"android:layout_width="30dp"android:layout_height="30dp"android:layout_marginLeft="220dp"android:layout_marginTop="15dp"android:src="@drawable/four_squares"/><ImageViewandroid:id="@+id/ThreePoints"android:layout_width="30dp"android:layout_height="30dp"android:layout_marginLeft="15dp"android:clickable="true"android:layout_marginTop="15dp"android:src="@drawable/three_points"/></LinearLayout><EditTextandroid:id="@+id/Scanf_EditText"android:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="3"android:background="@null"android:gravity="bottom|right"android:text=""android:lines="1"android:inputType="textVisiblePassword"android:textSize="40dp"android:paddingRight="16dp"android:textColor="@android:color/black"android:textStyle="bold" /><TextViewandroid:id="@+id/Printf_text"android:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="1"android:gravity="center|right"android:text=""android:textSize="25dp"android:layout_margin="8dp"android:paddingRight="13dp"android:textColor="@android:color/black"android:textStyle="bold" /><LinearLayoutandroid:id="@+id/keyboard"android:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="5"android:orientation="vertical"android:layout_margin="10dp"><!--  C % <- /      --><LinearLayoutandroid:id="@+id/One"android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"><!--            C--><FrameLayoutandroid:id="@+id/leftCFrmae"android:layout_width="wrap_content"android:layout_height="wrap_content"><ImageButtonandroid:id="@+id/leftC"android:layout_width="wrap_content"android:layout_height="wrap_content"android:background="@drawable/button_grey_round"android:layout_marginStart="20dp"/><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="("android:textSize="25sp"android:textColor="@android:color/black"android:layout_gravity="center"android:layout_marginLeft="10dp"android:padding="8dp"android:shadowColor="#80000000"android:shadowDx="2"android:shadowDy="2"android:shadowRadius="4"/></FrameLayout><FrameLayoutandroid:id="@+id/RightCFrame"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginStart="20dp"><ImageButtonandroid:id="@+id/RightC"android:layout_width="wrap_content"android:layout_height="wrap_content"android:background="@drawable/button_grey_round"/><TextViewandroid:layout_width="19dp"android:layout_height="wrap_content"android:layout_gravity="center"android:paddingLeft="10dp"android:shadowColor="#80000000"android:shadowDx="2"android:shadowDy="2"android:shadowRadius="4"android:text=")"android:textColor="@android:color/black"android:textSize="25sp" /></FrameLayout><FrameLayoutandroid:id="@+id/DeleteOneFrame"android:layout_width="wrap_content"android:layout_height="wrap_content"><ImageButtonandroid:id="@+id/DeleteOne"android:layout_width="wrap_content"android:layout_height="wrap_content"android:background="@drawable/button_grey_round"android:layout_marginStart="20dp"/><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:layout_marginLeft="11dp"android:padding="8dp"android:text="DEL"android:textColor="@android:color/black"android:textSize="25sp"android:shadowColor="#80000000"android:shadowDx="2"android:shadowDy="2"android:shadowRadius="4"/></FrameLayout><FrameLayoutandroid:id="@+id/DivisionFrame"android:layout_width="wrap_content"android:layout_height="wrap_content"><ImageButtonandroid:id="@+id/Division"android:layout_width="wrap_content"android:layout_height="wrap_content"android:background="@drawable/button_grey_round"android:layout_marginStart="20dp"/><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:layout_marginLeft="11dp"android:padding="8dp"android:text=""android:textColor="@android:color/black"android:textSize="25sp"android:shadowColor="#80000000"android:shadowDx="2"android:shadowDy="2"android:shadowRadius="4"/></FrameLayout></LinearLayout><!-- 7 8 9 x       --><LinearLayoutandroid:id="@+id/Two"android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"android:paddingTop="15dp"><FrameLayoutandroid:id="@+id/SevenNumFrame"android:layout_width="wrap_content"android:layout_height="wrap_content"><ImageButtonandroid:id="@+id/SevenNum"android:layout_width="wrap_content"android:layout_height="wrap_content"android:background="@drawable/button_white_round"android:layout_marginStart="20dp"/><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:layout_marginLeft="11dp"android:padding="8dp"android:text="7"android:textColor="@android:color/black"android:textSize="28dp"android:shadowColor="#80000000"android:shadowDx="2"android:shadowDy="2"android:shadowRadius="4" /></FrameLayout><FrameLayoutandroid:id="@+id/EightNumFrame"android:layout_width="wrap_content"android:layout_height="wrap_content"><ImageButtonandroid:id="@+id/EightNum"android:layout_width="wrap_content"android:layout_height="wrap_content"android:background="@drawable/button_white_round"android:layout_marginStart="20dp"/><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:layout_marginLeft="11dp"android:padding="8dp"android:text="8"android:textColor="@android:color/black"android:textSize="28dp"android:shadowColor="#80000000"android:shadowDx="2"android:shadowDy="2"android:shadowRadius="4" /></FrameLayout><FrameLayoutandroid:id="@+id/NineNumFrame"android:layout_width="wrap_content"android:layout_height="wrap_content"><ImageButtonandroid:id="@+id/NineNum"android:layout_width="wrap_content"android:layout_height="wrap_content"android:background="@drawable/button_white_round"android:layout_marginStart="20dp"/><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:layout_marginLeft="11dp"android:padding="8dp"android:text="9"android:textColor="@android:color/black"android:textSize="28dp"android:shadowColor="#80000000"android:shadowDx="2"android:shadowDy="2"android:shadowRadius="4" /></FrameLayout><FrameLayoutandroid:id="@+id/MultiplyFrame"android:layout_width="wrap_content"android:layout_height="wrap_content"><ImageViewandroid:id="@+id/Multiply"android:layout_width="wrap_content"android:layout_height="wrap_content"android:background="@drawable/button_grey_round"android:layout_marginStart="20dp"android:scaleType="centerInside"/><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:layout_marginLeft="11dp"android:padding="8dp"android:text="✖️"android:textColor="@android:color/black"android:textSize="28dp"android:shadowColor="#80000000"android:shadowDx="2"android:shadowDy="2"android:shadowRadius="4" /></FrameLayout></LinearLayout><!-- 4 5 6 -      --><LinearLayoutandroid:id="@+id/Three"android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"android:paddingTop="15dp"><FrameLayoutandroid:id="@+id/FourNumFrame"android:layout_width="wrap_content"android:layout_height="wrap_content"><ImageButtonandroid:id="@+id/FourNum"android:layout_width="wrap_content"android:layout_height="wrap_content"android:background="@drawable/button_white_round"android:layout_marginStart="20dp"/><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:layout_marginLeft="11dp"android:padding="8dp"android:text="4"android:textColor="@android:color/black"android:textSize="28dp"android:shadowColor="#80000000"android:shadowDx="2"android:shadowDy="2"android:shadowRadius="4" /></FrameLayout><FrameLayoutandroid:id="@+id/FiveNumFrame"android:layout_width="wrap_content"android:layout_height="wrap_content"><ImageButtonandroid:id="@+id/FiveNum"android:layout_width="wrap_content"android:layout_height="wrap_content"android:background="@drawable/button_white_round"android:layout_marginStart="20dp"/><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:layout_marginLeft="11dp"android:padding="8dp"android:text="5"android:textColor="@android:color/black"android:textSize="28dp"android:shadowColor="#80000000"android:shadowDx="2"android:shadowDy="2"android:shadowRadius="4" /></FrameLayout><FrameLayoutandroid:id="@+id/SixNumFrame"android:layout_width="wrap_content"android:layout_height="wrap_content"><ImageButtonandroid:id="@+id/SixNum"android:layout_width="wrap_content"android:layout_height="wrap_content"android:background="@drawable/button_white_round"android:layout_marginStart="20dp"/><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:layout_marginLeft="11dp"android:padding="8dp"android:text="6"android:textColor="@android:color/black"android:textSize="28dp"android:shadowColor="#80000000"android:shadowDx="2"android:shadowDy="2"android:shadowRadius="4" /></FrameLayout><FrameLayoutandroid:id="@+id/BubFrame"android:layout_width="wrap_content"android:layout_height="wrap_content"><ImageButtonandroid:id="@+id/Sub"android:layout_width="wrap_content"android:layout_height="wrap_content"android:background="@drawable/button_grey_round"android:layout_marginStart="20dp"/><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:layout_marginLeft="11dp"android:padding="8dp"android:text=""android:textColor="@android:color/black"android:textSize="28dp"android:shadowColor="#80000000"android:shadowDx="2"android:shadowDy="2"android:shadowRadius="4" /></FrameLayout></LinearLayout><!-- 1 2 3 +      --><LinearLayoutandroid:id="@+id/Four"android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"android:paddingTop="15dp"><FrameLayoutandroid:id="@+id/OneNumFrame"android:layout_width="wrap_content"android:layout_height="wrap_content"><ImageButtonandroid:id="@+id/OneNum"android:layout_width="wrap_content"android:layout_height="wrap_content"android:background="@drawable/button_white_round"android:layout_marginStart="20dp"/><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:layout_marginLeft="11dp"android:padding="8dp"android:text="1"android:textColor="@android:color/black"android:textSize="28dp"android:shadowColor="#80000000"android:shadowDx="2"android:shadowDy="2"android:shadowRadius="4" /></FrameLayout><FrameLayoutandroid:id="@+id/TwoNumFrame"android:layout_width="wrap_content"android:layout_height="wrap_content"><ImageButtonandroid:id="@+id/TwoNum"android:layout_width="wrap_content"android:layout_height="wrap_content"android:background="@drawable/button_white_round"android:layout_marginStart="20dp"/><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:layout_marginLeft="11dp"android:padding="8dp"android:text="2"android:textColor="@android:color/black"android:textSize="28dp"android:shadowColor="#80000000"android:shadowDx="2"android:shadowDy="2"android:shadowRadius="4" /></FrameLayout><FrameLayoutandroid:id="@+id/ThreeNumFrame"android:layout_width="wrap_content"android:layout_height="wrap_content"><ImageButtonandroid:id="@+id/ThreeNum"android:layout_width="wrap_content"android:layout_height="wrap_content"android:background="@drawable/button_white_round"android:layout_marginStart="20dp"/><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:layout_marginLeft="11dp"android:padding="8dp"android:text="3"android:textColor="@android:color/black"android:textSize="28dp"android:shadowColor="#80000000"android:shadowDx="2"android:shadowDy="2"android:shadowRadius="4" /></FrameLayout><FrameLayoutandroid:id="@+id/AddFrame"android:layout_width="wrap_content"android:layout_height="wrap_content"><ImageButtonandroid:id="@+id/add"android:layout_width="wrap_content"android:layout_height="wrap_content"android:background="@drawable/button_grey_round"android:layout_marginStart="20dp"/><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:layout_marginLeft="11dp"android:padding="8dp"android:text=""android:textColor="@android:color/black"android:textSize="28dp"android:shadowColor="#80000000"android:shadowDx="2"android:shadowDy="2"android:shadowRadius="4" /></FrameLayout></LinearLayout><!-- 00 0 . =      --><LinearLayoutandroid:id="@+id/Five"android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"android:paddingTop="15dp"><FrameLayoutandroid:id="@+id/CFrame"android:layout_width="wrap_content"android:layout_height="wrap_content"><ImageButtonandroid:id="@+id/C"android:layout_width="wrap_content"android:layout_height="wrap_content"android:background="@drawable/button_white_round"android:layout_marginStart="20dp"/><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="C"android:textSize="25sp"android:textColor="@android:color/black"android:layout_gravity="center"android:layout_marginLeft="10dp"android:padding="8dp"android:shadowColor="#80000000"android:shadowDx="2"android:shadowDy="2"android:shadowRadius="4"/></FrameLayout><FrameLayoutandroid:id="@+id/ONumFrame"android:layout_width="wrap_content"android:layout_height="wrap_content"><ImageButtonandroid:id="@+id/O"android:layout_width="wrap_content"android:layout_height="wrap_content"android:background="@drawable/button_white_round"android:layout_marginStart="20dp"/><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:layout_marginLeft="11dp"android:padding="8dp"android:text="0"android:textColor="@android:color/black"android:textSize="28dp"android:shadowColor="#80000000"android:shadowDx="2"android:shadowDy="2"android:shadowRadius="4" /></FrameLayout><FrameLayoutandroid:id="@+id/PointFrame"android:layout_width="wrap_content"android:layout_height="wrap_content"><ImageButtonandroid:id="@+id/Point"android:layout_width="wrap_content"android:layout_height="wrap_content"android:background="@drawable/button_white_round"android:layout_marginStart="20dp"/><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:layout_marginLeft="11dp"android:paddingBottom="10dp"android:text="."android:textColor="@android:color/black"android:textSize="28dp"android:shadowColor="#80000000"android:shadowDx="2"android:shadowDy="2"android:shadowRadius="4" /></FrameLayout><FrameLayoutandroid:id="@+id/equalFrame"android:layout_width="wrap_content"android:layout_height="wrap_content"><ImageButtonandroid:id="@+id/equal"android:layout_width="wrap_content"android:layout_height="wrap_content"android:background="@drawable/button_orange_round"android:layout_marginStart="20dp"/><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:layout_marginLeft="11dp"android:paddingBottom="15dp"android:text=""android:textColor="@android:color/white"android:textSize="45dp"android:shadowColor="#80000000"android:shadowDx="2"android:shadowDy="2"android:shadowRadius="4" /></FrameLayout></LinearLayout></LinearLayout></LinearLayout>

我们简单分解一下代码中的重点

1.1 给文本制作阴影并加粗

<TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="("android:textSize="25sp"android:textColor="@android:color/black"android:layout_gravity="center"android:layout_marginLeft="10dp"android:padding="8dp"android:shadowColor="#80000000"android:shadowDx="2"android:shadowDy="2"android:shadowRadius="4"android:textStyle="bold/>
  1. android:shadowColor="#80000000":表示为该视图的文本添加阴影效果,阴影颜色为半透明黑色(#80000000)。
  2. android:shadowDx="2":表示阴影在水平方向上的偏移量为2个像素。
  3. android:shadowDy="2":表示阴影在垂直方向上的偏移量为2个像素。
  4. android:shadowRadius="4":表示阴影的模糊半径为4个像素。
  5. android:textStyle="bold":是对TextView(文本视图)的一个属性设置,用于指定文本的字体样式为粗体(加粗)。

2.2 制作圆形按钮

在drawable目录下新建xml文件如下:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"android:shape="oval"><solid android:color="@color/orange" /><size android:height="60dp" android:width="60dp"/>
</shape>

android:shape指定图形,常用参数有:

  1. "rectangle": 表示创建一个矩形的形状。
  2. "oval": 表示创建一个椭圆的形状,也就是圆形。
  3. "line": 表示创建一条线段的形状。
  4. "ring": 表示创建一个圆环的形状。
  5. "line": 表示创建一条线段的形状。

solid:用于填充颜色

**size **:用于指定大小,因为是圆形我们这里长宽都要设置为相同

效果如下:

image-20230723170250310

2.3 制作点击变色的按钮

笔者在这里,没有使用点击变色而是使用了动画,后面介绍动画。这里介绍一下怎么点击变色。

首先还是在drawable目录下新建一个xml文件

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 定义按下状态的样式 -->
<item android:state_pressed="true"><shape android:shape="oval"><solid android:color="@color/Greywhite" /><size android:height="60dp" android:width="60dp"/></shape>
</item>
<!-- 定义普通状态的样式 -->
<item><shape android:shape="oval"><solid android:color="@color/Greywhite4" /><size android:height="60dp" android:width="60dp"/></shape>
</item>
</selector>

设置一个selector,如何设置两个item选项,设置为android:state_pressed="true"的选项在点击后就会产生对应的变化。

2、点击事件与界面处理

1、禁用输入法修改EditText文本内容

    protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);activityMainBinding = ActivityMainBinding.inflate(getLayoutInflater());setContentView(activityMainBinding.getRoot());//禁用输入法修改EditText文本内容activityMainBinding.ScanfEditText.setInputType(InputType.TYPE_NULL);//传入各个按键//功能键activityMainBinding.C.setOnClickListener(this);activityMainBinding.DeleteOne.setOnClickListener(this);//加减乘除,括号,等于activityMainBinding.add.setOnClickListener(this);activityMainBinding.Sub.setOnClickListener(this);activityMainBinding.Multiply.setOnClickListener(this);activityMainBinding.Division.setOnClickListener(this);activityMainBinding.leftC.setOnClickListener(this);activityMainBinding.RightC.setOnClickListener(this);activityMainBinding.equal.setOnClickListener(this);//数字,小数点activityMainBinding.O.setOnClickListener(this);activityMainBinding.OneNum.setOnClickListener(this);activityMainBinding.TwoNum.setOnClickListener(this);activityMainBinding.ThreeNum.setOnClickListener(this);activityMainBinding.FourNum.setOnClickListener(this);activityMainBinding.FiveNum.setOnClickListener(this);activityMainBinding.SixNum.setOnClickListener(this);activityMainBinding.SevenNum.setOnClickListener(this);activityMainBinding.EightNum.setOnClickListener(this);activityMainBinding.NineNum.setOnClickListener(this);activityMainBinding.Point.setOnClickListener(this);activityMainBinding.TwoArrowsShrink.setOnClickListener(this);//长按触发菜单registerForContextMenu(activityMainBinding.ThreePoints);}activityMainBinding.ScanfEditText.setInputType(InputType.TYPE_NULL);

通过设置 InputType.TYPE_NULLEditText 将不再响应输入法的输入,用户无法通过软键盘输入文本内容。这样做的目的通常是为了限制用户只能通过其他途径(比如扫码、点击按钮等)来输入文本内容,而不能直接通过键盘输入。如何我们为每个按钮注册监听事件

2、按键缩放动画和实现震动效果

    private void animation(View view) {// 加载缩放动画资源final AnimatorSet scaleAnimation = (AnimatorSet) AnimatorInflater.loadAnimator(this, R.animator.scale_animation);//实现震动效果/***         Vibrator vibrator=(Vibrator)getSystemService(Service.VIBRATOR_SERVICE);*         vibrator.vibrate(new long[]{0,100}, -1);* 从Android API level 26开始,Vibrator类的vibrate()方法被弃用,推荐使用VibrationEffect来实现震动效果。VibrationEffect类提供了更灵活和可控的震动方式。*/// 获取Vibrator实例Vibrator vibrator = (Vibrator) getSystemService(Service.VIBRATOR_SERVICE);// 判断Android版本是否大于等于API 26if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {// 创建VibrationEffect对象,指定震动模式和震动时长100msVibrationEffect vibrationEffect = VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE);// 开始震动vibrator.vibrate(vibrationEffect);} else {// Android版本低于API 26,使用老版本的vibrate方法//震动时间为100msvibrator.vibrate(100);}// 开始执行缩放动画scaleAnimation.setTarget(view);scaleAnimation.start();scaleAnimation.addListener(new AnimatorListenerAdapter() {@Overridepublic void onAnimationEnd(Animator animation) {// 在缩放动画结束时,创建还原动画并执行还原动画ObjectAnimator restoreAnimationX = ObjectAnimator.ofFloat(view, "scaleX", 1.0f);ObjectAnimator restoreAnimationY = ObjectAnimator.ofFloat(view, "scaleY", 1.0f);AnimatorSet restoreAnimation = new AnimatorSet();restoreAnimation.playTogether(restoreAnimationX, restoreAnimationY);restoreAnimation.start();}});}

首先加载缩放动画资源。

final AnimatorSet scaleAnimation = (AnimatorSet) AnimatorInflater.loadAnimator(this, R.animator.scale_animation);

scale_animationanimator下设置的动画属性

具体设置如下:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"><objectAnimatorandroid:propertyName="scaleX"android:valueFrom="1.0"android:valueTo="0.9"android:duration="100"android:repeatCount="0"android:repeatMode="restart"android:interpolator="@android:anim/accelerate_decelerate_interpolator" /><objectAnimatorandroid:propertyName="scaleY"android:valueFrom="1.0"android:valueTo="0.9"android:duration="100"android:repeatCount="0"android:repeatMode="restart"android:interpolator="@android:anim/accelerate_decelerate_interpolator" />
</set>
  1. android:propertyName: 定义要动画化的属性的名称。在这里,scaleXscaleY 分别表示水平和垂直方向的缩放。
  2. android:valueFrom: 定义动画的起始值。这里起始值为 1.0,表示不缩放。
  3. android:valueTo: 定义动画的结束值。这里结束值为 0.9,表示缩放到原始大小的 0.9 倍。
  4. android:duration: 定义动画的持续时间,以毫秒为单位。这里持续时间为 100 毫秒,即缩放动画的过程为 0.1 秒。
  5. android:repeatCount: 定义动画重复的次数。这里设置为 0,表示动画不重复。
  6. android:repeatMode: 定义动画重复的模式。这里设置为 restart,表示每次重复动画都从头开始。
  7. android:interpolator: 定义动画的插值器,用于控制动画的变化速率。这里使用了 Android 系统自带的 accelerate_decelerate_interpolator 插值器,表示动画先加速后减速。

这个 XML 文件定义了两个属性动画,分别是在水平方向和垂直方向上的缩放动画效果。在 Android 应用中,你可以通过加载这个 XML 文件来应用这个缩放动画,让指定的视图(如 ViewImageView)在缩放动画的作用下,由原始大小缩放到 0.9 倍的大小。

制作动画时,创建了一个 scaleAnimation 缩放动画,然后给该动画添加了一个监听器 AnimatorListenerAdapter。在监听器的 onAnimationEnd() 方法中,当缩放动画结束时,会执行还原动画。

大致逻辑:

  1. 创建缩放动画 scaleAnimation 并设置属性动画的相关参数。
  2. scaleAnimation 添加一个监听器,在缩放动画结束时执行还原动画。
  3. 创建两个 ObjectAnimator 分别对 viewscaleXscaleY 属性进行动画处理,将 view 的缩放比例恢复为原始大小(1.0f)。
  4. 将还原动画 restoreAnimation 中的两个动画一起播放。

3、实现显示框字体大小变化的动画

当我们计算完结果后,我们让计算的结果变大,计算的算式变小

private void DisplayFrameAnimation(View Little_big, View Big_little) {        // 加载动画资源,从25变到40AnimatorSet Little_Big = (AnimatorSet) AnimatorInflater.loadAnimator(this, R.animator.text_size_25_40);// 设置目标视图Little_Big.setTarget(Little_big);// 启动动画Little_Big.start();// 加载动画资源,从40变到25AnimatorSet Big_Little = (AnimatorSet) AnimatorInflater.loadAnimator(this, R.animator.text_size_40_25);// 设置目标视图Big_Little.setTarget(Big_little);// 启动动画Big_Little.start();
}

AnimatorInflater: 这是一个用于加载属性动画的类。它提供了一个方法 loadAnimator(),可以通过指定资源ID加载属性动画。

  1. this: 这里的 this 指的是当前的上下文,即所在的 Activity
  2. R.animator.text_size_40_25: 这是指定的属性动画资源的资源ID。在这里,text_size_40_25 是一个位于 res/animator 目录下的 XML 文件,它定义了一组属性动画。

同样这样的操作需要动画文件,我们在这里介绍一下字体大小由25变化到40的

<?xml version="1.0" encoding="utf-8"?>
<!-- text_size_change.xml -->
<set xmlns:android="http://schemas.android.com/apk/res/android"><objectAnimatorandroid:propertyName="textSize"android:valueType="floatType"android:valueFrom="25"android:valueTo="40"android:duration="500"/>
</set>
  1. <set>:这是属性动画的根元素,用于定义属性动画集合。在这里,我们只有一个动画,所以只有一个 <objectAnimator> 子元素。
  2. <objectAnimator>:这是一个对象动画元素,用于定义属性动画。它有以下属性:
    • android:propertyName="textSize":这是要动画化的属性名称,即视图的文本大小属性 textSize
    • android:valueType="floatType":这是动画值的类型,这里是浮点数类型,用于表示文本大小。
    • android:valueFrom="25":这是动画的起始值,即文本大小的初始值为 25。
    • android:valueTo="40":这是动画的结束值,即文本大小的目标值为 40。
    • android:duration="500":这是动画的持续时间,即从起始值到结束值的过渡时间为 500 毫秒。

3、逻辑与算法

1.1 按钮点击结果的分析与处理

package com.example.computermoblie;import android.animation.Animator;
import android.animation.AnimatorInflater;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.app.Service;
import android.os.Build;
import android.os.Bundle;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.text.InputType;
import android.util.Log;
import android.view.ContextMenu;
import android.view.MenuItem;
import android.view.View;import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;import com.example.computermoblie.databinding.ActivityMainBinding;import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;public class MainActivity extends AppCompatActivity implements View.OnClickListener {// 计算器储存总数据的上限public List<String> SumNumber = new ArrayList<>();//计算器存储的数字上限private StringBuilder Number = new StringBuilder();//当前输入框字体大小,初始状态为40dpprivate int SIzeOfText = 40;public ActivityMainBinding activityMainBinding;private boolean GetEqual = false;    //表示没有按下等号private boolean NegativeSymbol = false; //表示不是负数的符号public String ret; //运算结果private static final String Del = "Delete"; //表示删除键@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);activityMainBinding = ActivityMainBinding.inflate(getLayoutInflater());setContentView(activityMainBinding.getRoot());//禁用输入法修改EditText文本内容activityMainBinding.ScanfEditText.setInputType(InputType.TYPE_NULL);//传入各个按键//功能键activityMainBinding.C.setOnClickListener(this);activityMainBinding.DeleteOne.setOnClickListener(this);//加减乘除,括号,等于activityMainBinding.add.setOnClickListener(this);activityMainBinding.Sub.setOnClickListener(this);activityMainBinding.Multiply.setOnClickListener(this);activityMainBinding.Division.setOnClickListener(this);activityMainBinding.leftC.setOnClickListener(this);activityMainBinding.RightC.setOnClickListener(this);activityMainBinding.equal.setOnClickListener(this);//数字,小数点activityMainBinding.O.setOnClickListener(this);activityMainBinding.OneNum.setOnClickListener(this);activityMainBinding.TwoNum.setOnClickListener(this);activityMainBinding.ThreeNum.setOnClickListener(this);activityMainBinding.FourNum.setOnClickListener(this);activityMainBinding.FiveNum.setOnClickListener(this);activityMainBinding.SixNum.setOnClickListener(this);activityMainBinding.SevenNum.setOnClickListener(this);activityMainBinding.EightNum.setOnClickListener(this);activityMainBinding.NineNum.setOnClickListener(this);activityMainBinding.Point.setOnClickListener(this);activityMainBinding.TwoArrowsShrink.setOnClickListener(this);//长按触发菜单registerForContextMenu(activityMainBinding.ThreePoints);}@Overridepublic void onClick(View view) {final int Id = view.getId();// 调用按键动画animation(view);//退出if(Id == R.id.TwoArrows_Shrink){finish();}//功能按钮if (Id == R.id.C) {//清空显示activityMainBinding.ScanfEditText.setText("");ret = "";activityMainBinding.PrintfText.setText(ret);//清空储存的内容SumNumber.clear();Number.setLength(0);//还原字体大小SIzeOfText = 40;} else {if (Id == R.id.leftC) {if (!GetEqual) {if (Number.length() > 0) {char cat = Number.charAt(Number.length() - 1);if (cat != '.' && !Character.isDigit(cat)) {SumNumber.add("(");} else {//左括号前面没有符号只有数字默认为乘号//将number的数据放入Sumnumberif (!Number.equals("") && Number != null) {//保存数据try {SumNumber.add(String.valueOf(Number));} catch (NumberFormatException e) {Log.d("Number", "出现空值");}}Number.setLength(0);SumNumber.add("×");SumNumber.add("(");}} else if (!SumNumber.isEmpty()) {//判断是不是存在两个相邻的操作String str = SumNumber.get(SumNumber.size() - 1);if (!str.equals("+") && !str.equals("-") && !str.equals("×") && !str.equals("÷") && !str.equals("(")) {SumNumber.add("×");SumNumber.add("(");} else {SumNumber.add("(");}}//开头就使用括号的情况if (SumNumber.isEmpty()){SumNumber.add("(");}}}if (Id == R.id.RightC) {if (!GetEqual) {if (Number.length() > 0) {char cat = Number.charAt(Number.length() - 1);if (cat != '.') {//将number的数据放入Sumnumberif (!Number.equals("") && Number != null) {//保存数据try {SumNumber.add(String.valueOf(Number));} catch (NumberFormatException e) {Log.d("Number", "出现空值");}}Number.setLength(0);SumNumber.add(")");//使得按下后动态计算结果DynamicCalculation();}} else if (!SumNumber.isEmpty()) {//判断是不是存在两个相邻的操作String str = SumNumber.get(SumNumber.size() - 1);Log.d("DEL1", SumNumber.toString());if (!str.equals("+") && !str.equals("-") && !str.equals("×") && !str.equals("÷") && !str.equals("(")) {SumNumber.add(")");//使得按下后动态计算结果DynamicCalculation();}}}}if (Id == R.id.DeleteOne) {if (!GetEqual) {if (Number.length() > 0) {Number.deleteCharAt(Number.length() - 1);} else if (SumNumber.size() > 0) {try {Number.setLength(0);Number.append(SumNumber.remove(SumNumber.size() - 1));Number.deleteCharAt(Number.length() - 1);}catch (NumberFormatException e){Log.d("Number", "出现空值");}}else {ret = "";activityMainBinding.PrintfText.setText(ret);}//删除所有数据后清除结果if (SumNumber.isEmpty()){ListComputing(Del);}}else {ListComputing(Del);}//使得按下后动态计算结果DynamicCalculation();}//加减乘除if (Id == R.id.add) {if (!GetEqual) {if (Number.length() > 0) {if (Number.charAt(Number.length() - 1) != '.') {//将number的数据放入Sumnumberif (!Number.equals("") && Number != null) {//保存数据try {SumNumber.add(String.valueOf(Number));} catch (NumberFormatException e) {Log.d("Number", "出现空值");}}Number.setLength(0);SumNumber.add("+");}} else if (!SumNumber.isEmpty()) {//判断是不是存在两个相邻的操作String str = SumNumber.get(SumNumber.size() - 1);if (!str.equals("+") && !str.equals("-") && !str.equals("×") && !str.equals("÷") && !str.equals("(")) {//将number的数据放入Sumnumberif (!Number.equals("") && Number != null) {//保存数据try {SumNumber.add(String.valueOf(Number));} catch (NumberFormatException e) {Log.d("Number", "出现空值");}}Log.d("String1", String.valueOf(Number));Number.setLength(0);SumNumber.add("+");}}} else {ListComputing("+");}}if (Id == R.id.Sub) {if (!GetEqual) {if (Number.length() > 0) {if (Number.charAt(Number.length() - 1) != '.') {//将number的数据放入Sumnumberif (!Number.equals("") && Number != null) {//保存数据try {SumNumber.add(String.valueOf(Number));} catch (NumberFormatException e) {Log.d("Number", "出现空值");}}Log.d("String1", String.valueOf(Number));Number.setLength(0);SumNumber.add("-");}} else if (!SumNumber.isEmpty()) {//判断是不是存在两个相邻的操作String str = SumNumber.get(SumNumber.size() - 1);if (!str.equals("+") && !str.equals("-") && !str.equals("×") && !str.equals("÷") && !str.equals("(")) {//将number的数据放入Sumnumberif (!Number.equals("") && Number != null) {//保存数据try {SumNumber.add(String.valueOf(Number));} catch (NumberFormatException e) {Log.d("Number", "出现空值");}}Log.d("String1", String.valueOf(Number));Number.setLength(0);SumNumber.add("-");} else if (str.equals("×") || str.equals("÷")||str.equals("(")) {Number.append("-");}}else {Number.append("-");}} else {ListComputing("-");}}if (Id == R.id.Multiply) {if (!GetEqual) {if (Number.length() > 0) {if (Number.charAt(Number.length() - 1) != '.') {//将number的数据放入Sumnumberif (!Number.equals("") && Number != null) {//保存数据try {SumNumber.add(String.valueOf(Number));} catch (NumberFormatException e) {Log.d("Number", "出现空值");}}Log.d("String1", String.valueOf(Number));Number.setLength(0);SumNumber.add("×");}} else if (!SumNumber.isEmpty()) {//判断是不是存在两个相邻的操作String str = SumNumber.get(SumNumber.size() - 1);if (!str.equals("+") && !str.equals("-") && !str.equals("×") && !str.equals("÷") && !str.equals("(")) {//将number的数据放入Sumnumberif (!Number.equals("") && Number != null) {//保存数据try {SumNumber.add(String.valueOf(Number));} catch (NumberFormatException e) {Log.d("Number", "出现空值");}}Log.d("String1", String.valueOf(Number));Number.setLength(0);SumNumber.add("×");}}} else {ListComputing("×");}}if (Id == R.id.Division) {if (!GetEqual) {if (Number.length() > 0) {if (Number.charAt(Number.length() - 1) != '.') {//将number的数据放入Sumnumberif (!Number.equals("") && Number != null) {//保存数据try {SumNumber.add(String.valueOf(Number));} catch (NumberFormatException e) {Log.d("Number", "出现空值");}}Log.d("String1", String.valueOf(Number));Number.setLength(0);SumNumber.add("÷");}} else if (!SumNumber.isEmpty()) {//判断是不是存在两个相邻的操作String str = SumNumber.get(SumNumber.size() - 1);if (!str.equals("+") && !str.equals("-") && !str.equals("×") && !str.equals("÷") && !str.equals("(")) {//将number的数据放入Sumnumberif (!Number.equals("") && Number != null) {//保存数据try {SumNumber.add(String.valueOf(Number));} catch (NumberFormatException e) {Log.d("Number", "出现空值");}}Log.d("String1", String.valueOf(Number));Number.setLength(0);SumNumber.add("÷");}}} else {ListComputing("÷");}}//数字if (Id == R.id.OneNum) {ClearDisplay();//清空显示if (!SumNumber.isEmpty()) {String str = SumNumber.get(SumNumber.size() - 1);if (str.equals(")")) {SumNumber.add("×");}}Number.append("1");//                //使得按下数字后动态计算结果DynamicCalculation();}if (Id == R.id.TwoNum) {ClearDisplay();//清空显示if (!SumNumber.isEmpty()) {String str = SumNumber.get(SumNumber.size() - 1);if (str.equals(")")) {SumNumber.add("×");}}Number.append("2");//                //使得按下数字后动态计算结果DynamicCalculation();}if (Id == R.id.ThreeNum) {ClearDisplay();//清空显示if (!SumNumber.isEmpty()) {String str = SumNumber.get(SumNumber.size() - 1);if (str.equals(")")) {SumNumber.add("×");}}Number.append("3");//                //使得按下数字后动态计算结果DynamicCalculation();}if (Id == R.id.FourNum) {ClearDisplay();//清空显示if (!SumNumber.isEmpty()) {String str = SumNumber.get(SumNumber.size() - 1);if (str.equals(")")) {SumNumber.add("×");}}Number.append("4");//                //使得按下数字后动态计算结果DynamicCalculation();}if (Id == R.id.FiveNum) {ClearDisplay();//清空显示if (!SumNumber.isEmpty()) {String str = SumNumber.get(SumNumber.size() - 1);if (str.equals(")")) {SumNumber.add("×");}}Number.append("5");//                //使得按下数字后动态计算结果DynamicCalculation();}if (Id == R.id.SixNum) {ClearDisplay();//清空显示if (!SumNumber.isEmpty()) {String str = SumNumber.get(SumNumber.size() - 1);if (str.equals(")")) {SumNumber.add("×");}}Number.append("6");//                //使得按下数字后动态计算结果DynamicCalculation();}if (Id == R.id.SevenNum) {ClearDisplay();//清空显示if (!SumNumber.isEmpty()) {String str = SumNumber.get(SumNumber.size() - 1);if (str.equals(")")) {SumNumber.add("×");}}Number.append("7");//                //使得按下数字后动态计算结果DynamicCalculation();}if (Id == R.id.EightNum) {ClearDisplay();//清空显示if (!SumNumber.isEmpty()) {String str = SumNumber.get(SumNumber.size() - 1);if (str.equals(")")) {SumNumber.add("×");}}Number.append("8");//                //使得按下数字后动态计算结果DynamicCalculation();}if (Id == R.id.NineNum) {ClearDisplay();//清空显示if (!SumNumber.isEmpty()) {String str = SumNumber.get(SumNumber.size() - 1);if (str.equals(")")) {SumNumber.add("×");}}Number.append("9");//使得按下数字后动态计算结果DynamicCalculation();}if (Id == R.id.O) {ClearDisplay();//清空显示if (!SumNumber.isEmpty()) {String str = SumNumber.get(SumNumber.size() - 1);if (str.equals(")")) {SumNumber.add("×");}}Number.append("0");//使得按下数字后动态计算结果DynamicCalculation();}if (Id == R.id.Point) {if (Number.length() > 0) {int t = 0;for (int i = 0; i < Number.length(); i++) {if (Number.charAt(i) == '.') {t++;}}if (t < 1) {Number.append(".");}}}if (Id == R.id.equal) {if (Number.length() > 0) {if (Number.charAt(Number.length() - 1) != '.') {//将number的数据放入Sumnumberif (!Number.equals("") && Number != null) {//保存数据try {SumNumber.add(String.valueOf(Number));} catch (NumberFormatException e) {Log.d("Number", "出现空值");}}Number.setLength(0);//表示等号已经按下,用于清除文本框GetEqual = true;//计算结果ReversePolish reversePolish = new ReversePolish(SumNumber);ret = reversePolish.EvaluatePostfixExpressions();//加载大小变化的动画DisplayFrameAnimation(activityMainBinding.PrintfText, activityMainBinding.ScanfEditText);}} else if (!SumNumber.isEmpty()) {//判断是不是存在两个相邻的操作String str = SumNumber.get(SumNumber.size() - 1);if (str.equals(")")) {//将number的数据放入Sumnumberif (!Number.equals("") && Number != null) {//保存数据try {SumNumber.add(String.valueOf(Number));} catch (NumberFormatException e) {Log.d("Number", "出现空值");}}Number.setLength(0);//表示等号已经按下,用于清除文本框GetEqual = true;//计算结果ReversePolish reversePolish = new ReversePolish(SumNumber);ret = reversePolish.EvaluatePostfixExpressions();//加载大小变化的动画DisplayFrameAnimation(activityMainBinding.PrintfText, activityMainBinding.ScanfEditText);}}}//显示文本框内容if (Number != null) {StringBuilder builder = new StringBuilder();for (String str : SumNumber) {builder.append(str);}builder.append(Number);activityMainBinding.ScanfEditText.setText(builder);activityMainBinding.PrintfText.setText(ret);MaxDisplayFrameAnimation(activityMainBinding.ScanfEditText);}}}//按键缩放动画和实现震动效果private void animation(View view) {// 加载缩放动画资源final AnimatorSet scaleAnimation = (AnimatorSet) AnimatorInflater.loadAnimator(this, R.animator.scale_animation);//实现震动效果/***         Vibrator vibrator=(Vibrator)getSystemService(Service.VIBRATOR_SERVICE);*         vibrator.vibrate(new long[]{0,100}, -1);* 从Android API level 26开始,Vibrator类的vibrate()方法被弃用,推荐使用VibrationEffect来实现震动效果。VibrationEffect类提供了更灵活和可控的震动方式。*/// 获取Vibrator实例Vibrator vibrator = (Vibrator) getSystemService(Service.VIBRATOR_SERVICE);// 判断Android版本是否大于等于API 26if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {// 创建VibrationEffect对象,指定震动模式和震动时长VibrationEffect vibrationEffect = VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE);// 开始震动vibrator.vibrate(vibrationEffect);} else {// Android版本低于API 26,使用老版本的vibrate方法vibrator.vibrate(100);}// 开始执行缩放动画scaleAnimation.setTarget(view);scaleAnimation.start();scaleAnimation.addListener(new AnimatorListenerAdapter() {@Overridepublic void onAnimationEnd(Animator animation) {// 在缩放动画结束时,创建还原动画并执行还原动画ObjectAnimator restoreAnimationX = ObjectAnimator.ofFloat(view, "scaleX", 1.0f);ObjectAnimator restoreAnimationY = ObjectAnimator.ofFloat(view, "scaleY", 1.0f);AnimatorSet restoreAnimation = new AnimatorSet();restoreAnimation.playTogether(restoreAnimationX, restoreAnimationY);restoreAnimation.start();}});}// 显示框的动画效果private void DisplayFrameAnimation(View Little_big, View Big_little) {/*** SIzeOfText == 40 由上方40-25 下方25-40* SIzeOfText == 30 由上方30-25 下方25-30* SIzeOfText == 25 由上方不变 下方25-30*/if (SIzeOfText == 40) {// 加载动画资源,从25变到40AnimatorSet Little_Big = (AnimatorSet) AnimatorInflater.loadAnimator(this, R.animator.text_size_25_40);// 设置目标视图Little_Big.setTarget(Little_big);// 启动动画Little_Big.start();// 加载动画资源,从40变到25AnimatorSet Big_Little = (AnimatorSet) AnimatorInflater.loadAnimator(this, R.animator.text_size_40_25);// 设置目标视图Big_Little.setTarget(Big_little);// 启动动画Big_Little.start();} else if (SIzeOfText == 30) {Log.d("SIzeOfText", String.valueOf(SIzeOfText));// 加载动画资源,从25变到30AnimatorSet Little_Big = (AnimatorSet) AnimatorInflater.loadAnimator(this, R.animator.text_size_25_30);// 设置目标视图Little_Big.setTarget(Little_big);// 启动动画Little_Big.start();// 加载动画资源,从30变到25AnimatorSet Big_Little = (AnimatorSet) AnimatorInflater.loadAnimator(this, R.animator.text_size_30_25);// 设置目标视图Big_Little.setTarget(Big_little);// 启动动画Big_Little.start();} else if (SIzeOfText == 25) {// 加载动画资源,从25变到30AnimatorSet Little_Big = (AnimatorSet) AnimatorInflater.loadAnimator(this, R.animator.text_size_25_30);// 设置目标视图Little_Big.setTarget(Little_big);// 启动动画Little_Big.start();}}//显示框的字符达到指定长度使得一行无法放下private void MaxDisplayFrameAnimation(View Big_littleView) {int MaxDigit = 0;for (String str : SumNumber) {MaxDigit += str.length();}MaxDigit += Number.length();Log.d("StrSize", "num:" + MaxDigit);if (MaxDigit == 10) {SIzeOfText = 30;    //字体变化为30// 加载动画资源,从40变到30AnimatorSet Big_Little = (AnimatorSet) AnimatorInflater.loadAnimator(this, R.animator.text_size_40_30);// 设置目标视图Big_Little.setTarget(Big_littleView);// 启动动画Big_Little.start();}if (MaxDigit > 10 && MaxDigit < 25) activityMainBinding.ScanfEditText.setTextSize(30);if (MaxDigit == 15) {SIzeOfText = 25;    //字体变化为25// 加载动画资源,从30变到25AnimatorSet Big_Little = (AnimatorSet) AnimatorInflater.loadAnimator(this, R.animator.text_size_30_25);// 设置目标视图Big_Little.setTarget(Big_littleView);// 启动动画Big_Little.start();activityMainBinding.ScanfEditText.setTextSize(25);}if (MaxDigit > 15) activityMainBinding.ScanfEditText.setTextSize(25);}//加载菜单@Overridepublic void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {Log.d("Tag", "加载菜单");getMenuInflater().inflate(R.menu.function, menu);super.onCreateContextMenu(menu, v, menuInfo);}//注册菜单点击事件@Overridepublic boolean onContextItemSelected(@NonNull MenuItem item) {Log.d("Tag", "点击事件");final int Id = item.getItemId();if (Id == R.id.Log) {//历史记录} else if (Id == R.id.SetUp) {//设置}return super.onContextItemSelected(item);}//清空显示private void ClearDisplay() {//还原显示框大小activityMainBinding.ScanfEditText.setTextSize(40);activityMainBinding.PrintfText.setTextSize(30);//表示已经按下=后,重新输入数字的情况if (GetEqual) {GetEqual = false;activityMainBinding.ScanfEditText.setText("");ret = "";//清空储存的内容SumNumber.clear();Number.setLength(0);//还原字体大小SIzeOfText = 40;}}//连续计算的情况private void ListComputing(String str) {if (str.equals(Del)) {GetEqual = false;//还原显示框大小activityMainBinding.ScanfEditText.setTextSize(40);activityMainBinding.PrintfText.setTextSize(30);SIzeOfText = 40;ret = "";if (Number.length() > 0) {Number.deleteCharAt(Number.length() - 1);} else if (SumNumber.size() > 0) {try {Number.setLength(0);Number.append(SumNumber.remove(SumNumber.size() - 1));Number.deleteCharAt(Number.length() - 1);}catch (NumberFormatException e){Log.d("Number", "出现空值");}}} else if (!ret.equals("Error")){GetEqual = false;//还原显示框大小activityMainBinding.ScanfEditText.setTextSize(40);activityMainBinding.PrintfText.setTextSize(30);//清空储存的内容SumNumber.clear();Number.setLength(0);//还原字体大小SIzeOfText = 40;SumNumber.add(ret);SumNumber.add(str);ret = "";}}//使得按下数字后动态计算结果private void DynamicCalculation() {if(!SumNumber.isEmpty() && SumNumber.size() > 0){//将number的数据放入Sumnumberif (!Number.equals("") && Number != null) {//保存数据try {SumNumber.add(String.valueOf(Number));} catch (NumberFormatException e) {Log.d("Number", "出现空值");}}//计算当前结果ReversePolish reversePolish = new ReversePolish(SumNumber);ret = reversePolish.EvaluatePostfixExpressions();//删去暂时存放的数据SumNumber.remove(SumNumber.size() - 1);}}
}

2.2 中缀表达式转后缀表达式,使用逆波兰计算

package com.example.computermoblie;import android.content.Intent;
import android.util.Log;
import android.view.View;import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.List;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.Stack;public class ReversePolish {public List<String> SumNumber;Stack<BigDecimal> SymbolStack = new Stack<>();//表示没有计算错误的情况private boolean Error = false;public static final int LeftBrack = 0;   //左括号优先级public static final int AandS = 1;   //加减法public static final int MandD = 2;   //乘除public static final int RightBrack = 3;   //右括号优先级//中缀转后缀public Queue<String> InfixToSuffix() {Queue<String> queue = new ArrayDeque<>();Stack<String> swapStack = new Stack<>();for (int i = 0; i < SumNumber.size(); i++) {String isStr = SumNumber.get(i);if (!IsSymbol(isStr)) {queue.add(isStr); // 将数字直接添加到输出队列中} else {if (swapStack.isEmpty() || isStr.equals("(")) {swapStack.push(isStr);} else if (isStr.equals(")")) {if(!swapStack.isEmpty()){while (!swapStack.peek().equals("(")) {queue.add(swapStack.pop());}swapStack.pop(); // 弹出 "("}} else {while (!swapStack.isEmpty() && JudgmentPriority(swapStack.peek(), isStr)) {queue.add(swapStack.pop());}swapStack.push(isStr);}}}while (!swapStack.isEmpty()) {queue.add(swapStack.pop());}return queue;}public String EvaluatePostfixExpressions() {Error = false;  // 表示运算未发现错误BigDecimal ret = null; // 表示最后的运算结果Queue<String> queue = InfixToSuffix();  // 获取后缀表达式Log.d("WQWQ",queue.toString());while (!queue.isEmpty()) {String str = queue.remove();if (!IsSymbol(str)) {//保存数据try {BigDecimal number = new BigDecimal(str);SymbolStack.push(number);} catch (NumberFormatException e) {Log.d("Number","出现空值");}} else {if (SymbolStack.size() < 2) {Error = true; // 栈中元素不足2个,运算错误break;}BigDecimal op2 = SymbolStack.pop();BigDecimal op1 = SymbolStack.pop();BigDecimal result = null;switch (str) {case "+":result = op1.add(op2);break;case "-":result = op1.subtract(op2);break;case "×":result = op1.multiply(op2);break;case "÷":if (op2.compareTo(BigDecimal.ZERO) == 0) {Error = true; // 除数为零,运算错误break;}result = op1.divide(op2, 3, RoundingMode.HALF_UP);break;default:Error = true; // 非法的运算符,运算错误break;}if (Error) {break;} else {SymbolStack.push(result);}}}if (Error) {return "Error"; // 出现错误后直接返回错误信息} else {if (!SymbolStack.isEmpty()) {ret = SymbolStack.pop();}// 使用toEngineeringString()方法将结果输出为工程计数法return ret.toEngineeringString();}}public ReversePolish(List<String> sumNumber) {SumNumber = sumNumber;}//判断是否为符号public boolean IsSymbol(String str) {boolean isSymbole = false;if (str.equals("+")) {isSymbole = true;} else if (str.equals("-")) {isSymbole = true;} else if (str.equals("×")) {isSymbole = true;} else if (str.equals("÷")) {isSymbole = true;} else if (str.equals("(")) {isSymbole = true;} else if (str.equals(")")) {isSymbole = true;}return isSymbole;}//判断优先级public boolean JudgmentPriority(String PreantStr, String SunStr) {//默认PreantStr优先级小于SunStrboolean value = false;if (SymbolValue(PreantStr) >= SymbolValue(SunStr)) {value = true;}return value;}//判断属于什么符号public int SymbolValue(String str) {int Value = LeftBrack;if (str.equals("+")) {Value = AandS;} else if (str.equals("-")) {Value = AandS;} else if (str.equals("×")) {Value = MandD;} else if (str.equals("÷")) {Value = MandD;} else if (str.equals("(")) {Value = LeftBrack;} else if (str.equals(")")) {Value = RightBrack;}return Value;}public List<String> getSumNumber() {return SumNumber;}
}

主要代码如上,此外笔者代码存在一个空指针异常(NullPointerException)的问题,问题出在SumNumber如果多次使用删除键反复输入数据在删除这种测试操作会导致空指针异常,使得操作SumNumber的代码逻辑访问到空指针。这个问题笔者解决了很久都没找到问题所在,如有找到问题所在烦请告知笔者,不胜感激。具体异常信息如下:

根据日志信息,异常的原因是在某个地方尝试调用一个对象的toString()方法,但该对象为空(null),因此无法执行该方法。

根据提供的堆栈跟踪信息,可以看出空指针异常(NullPointerException)在以下方法中被抛出:

  1. com.example.computermoblie.ReversePolish.InfixToSuffix(ReversePolish.java:45)

    image-20230723174051695

  2. com.example.computermoblie.ReversePolish.EvaluatePostfixExpressions(ReversePolish.java:69)

image-20230723174318764

  1. com.example.computermoblie.MainActivity.onClick(MainActivity.java:189)

image-20230723174451336

  1. com.example.computermoblie.MainActivity.DynamicCalculation(MainActivity.java:872)

image-20230723174428328

相关文章:

制作一个简易的计算器app

github项目地址&#xff1a;https://github.com/13008451162/AndroidMoblieCalculator 1、Ui开发 笔者的Ui制作的制作的比较麻烦仅供参考&#xff0c;在这里使用了多个LinearLayout对屏幕进行了划分。不建议大家这样做最好使用GridLayout会更加快捷简单 笔者大致划分是这样的…...

48. 旋转图像

题目介绍 给定一个 n n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。 你必须在** 原地** 旋转图像&#xff0c;这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。 示例 1&#xff1a; 输入&#xff1a;matrix [[1,2,3],[4,5,6]…...

“深入解析Spring Boot:从入门到精通的完整指南“

标题&#xff1a;深入解析Spring Boot&#xff1a;从入门到精通的完整指南 摘要&#xff1a;本文将深入解析Spring Boot框架&#xff0c;从入门到精通&#xff0c;为读者提供全面的指南。我们将介绍Spring Boot的基本概念、核心特性以及使用方法&#xff0c;并通过示例代码演示…...

【C++】C++11——包装器

文章目录 1. function包装器1.1 遇到的问题1.2 包装器的定义1.3 解决问题1.4 包装器的其他应用 2. bind2.1 bind的定义2.2 bind包装器绑定固定参数2.3 bind包装器调整传参顺序2.4 bind包装器的意义 1. function包装器 1.1 遇到的问题 我们首先来看一行代码&#xff1a; ret …...

插件使用权限管理软件(三)WebAPI项目IIS部署

前言 前面完成了WebAPI项目的接口服务类编写工作&#xff0c;接下来讲把项目部署到服务器的IIS上&#xff0c;让系统运行起来。 一. 项目发布 右键项目RightsManagementSystems.Web.Entry 选择“发布”选项 弹出发布选项界面&#xff0c;选择“文件夹”&#xff0c;点击下一步…...

[算法很美打卡] 多维数组篇 (打卡第二天)

文章目录 Z形打印边界为1的最大子方阵 Z形打印 package 每日算法学习打卡.算法打卡.七月份.七月二十七号;public class test1 {public static void main(String[] args) {int[][] matrix {{1, 2, 3, 4},{5, 6, 7, 8},{9, 10, 11, 12},};print(matrix);}static void print(int[…...

K8S初级入门系列之十一-安全

一、前言 安全是K8S重要的特性&#xff0c;在K8S初级入门系列之四-Namespace/ConfigMap/Secret章节&#xff0c;我们已经已经了解了Namespace&#xff0c;Secret与安全相关的知识。本篇将梳理K8S在安全方面的策略。主要包括两个方面&#xff0c;API安全访问策略以及Pod安全策略…...

【雕爷学编程】MicroPython动手做(02)——尝试搭建K210开发板的IDE环境6

#尝试搭建K210的Micropython开发环境&#xff08;Win10&#xff09; #实验程序之六&#xff1a;测试Microphone阵列算法 #尝试搭建K210的Micropython开发环境&#xff08;Win10&#xff09; #实验程序之六&#xff1a;测试Microphone阵列算法from Maix import MIC_ARRAY as mi…...

“深入解析Spring Boot:从入门到精通“

标题&#xff1a;深入解析Spring Boot&#xff1a;从入门到精通 摘要&#xff1a;本文深入解析了Spring Boot框架&#xff0c;从入门到精通&#xff0c;包括核心概念、特性、使用方法和示例代码。通过阅读本文&#xff0c;读者将对Spring Boot有一个全面的了解&#xff0c;并可…...

[自然语言处理] 自然语言处理库spaCy使用指北

spaCy是一个基于Python编写的开源自然语言处理库。基于自然处理领域的最新研究&#xff0c;spaCy提供了一系列高效且易用的工具&#xff0c;用于文本预处理、文本解析、命名实体识别、词性标注、句法分析和文本分类等任务。 spaCy的官方仓库地址为&#xff1a;spaCy-github。本…...

【新日语(2)】第6課 拓哉もさしみを食べたがってします

第6課 拓哉もさしみを食べたがっています 注释&#xff1a; 食べたがっています&#xff1a;食べ&#xff0b;たが&#xff0b;ています、想要吃。たがっています&#xff1a;たがる&#xff0b;ています、想要。 练习A 一、 例句 わたしは、明日、デパートへ行きます。 …...

uni-app 经验分享,从入门到离职(一)——初始 uni-app,快速上手(文末送书福利1.0)

文章目录 &#x1f4cb;前言&#x1f3af;什么是 uni-app&#x1f3af;创建第一个 uni-app 项目&#x1f9e9;前期工作&#x1f9e9;创建项目&#xff08;熟悉默认项目、结构&#xff09;&#x1f9e9;运行项目 &#x1f4dd;最后&#x1f3af;文末送书&#x1f525;参与方式 &…...

Python爬虫实例之淘宝商品页面爬取(api接口)

可以使用Python中的requests和BeautifulSoup库来进行网页爬取和数据提取。以下是一个简单的示例&#xff1a; import requests from bs4 import BeautifulSoupdef get_product_data(url):# 发送GET请求&#xff0c;获取网页内容headers {User-Agent: Mozilla/5.0 (Windows NT…...

并发编程 | CompletionService - 如何优雅地处理批量异步任务

引言 上一篇文章中&#xff0c;我们详细地介绍了 CompletableFuture&#xff0c;它是一种强大的并发工具&#xff0c;能帮助我们以声明式的方式处理异步任务。虽然 CompletableFuture 很强大&#xff0c;但它并不总是最适合所有场景的解决方案。 在这篇文章中&#xff0c;我们…...

医学案例|ROC曲线之面积对比

一、案例介绍 为评价CT和CT增强对肝癌的诊断效果&#xff0c;共检查了32例患者&#xff0c;每例患者分别用两种方法检查&#xff0c;由医生盲态按4个等级诊断&#xff0c;最后经手术病理检查确诊其中有16例患有肝癌&#xff0c;评价CT个CT增强对肝癌是有有诊断效果并且试着比较…...

Kotlin线程的基本用法

线程的基本用法 新建一个类继承自Thread&#xff0c;然后重写父类的run()方法 class MyThread : Thread() {override fun run() {// 编写具体的逻辑} }// 使用 MyThread().start()实现Runnable接口 class MyThread : Runnable {override fun run() {// 编写具体的逻辑} }// …...

2.03 PageHelper分页工具

步骤1&#xff1a;在application.yml中添加分页配置 # 分页插件配置 pagehelper:helperDialect: mysqlsupportMethodsArguments: true步骤2&#xff1a;在顶级工程pom文件下引入分页插件依赖 <!--5.PageHelper --> <dependency><groupId>com.github.pagehe…...

VUE中使用ElementUI组件的单选按钮el-radio-button实现第二点击时取消选择的功能

页面样式为&#xff1a; html 代码为&#xff1a; 日志等级&#xff1a; <el-radio-group v-model"logLevel"><el-radio-button label"DEBUG" click.native.prevent"changeLogLevel(DEBUG)">DEBUG</el-radio-button><el-r…...

瓴羊Quick BI:可视化大屏界面设计满足企业个性需求

大数据技术成为现阶段企业缩短与竞争对手之间差距的重要抓手&#xff0c;依托以瓴羊Quick BI为代表的工具开展内部数据处理分析工作&#xff0c;也成为诸多企业持续获取竞争优势的必由之路。早年间国内企业倾向于使用进口BI工具&#xff0c;但随着瓴羊Quick BI等一众国内数据处…...

617. 合并二叉树

题目 题解一&#xff1a;递归 /*** 递归* param root1* param root2* return*/public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {//结束条件if (root1 null) {return root2;} //结束条件if (root2 null) {return root1;}//两节点数值相加TreeNode me…...

【T1】存货成本异常、数量为零金额不为零的处理方法。

【问题描述】 使用T1飞跃专业版的过程中&#xff0c; 由于业务问题或者是操作问题&#xff0c; 经常会遇到某个商品成本异常不准确&#xff0c; 或者是遇到数量为0金额不为0的情况&#xff0c;需要将其成本调为0。 但是T1软件没有出入库调整单&#xff0c;并且结账无法针对数量…...

EtherNet IP转PROFINET网关连接西门子与欧姆龙方法

本文主要介绍了捷米特JM-PN-EIP&#xff08;EtherNet/IP转PROFINET&#xff09;网关西门子200智能PLC&#xff08;PROFINET&#xff09;和欧姆龙系统EtherNet/IP通信的配置过程。 1, 将 EDS 文件复制到欧姆龙软件的对应文件夹下 2, 首先添加捷米特JM-PN-EIP网关的全局变量&…...

低代码开发重要工具:jvs-flow(流程引擎)审批功能配置说明

流程引擎场景介绍 流程引擎基于一组节点与执行界面&#xff0c;通过人机交互的形式自动地执行和协调各个任务和活动。它可以实现任务的分配、协作、路由和跟踪。通过流程引擎&#xff0c;组织能够实现业务流程的优化、标准化和自动化&#xff0c;提高工作效率和质量。 在企业…...

[SQL挖掘机] - GROUP BY语句

介绍: group by 是 sql 中用于对结果集进行分组的关键字。通过使用 group by&#xff0c;可以根据一个或多个列的值将结果集中的行分组&#xff0c;并对每个分组应用某种聚合函数&#xff08;如 count、sum、avg 等&#xff09;以生成汇总信息。这样可以方便地对数据进行分类、…...

【ubuntu|内核】ubuntu 22.04修改内核为指定版本

every blog every motto: You can do more than you think. https://blog.csdn.net/weixin_39190382?typeblog 0. 前言 ubuntu 22.04 安装指定内核 1. 正文 查看已安装的内核镜像 dpkg --get-selections | grep linux-image1.1 安装指定版本的内核 安装镜像 sudo apt-g…...

Carla教程一:动力学模型到LQR

Carla教程一、动力学模型到LQR 从运动学模型和动力学模型到LQR 模型就是可以描述车辆运动规律的模型。车辆建模都是基于自行车模型的设定,也就是将四个轮子抽象为自行车一样的两个轮子来建模。 1、运动学模型 运动学模型是基于几何关系分析出来的,一般适用于低俗情况下,…...

IDE/mingw下动态库(.dll和.a文件)的生成和部署使用(对比MSVC下.dll和.lib)

文章目录 概述问题的产生基于mingw的DLL动态库基于mingw的EXE可执行程序Makefile文件中使用Qt库的\*.a文件mingw下的*.a 文件 和 *.dll 到底谁起作用小插曲 mingw 生成的 \*.a文件到底是什么为啥mingw的dll可用以编译链接过程转换为lib引导文件 概述 本文介绍了 QtCreator mi…...

点击加号添加新的输入框

实现如上图的效果 html部分&#xff1a; <el-form-item class"forminput" v-for"(item,index) in formdata.description" :key"index" :label"描述(index1)" prop"description"><el-input v-model"formdata…...

SQL AND OR 运算符

AND & OR 运算符用于基于一个以上的条件对记录进行过滤。 如果第一个条件和第二个条件都成立&#xff0c;则 AND 运算符显示一条记录。 如果第一个条件和第二个条件中只要有一个成立&#xff0c;则 OR 运算符显示一条记录。 下面是选自 "students" 表的数据&a…...

6、C++内存模型

原文&#xff1a; https://my.oschina.net/u/2516597/blog/805489 背景 C11开始支持多线程&#xff0c;其中提供了原子类型atomic, 和atomic关系比较密切的是memory_order&#xff0c;所有的内存模型都是指atomic类型 enum memory_order {memory_order_relaxed,memory_order…...