APP介绍
计算器app,一款可以运行在安卓系统上的计算器,可以进行加减乘除运算。除了数字按键、加、减、乘、除、等于等按键之外,还有三个功能按键清零C
,后退DEL
,正负数切换+/-
。本博客从界面设计、代码实现和发布应用三个部分来进行总结计算器app的制作过程和发布时需要注意的问题,尽量做到条理清晰、结构明了。建议使用电脑查看本博客,可以看到侧栏的大纲,方便快速跳转回顾一些细节。项目源码放在最后。
预备知识:Android线性布局、TextView控件、Button控件、JAVA基础知识。(Android先导知识可以看这里:传送门)
界面设计
页面布局从外到内:
Activity
Activity(可以理解为界面)采用线性布局,内部组件对齐方式为垂直方向靠到底部、水平方向居中。
大LinearL…
一个大的LinearLayout标签,包裹全部的组件。
整个页面全部的组件均被一个大的LinearLayout标签包裹(上图蓝色框),用于定位全部的组件(计算器主体部分)。宽高由内容撑开,子控件的存放方式为垂直方向。
显示框
两个显示框、按钮组件三部分为平级关系,放置在大的LinearLayout标签(上上图蓝色框)中。
第一个显示框,用于显示计算步骤。id名为tv1,宽度占满父级容器,高度由内容撑开。文本对齐方式为垂直方向靠到底部、水平方向靠到右边。字体大小为35sp,字体颜色为#d5d5d5(灰色),最多显示4行。设置左右内边距为58dp,顶部外边距为20dp。第一个显示框组件的高度由显示内容决定,所以前边设置全部组件都靠到底部,可以避免不同机型屏幕尺寸不一致导致的显示不全问题。
第二个显示框,用于显示计算的结果。id名为tv2,宽度占满父级容器,高度80dp。文本对齐方式为垂直方向靠到底部、水平方向靠到右边。字体大小为60sp,字体颜色为#000000(黑色),最多显示1行。设置左右内边距为58dp。没有内容显示时显示数字“0”。
按钮
计算器按键部分。每一行的按钮(四个)都用一个LinearLayout标签包裹,5行按钮再包裹在一个LinearLayout标签内。大的LinearLayout标签,宽高由内容撑开,子控件的存放方式为垂直方向。内部组件对齐方式为水平方向居中。设置底部外边距为30dp,顶部外边距为10dp。
小的LinearLayout标签(包裹一行按钮),宽高由内容撑开,内部组件对齐方式为垂直方向靠到顶部、水平方向居中。
按钮,宽高均为66dp,外边距8dp,文本居中显示,字体大小为30sp。
代码实现
导入资源
在进行界面的搭建之前先把需要用到的资源导入进来,控件的样式跟属性、图片、图标等等。
Styles
控件的样式跟属性等具体值建议添加到模块/res/values/styles.xml
文件中,在layout布局文件中通过style="@style/定义的style名"
调用。这样可以让layout布局文件中的代码变得更加简洁,比较容易掌握页面的整体布局。
根据前边界面设计中的描述,我们将它们用代码形式表示出来再添加到styles.xml
文件中。
caltv1
应用在第一个显示框上,caltv2
应用在第二个显示框上,calLinearBig
应用在包裹全部按钮的大LinearLayout
标签上,calLinear
应用在包裹每一行按钮的LinearLayout
标签上,calBtn
应用在按钮上。
从上到下介绍一下各个属性的含义:
layout_height -- wrap_content
:高度 – 由内容撑开layout_width -- match_parent
:宽度 – 占满父容器gravity -- right|bottom
:子控件对齐方式 – 水平方向靠右,垂直方向靠底paddingRight -- 58dp
:右侧内边距 – 58dplayout_marginTop -- 20dp
:顶部外边距 –20dptextSize -- 35sp
:文本(字体)大小 – 35sptextColor -- #d5d5d5
:文本颜色 – 灰色maxLines -- 4
:最多行数 – 4行hint -- 0
:当没有内容时显示的(提示)文本 – 显示数字0orientation -- vertical
:子控件的存放方式(线性布局) – 垂直方向background -- @drawable/calbtn
:背景 – 图片calbtn
1 |
<style name="caltv1"> |
colors
在模块/res/values/colors.xml
文件中定义颜色值,调用时使用@color/定义的color名
。下边是一些常见的color属性:
colorPrimary
:Appbar背景色colorPrimaryDark
:状态栏颜色colorAccent
:控件被选中时的颜色windowBackground
:页面背景色navigationBarColor
:底部导航栏颜色textColorPrimary
:ToolBar上的Title颜色colorControlNormal
:各个控制控件的默认颜色
在这个项目中我只修改了colorPrimaryDark
的值,修改为#00FFFFFF
,前两个数字表示透明度值。0-->255
,透明–>不透明。自定义一个color属性,名为calC
,值为#fb3654
,应用在第一行红色按钮的文本上。
strings
在模块/res/values/strings.xml
文件中定义字符串值,调用时使用@string/定义的string名
。这里将app_name值修改为“计算器”,即应用程序的名字。
drawable
应用程序需要用到的图片、图标等资源统一放到模块/res/drawable
文件夹中。
修改程序图标
修改应用程序图标在模块/manifests/AndroidManifest.xml
文件中进行。icon
属性即为应用程序图标。roundIcon
属性为应用程序圆角图标。label
属性为应用程序名称。(从安全性方面考虑,建议将allowBackup
属性值修改为false
)
修改程序名
在模块/res/values/strings.xml
文件中修改应用程序的名称,name
为app_name
的string标签。
去掉appbar

