制作一个简易的计算器app

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/7764.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

机器学习 day31(baseline)

语音识别的Jtrain、Jcv和人工误差 对于逻辑回归问题&#xff0c;Jtrain和Jcv可以用分类错误的比例&#xff0c;这一方式来代替单单只看Jtrain&#xff0c;不好区分是否高偏差。可以再计算人类识别误差&#xff0c;即人工误差&#xff0c;作为基准线Jtrain与baseline对比只高了…

鸿合一体机怎么系统还原

系统还原方法&#xff1a;先用牙签按住电脑还原按钮不松&#xff0c;然后按一下电源按钮开机&#xff0c;直到电脑进入系统还原画面&#xff0c;就可以松开还原按钮了&#xff0c;系统还原完成后会自动重启&#xff0c;重启完成就可以用了。

YOLOv7改进:CVPR 2023 | SCConv: 即插即用的空间和通道重建卷积

1.该文章属于YOLOV5/YOLOV7/YOLOV8改进专栏,包含大量的改进方式,主要以2023年的最新文章和2022年的文章提出改进方式。 2.提供更加详细的改进方法,如将注意力机制添加到网络的不同位置,便于做实验,也可以当做论文的创新点。 2.涨点效果:添加 SCConv,经过测试,有效涨点。…

UE5 用DLL文件制作第三方插件

本篇博文介绍了&#xff0c;如果在UE 中如何使用第三方库&#xff0c;及制作成插件的方法。 DLL 文件是上篇文章中创键的具体的方法见上篇文章。下面开始介绍方法 首先&#xff0c;创建一个空白的 UE5 C 项目&#xff0c;然后再创建一个空白内容的插件&#xff0c;如下图所示 …

基于VUE3+Layui从头搭建通用后台管理系统(前端篇)六:后台主页功能实现下

一、本章内容 接上一章,继续实现后端主页内容,主要实现工具栏对应相关内容的实现,包括系统消息、系统公告、全屏切换、语言切换、主题切换等。 1. 详细课程地址: 待发布 2. 源码下载地址: 待发布 二、界面预览 三、开发视频 基于VUE3+Layui从头搭建通用后台管理系统合集…

微服务学习笔记-----Nacos安装教程(Windows和Linux版本)

Nacos安装教程 Nacos安装指南1.Windows安装1.1.下载安装包1.2.解压1.3.端口配置1.4.启动1.5.访问 2.Linux安装2.1.安装JDK2.2.上传安装包2.3.解压2.4.端口配置2.5.启动 3.Nacos的依赖 Nacos安装指南 1.Windows安装 开发阶段采用单机安装即可。 1.1.下载安装包 在Nacos的Git…

ClickHouse(一):ClickHouse介绍及OLAP场景特征

目录 1. ClickHouse与其特性 ​​​​​​​2. 什么是ClickHouse ​​​​​​​3. OLAP场景的特征 进入正文前&#xff0c;感谢宝子们订阅专题、点赞、评论、收藏&#xff01;关注IT贫道&#xff0c;获取高质量博客内容&#xff01; ​​​​​​​1. ClickHouse与其特性 …

Spring Cloud Eureka 服务发现速度慢配置优化总结

文章目录 一、 Eureka-server 服务端缓存问题1.1 服务端缓存1.2 客户端从服务端获取实例数据的过程1.3 优化 二、客户端 Eureka-client 缓存导致2.1 Eureka客户端和服务端交互缓存2.2 Ribbon 缓存了EurekaClient的缓存2.3 优化 使用Eureka时&#xff0c;常常会发现服务发现慢&a…

8款常用系统镜像烧录软件

系统烧录软件是一种用于将操作系统或其他软件程序安装到嵌入式系统、嵌入式设备或存储设备中的工具。它通常用于将预先编译好的二进制文件或源代码烧录到硬件设备的非易失性存储器中&#xff0c;例如闪存芯片、EEPROM、EPROM或其他存储介质。系统烧录软件提供了一个便捷的方式&…

基于深度神经网络的肺炎检测系统实现

