声明:
简书内容同步:https://www.jianshu.com/u/90ce902439cc
1.本文章为原创文章,转载注明出处,蟹蟹~
2.初学安卓,水平有限,还有很多不足和应当修正的地方,欢迎评论指点
先来最终效果图:
大二的java课程快结束前,想着自己用课余时间学习下安卓,于是不久前买了一本安卓开发的入门书(本人用的书是《第一行代码》,听说《疯狂安卓讲义》也不错)。在写完计算器时,java课程已结课了,时光飞逝吧。
刚好这时学院有个团队招新,发现是有安卓部分的,于是就去了解了一下,毕竟也是一个难得的机会,错过了不知道下次要等多久,团队面试对我这届安卓部分招新难度不算高,做一个基于安卓的简单计算器,要求有复合运算(括号),小数点,加减乘除功能即可。可能是因为大二上学校还没教安卓吧,要求不是很高,主要看兴趣和态度以及一点自学能力吧。
以下是具体要求:
在开始写之前我事先看了一下班上王同学用java自带的awt和swing做的用于整数运算的计算器,是一步一步算的那种。这是王同学的计算器简书链接
下面稍微详细的介绍一下具体实现步骤:
##视图部分(.xml)
最终效果就是文章最上边那样,当然一开始没有那些计算的式子。
由于计算器按钮的排布很容易就让我想到了javaGUI里的网格布局,虽然买的这本书上只讲了常用的几种布局,所以百度了一下安卓视图里的有没有网格布局,结果当然是有的。
做了一个小总结:
网格布局中除了一开始要设置layout_width,layout_height属性外,还要设置网格行数列数:rowCount,columnCount。这里我计算器按钮是5行*4列网格。
具体到网格布局里面则是按钮,我这里:
android:layout_columnWeight="1"
android:layout_rowWeight="1"
就是设置按钮在行和列的权重,我每个按钮都设置成1,所以最后每行每列都平均了,刚好是4*5网格。
<GridLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:rowCount="5"
android:columnCount="4"
>
<Button
android:text="("
android:id="@+id/buttonleft"
android:layout_columnWeight="1"
android:layout_rowWeight="1"
android:textSize="30dp"
android:textColor="#CD853F"
android:background="#FFDAB9"/>
//...
//其他按钮同理,省略
//...
GridLayout>
##逻辑部分(.java)
该计算器有20个按钮,功能各不相同,为每个按钮单独写监听器不现实,于是在一个监听器里面通过获取被按的按钮的ID新建一个暂时的按钮引用变量tempButton,通过获取tempButton上的text信息来判断是哪一个按钮被按了,然后再做相应的操作。
以下是实现View.OnClickListener接口重写onClick方法,当然用匿名内部类的方式也ok。
public void onClick(View v){
Button tempButton =findViewById(v.getId());
String tempString=(String )tempButton.getText();
swich(tempString){
//...
//省略
//...
}
//...
//省略
//...
}
其他按钮的功能实现较为简单,其中重点是等于号按钮功能的实现,即表达式求值。
这里由于代码量相对较多,于是专门写了个方法去计算,参数即String类型的运表达式str。获取了表达式后首先将运算符和运算数分开。由于需要支持小数点运算,所以后边操作数都转换成double类型了。
首先从String类型上将运算符运算数分离,之后再转换类型,这里分离的方法也不唯一,这只是我个人的想法,相信肯定会有更加简便的方法。
//str为表达式,strBuffernum存运算数(以逗号隔开),strBuffersign存运算符
StringBuffer strBuffernum=new StringBuffer() ;
StringBuffer strBuffersign=new StringBuffer() ;
//将运算式分开存入中, temp标记当前操作数起始下标
for ( int temp=0,i=0; i<str.length(); i++) {
String ele = "" + str.charAt(i);
System.out.print(ele);
if (ele.equals("+")||ele.equals("-")||ele.equals("*")||ele.equals("/")||ele.equals("=")||ele.equals("(")||ele.equals(")"))
{
strBuffernum.append(str.substring(temp, i));
if(ele.equals("=")==false&&ele.equals("(")==false) {
strBuffernum.append(",");
}
temp=i+1;
strBuffersign.append(ele);
}
}
String strsign =strBuffersign.toString();
String numString= strBuffernum.toString();
numString=numString.replace(",,", ",");//去除中间空string
其中
numString=numString.replace(",,", ",");//去除中间空string
可能会有疑问,为什么只需去掉中间的空字符串,如果第一个就是“,”那转换后还会剩一个“,”,其实这个问题在前面已经考虑,即如果以“(”开始则strBuffernum中不加“,”,结果就是遇到左括号时strBuffernum不会加任何字符串。
if(ele.equals("=")==false&&ele.equals("(")==false) {
strBuffernum.append(",");
}
如果将分离后的两字符串输出则是以下效果:
为了方便计算,之后将运算数的字符串转换类型时则统一转换成double类型,让后存到ArrayList数组对象中。
String numString= strBuffernum.toString();
numString=numString.replace(",,", ",");//去除中间空string
String[] numStrArr=numString.split(",");
Double[] numArr = new Double[numStrArr.length];
for(int i=0;i<numStrArr.length;i++)
{ numArr[i]=Double.valueOf(numStrArr[i]); }
List<Double> numl = new ArrayList();
for (int i = 0; i < numArr.length; i++) {
if (!"".equals(numArr[i])) {
numl.add(numArr[i]);
}
}
这样准备工作就完成了,之后就利用分离好的运算符字符串strsign,和ArrayList数组对象numl即可。
这里用到双栈运算式求值的方法,网上也有很多类似的解法,我这个解法仅供参考,解法并不唯一。之前看过算法第四版这本书是有这个解法解析的,但那个算法局限性比较高,对表达式的形式有一定要求,没有省略任何括号, 比如平常的1+2×3要求为:(1+(2×3)) 。也想过怎么把一般式子改写成标准式,后来还是直接对已有式子求值了,想法很相似。
我会在我的下一篇文章中分别用栈和链表解一遍,也算是复习一下数据结构吧,其实基本思路是完全一样的,只是换一种存数和存符的数据类型而已。