为了实现全屏显示计算器界面,我们需要去掉计算器界面中的appbar。将模块/res/values/styles.xml
文件中名为AppTheme
的style标签的parent
属性值修改为Theme.AppCompat.Light.NoActionBar
。
去掉状态栏
去掉状态栏,让计算器app全屏显示。在模块/res/values/styles.xml
文件的AppTheme中添加以下语句:
1 |
<item name="android:windowNoTitle">true</item> |
或自定义一个Style,但需要到模块/manifests/AndroidManifest.xml
文件中应用到theme:
搭建界面
为了能够更好的理解各个部分的位置、整体的结构,需要配合前边的界面设计部分的说明。(可借助侧栏的大纲快速跳转)
Activity
Activity(可以理解为界面)采用线性布局,子控件对齐方式为垂直方向靠到底部、水平方向居中。
打开主界面的layout布局文件activity_main.xml
,将最外层的标签改为LinearLayout
。添加属性gravity
,值为center|bottom
。添加属性layout_width与layout_height
,值为match_parent
。
1 |
|
大LinearL…
一个大的LinearLayout标签,包裹全部的组件。
整个页面全部的组件均被一个大的LinearLayout标签包裹,用于定位全部的组件(计算器主体部分)。宽高由内容撑开,子控件的存放方式为垂直方向。
后边的两个显示框、按钮组件三部分为平级关系,放置在这个大的LinearLayout标签中。
1 |
<LinearLayout |
显示框
第一个显示框,用于显示计算步骤。id名为tv1,应用名为caltv1的style样式。
1 |
<!-- 第一个显示框--> |
第二个显示框,用于显示计算的结果。id名为tv2,应用名为caltv2的style样式。
1 |
<!-- 第一个显示框--> |
计算器按键部分。每一行的按钮(四个)都用一个LinearLayout标签包裹,5行按钮再包裹在一个LinearLayout标签内。大的LinearLayout标签应用名为calLinearBig的style样式。
1 |
<!-- 按键部分--> |
两个显示框、按钮组件三部分为平级关系。
按键
每一行的按钮(四个)都用一个LinearLayout标签包裹。四行按钮,所以使用四个LinearLayout标签,每个标签都应用名为calLinear的style样式。
1 |
<!--第一排按键--> |
第一行按键
第一行的按键有:清零、后退、正负切换、等于。
按键“清零”,id为calBtnc,按键显示的文本为“C”,应用名为calBtn的style样式。特有样式:文本颜色为@color/calC(红色)、背景图片为@drawable/calbtnc(圆角淡红色)。

1 |
<Button |
按键“后退”,id为calBtnDel,按键显示的文本为“DEL”,应用名为calBtn的style样式。特有样式:文本大小为25sp、文本颜色为@color/calC(红色)、背景图片为@drawable/calbtnc(圆角淡红色)。

1 |
<Button |
按键“正负切换”,id为calBtnChange,按键显示的文本为“+/-”,应用名为calBtn的style样式。特有样式:文本颜色为@color/calC(红色)、背景图片为@drawable/calbtnc(圆角淡红色)。

1 |
<Button |
按键“等于”,id为calBtnequal,按键显示的文本为“=”,应用名为calBtn的style样式。特有样式:文本颜色为#fff(白色)、背景图片为@drawable/calbtnequal(圆角淡绿色)。

1 |
<Button |
第一行按键完整代码:
1 |
<!-- 第一排按键 --> |
第二行按键

