1、Ui开发
笔者的Ui制作的制作的比较麻烦仅供参考,在这里使用了多个LinearLayout对屏幕进行了划分。不建议大家这样做最好使用GridLayout会更加快捷简单
笔者大致划分是这样的:
使用了四个大框,在第四个大框里面有多个小框
最终界面如下:
然后在每一个小框写计算机的按键。
代码如下:
<?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/>
android:shadowColor="#80000000"
:表示为该视图的文本添加阴影效果,阴影颜色为半透明黑色(#80000000)。android:shadowDx="2"
:表示阴影在水平方向上的偏移量为2个像素。android:shadowDy="2"
:表示阴影在垂直方向上的偏移量为2个像素。android:shadowRadius="4"
:表示阴影的模糊半径为4个像素。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
指定图形,常用参数有:
"rectangle"
: 表示创建一个矩形的形状。"oval"
: 表示创建一个椭圆的形状,也就是圆形。"line"
: 表示创建一条线段的形状。"ring"
: 表示创建一个圆环的形状。"line"
: 表示创建一条线段的形状。
solid:用于填充颜色
**size **:用于指定大小,因为是圆形我们这里长宽都要设置为相同
效果如下:
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_NULL
,EditText
将不再响应输入法的输入,用户无法通过软键盘输入文本内容。这样做的目的通常是为了限制用户只能通过其他途径(比如扫码、点击按钮等)来输入文本内容,而不能直接通过键盘输入。如何我们为每个按钮注册监听事件
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_animation
是animator
下设置的动画属性
具体设置如下:
<?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>
android:propertyName
: 定义要动画化的属性的名称。在这里,scaleX
和scaleY
分别表示水平和垂直方向的缩放。android:valueFrom
: 定义动画的起始值。这里起始值为 1.0,表示不缩放。android:valueTo
: 定义动画的结束值。这里结束值为 0.9,表示缩放到原始大小的 0.9 倍。android:duration
: 定义动画的持续时间,以毫秒为单位。这里持续时间为 100 毫秒,即缩放动画的过程为 0.1 秒。android:repeatCount
: 定义动画重复的次数。这里设置为 0,表示动画不重复。android:repeatMode
: 定义动画重复的模式。这里设置为restart
,表示每次重复动画都从头开始。android:interpolator
: 定义动画的插值器,用于控制动画的变化速率。这里使用了 Android 系统自带的accelerate_decelerate_interpolator
插值器,表示动画先加速后减速。
这个 XML 文件定义了两个属性动画,分别是在水平方向和垂直方向上的缩放动画效果。在 Android 应用中,你可以通过加载这个 XML 文件来应用这个缩放动画,让指定的视图(如 View
或 ImageView
)在缩放动画的作用下,由原始大小缩放到 0.9 倍的大小。
制作动画时,创建了一个 scaleAnimation
缩放动画,然后给该动画添加了一个监听器 AnimatorListenerAdapter
。在监听器的 onAnimationEnd()
方法中,当缩放动画结束时,会执行还原动画。
大致逻辑:
- 创建缩放动画
scaleAnimation
并设置属性动画的相关参数。 - 给
scaleAnimation
添加一个监听器,在缩放动画结束时执行还原动画。 - 创建两个
ObjectAnimator
分别对view
的scaleX
和scaleY
属性进行动画处理,将view
的缩放比例恢复为原始大小(1.0f)。 - 将还原动画
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加载属性动画。
this
: 这里的this
指的是当前的上下文,即所在的Activity
。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>
<set>
:这是属性动画的根元素,用于定义属性动画集合。在这里,我们只有一个动画,所以只有一个<objectAnimator>
子元素。<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
)在以下方法中被抛出:
-
com.example.computermoblie.ReversePolish.InfixToSuffix(ReversePolish.java:45)
-
com.example.computermoblie.ReversePolish.EvaluatePostfixExpressions(ReversePolish.java:69)
com.example.computermoblie.MainActivity.onClick(MainActivity.java:189)
com.example.computermoblie.MainActivity.DynamicCalculation(MainActivity.java:872)