一、说在前面 使用AI进行新冠肺炎图像诊断可以加快病例的诊断速度&#xff0c;提高诊断的准确性&#xff0c;并在大规模筛查中发挥重要作用&#xff0c;从而更好地控制和管理这一流行病。然而&#xff0c;需要强调的是&#xff0c;AI技术仅作为辅助手段&#xff0c;最终的诊断决…

Unity Shader 消融效果

消融效果 消融广泛运用于各种场合&#xff0c;例如 怪物击杀与道具时区&#xff08;原神&#xff09;燃烧 注意&#xff1a;根据噪声图的不同&#xff0c;效果有很大的差异&#xff0c; 第一步&#xff1a;创建着色器 我们要选择Unlit Shader创建 然后定义如下属性 Prop…

Spring MVC 是什么?

一、什么是 Spring MVC&#xff1f; 官方对于 Spring MVC 的描述是这样的&#xff1a; Spring Web MVC is the original web framework built on the Servlet API and has been included in the Spring Framework from the very beginning. The formal name, “Spring Web …

【教程】查看CPU、GPU架构的拓扑结构和系统信息

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhang.cn] 一些概念 Package&#xff1a;处理器封装。在多处理器系统中&#xff0c;每个物理 CPU 芯片通常被封装在一个单独的封装中&#xff0c;这个封装被称为 Package。一个 Package 可以包含一个或多个物理 CPU 核心。…

一站式解决方案:Qt 跨平台开发灵活可靠

Qt 是一种跨平台开发工具&#xff0c;为开发者提供了一站式解决方案。无论您的项目目标是 Windows、Linux、macOS、嵌入式系统还是移动平台&#xff0c;Qt 都能胜任。这种跨平台的特性不仅节省开支&#xff0c;还推动了战略的快速落地。 适用范围广泛&#xff1a;Qt 可在多种操…

ArgoCD结合Gitlab交付项目到kubernetes集群

ArgoCD结合Gitlab交付项目到kubernetes集群 作者:行癫(盗版必究) 一:环境准备 1.kubernetes集群环境 2.HA_Argocd环境 3.Gitlab集群环境 二:项目配置 1.配置Gitlab 创建仓库,并写入yaml文件,利用yaml构建application;此案例结合了NFS实现持久化存储

平板用的触控笔什么牌子好?ipad第三方电容笔推荐

随着技术的发展&#xff0c;出现了各种各样的平板电容笔。一支好的电容笔&#xff0c;不但可以极大地提升我们的工作效率&#xff0c;还可以极大地提升我们的学习效果。平替的电容笔&#xff0c;无论是在技术方面&#xff0c;还是在质量方面&#xff0c;都还有很大的提升空间&a…

【阿里云试用计划】免费试用GPU

写在前面&#xff1a;本博客仅作记录学习之用&#xff0c;部分图片来自网络&#xff0c;如需引用请注明出处&#xff0c;同时如有侵犯您的权益&#xff0c;请联系删除&#xff01; 文章目录 前言试用步骤问题No CUDA GPUs are available无故被killed 致谢 前言 算力在深度学习…

Nodejs 安装之后cmd 输入npm -v 提示error的问题解决

1.问题现象&#xff1a; 安装时候选择&#xff1a; 2. 解决问题 卸载nodejs 删除安装路径下的node_modules, 重新安装 按照下面的选择

上海科技大学智能生活组齐聚合合信息,“沉浸式”体验人工智能产品

近期&#xff0c;上海科技大学组织本科生产业实践-校企联合人才培养活动&#xff0c;30余名学生组成的“智能生活组”实地参访人工智能及大数据科技企业上海合合信息科技股份有限公司&#xff08;简称“合合信息”&#xff09;。本次活动旨在通过项目体验、主题交流&#xff0c…

uniapp 即时通讯开发流程详解

今天我将为您详细介绍UniApp开发中的即时通讯流程。本文将向您展示如何在UniApp中实现即时通讯功能&#xff0c;为您的应用程序增添交互性和实时性。 1. 准备工作 在开始开发之前&#xff0c;确保您已完成以下准备工作&#xff1a; 确保您已经安装好UniApp开发环境&#xff…