第二行的按键有:数字7、数字8、数字9、加。
按键“数字7”,id为calBtn7,按键显示的文本为“7”,应用名为calBtn的style样式。
按键“数字8”,id为calBtn8,按键显示的文本为“8”,应用名为calBtn的style样式。
按键“数字9,id为calBtn9,按键显示的文本为“9”,应用名为calBtn的style样式。
按键“加”,id为calBtnPlus,按键显示的文本为“+”,应用名为calBtn的style样式。特有样式:文本颜色为#fff(白色)、背景图片为@drawable/calbtnfu(圆角淡橙色)。
1 |
<!--第二排按键--> |
第三行按键

第二行的按键有:数字4、数字5、数字6、减。
按键“数字4”,id为calBtn4,按键显示的文本为“4”,应用名为calBtn的style样式。
按键“数字5”,id为calBtn5,按键显示的文本为“5”,应用名为calBtn的style样式。
按键“数字6,id为calBtn6,按键显示的文本为“6”,应用名为calBtn的style样式。
按键“减”,id为calBtn_,按键显示的文本为“-”,应用名为calBtn的style样式。特有样式:文本颜色为#fff(白色)、背景图片为@drawable/calbtnfu(圆角淡橙色)。
1 |
<!--第三排按键--> |
第四行按键

第二行的按键有:数字1、数字2、数字3、乘。
按键“数字1”,id为calBtn1,按键显示的文本为“1”,应用名为calBtn的style样式。
按键“数字2”,id为calBtn2,按键显示的文本为“2”,应用名为calBtn的style样式。
按键“数字3,id为calBtn3,按键显示的文本为“3”,应用名为calBtn的style样式。
按键“乘”,id为calBtnX,按键显示的文本为“*”,应用名为calBtn的style样式。特有样式:文本颜色为#fff(白色)、背景图片为@drawable/calbtnfu(圆角淡橙色)。
1 |
<!--第四排按键--> |
第五行按键

第二行的按键有:数字0、小数点、除。
按键“数字0”,id为calBtn0,按键显示的文本为“0”,应用名为calBtn的style样式。特有样式:按键宽度为150dp、背景图片为@drawable/calbtnbig。
按键“小数点”,id为calBtnDot,按键显示的文本为“.”,应用名为calBtn的style样式。
按键“除”,id为calBtnExcept,按键显示的文本为“/”,应用名为calBtn的style样式。特有样式:文本颜色为#fff(白色)、背景图片为@drawable/calbtnfu(圆角淡橙色)。
1 |
<!--第五排按键--> |
至此计算器app的界面搭建已经完成了,下边是功能实现部分。使用java语言为按键添加点击震动效果和实现计算功能。
功能实现
需要实现的功能有:按键点击震动效果、计算功能。
添加事件监听
实现这两个功能都需要给每个按键添加事件监听,当按键被按下时完成一些事情。这里给按键添加点击事件监听的方式是实现接口View.OnClickListener
。实现接口View.OnClickListener
需要重写方法onClick,当按键被点击时会执行该方法。
OnCreate是Android中的一个特别的函数,用来“表示一个窗口正在生成”。其不产生窗口,只是在窗口显示前设置窗口的属性如风格、位置颜色等。详情见:百度百科。
在方法onCreate中,通过语句findViewById(按键id).setOnClickListener(this);
为每个按键添加事件监听器。按键id在搭建界面部分都有给出,可通过侧栏快速跳转进行对照。
1 |
findViewById(R.id.calBtnc).setOnClickListener(this);//清零 |
实现震动效果
给按键添加点击震动效果。实现此效果只需要在方法onClick(View v)
中添加该语句:
1 |
v.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING); |
实现计算功能
一些变量
我使用了一些变量作为标志位,标志是否输入了第一个参与运算的数,是否已经存在了小数点等等。除此之外还使用了变量tv1、tv2用于操作两个显示框中的数据。
1 |
private TextView tv1;//第一个显示框 |
1 |
tv1 = (TextView)findViewById(R.id.tv1); |
isFirst
:是否存在第一个操作数isSecond
:是否存在第二个操作数isDot
:是否存在小数点lastOprator
:记录上一个操作符firstNum
:第一个数,按下运算符加减乘除后将secondNum的值保存在其中(不包括运算符等于)secondNum
:第二个数,接收输入在第二个显示框中数据
计算逻辑
让我们来想象一下计算器的工作流程是怎样。首先,通过按键输入第一个要参与计算的数,然后输入运算符(加减乘除),再之后输入第二个要参与计算的数,在这之后可能还会继续输入第三个要参与计算的数(即输入运算符后输入第三个数),在这个过程中可以使用”后退“按键删除某个输错的数或使用“清零”按键推倒重来,最后按下“等于”按键计算出结果。
按下数字按键后,数据显示在第二个显示框中,并且将数据保存在一个变量中(例如:secondNum。主要用于接收输入到第二个显示框中的数据)。第一个要参与计算的数输入完了按下运算键(加减乘除)后将一直在接收数据的变量(例如:secondNum)的数据保存到另一个变量中(例如:firstNum)再将其归0(例如:firstNum
= secondNum;secondNum = 0)。把我都快绕晕了,看下边的流程图吧。
在方法onClick中(前边给每个按键都添加了点击事件监听器,当按键被点击时执行该方法),使用switch-case结构,通过判断触发事件的对象的id来区别不同的按键,为各按键添加相应的处理规则。例如下图:

1 |
Button btn = (Button)v; |
数字按键
数字按键可以使用同一套规则:
1 |
if (!isFirst){ |
这里的btn是触发点击事件的按键,见下图51行:
后退

1 |
if (!tv2.getText().toString().equals("")){ |
正负切换

1 |
|
其他按键
按键加、减、乘、除、等于、小数点触发后执行的规则抽取出来放到了方法opration中。
1 |
Button btn = (Button)v; |
方法opration,对应上边流程图中的红色框部分。这部分结合上边的流程图很容易理解。
1 |
public void opration(Button btn){//当前操作符oprator |
至此功能实现部分也完成了,使用数据线连接手机即可进行测试。(不会使用手机调试可以看看这里:传送门)
发布
一般下载应用程序我们都是通过应用商店来下载的,所以我们的app开发好之后需要将它发布到应用商店去才能让它发光发热😆。
设置版本号
修改应用程序的版本号,需要到模块/build.gradle
文件中进行修改。(defaultConfig
)
需要查看包名 ,可以到java代码中去查看。例如上边“功能实现”部分最后一张图,第一行代码就是。
1 |
package com.qsdbl.calculator; |
构建应用
构建应用即将我们制作好的计算器app打包成apk文件,这样才能在手机上进行安装。
点击菜单栏上的“Build”,选择“Generate Signed Bundle/APK”,选择app,点击next下一步,选择你的密钥文件。
选择你的密钥文件或点击“Create new”创建一个。创建密钥文件比较简单,可以百度一下。(几点建议:最好是设置相同密码。别名设置简单一点,例如:key。记住密钥保存位置,下边有用)
选择release,发行版本。下边的Signature Versions(签名版本)建议两个都勾选上,一些应用商店审核比较严。
点击Finish(完成)即可开始构建签名版本apk。apk文件生成成功后,AS的右下角会有提示,点击“locate”即可打开存放apk文件的文件夹。(使用命令对apk文件进行重新签名,见下边)
上传应用
应用加固
在上传应用之前,我们还需要对应用进行加固。对于应用加固,网上的一些介绍:
防止开发的App被恶意篡改,刚投入市场就出现各类盗版
防止通过反编译获取App源代码、创意,保护软件开发专者的核心技术不被窃取
防止App被替换各类资源文件、信息内容
增加App反调试保护,防止App运行过程中通属过注入代码窃取用户账号信息、修改内存数据等行为
通过加密App运行过程中产生的数据,避免中间数据被窃取和修改
应用加固还有一个好处,能够提高应用通过应用商店审核的几率。腾讯云可以免费进行应用加固和安全检测,应用加固访问这里:传送门。
应用加固后需要重新签名,可以使用下边的命令进行重签名:
1 |
jarsigner -verbose -keystore 密钥保存路径 -signedjar 重签名后的保存路径 待签名的应用 密钥别名 |

应用加固后可以使用安全测评对应用进行初步的安全测评,若有问题及时修复,提高应用通过应用商店审核的几率。
最后的准备
应用程序通过了腾讯云的安全测评,就可以发布到应用商店了,但是在这之前我们还需要去注册一个开发者账号。应用商店有很多,基本每个手机厂商都会有自己的应用商店也有一些第三方的应用商店,例如豌豆荚、应用宝、酷安等等。
以小米应用商店为例,访问该网站:小米开发者平台,注册开发者账号,然后点击下图的“上传应用”上传应用即可。流程很简单,填写应用程序的基本信息、应用截图等等。应用包名可以在应用安全加固或安全测评中看到或者打开AS,java代码文件的第一行就是。
项目源码:https://cloud.189.cn/t/rYfQNrmmy6Zn(访问码:1tpc)
计算器app:https://cloud.189.cn/t/RF7NJf6zIVr2 (访问码:3l1s)