实现一个计算器 ,有加减乘除功能,小数点和清除操作。
这是学校安卓老师布置的作业,计算器说实话实现起来挺多坑的,之前在算法比赛中见过这种题,用来熟悉安卓的布局的确是挺好的一个小案例,不过需要挺多逻辑处理!
下面本人的实现思路:
- 布局方面:使用网格GridLayout布局,除了显示数据的textView,其他都是button,使用xml设置好,添加到res。
- 事件方面:通过findViewBy在R.id.xxx,拿到上面添加到resource库里面的控件,创建对象并添加对应的点击处理方法。这里我是把所有按钮都放在一个click类处理,通过switch对比传进来的view(即button)是哪个id,做出不同的点击方法。
下面说说计算器实现要考虑的点
- 1+1 =2 这时候2可以继续加上去或其他符号操作
- 1 + * -连续点几个操作符,只能筛选出最后一个操作符,并且可以继续计算操作
- 1 + 然后按等号,应该变成2,1 - 然后按等号应该变成0,乘除也一样。
- 做除法运算的时候,分母不能为0,否则输出error,window和ios计算器都会提示
- 小数点的处理
下面先上XML布局代码 ,挺多重复东西的,可以直接拉下去。
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout 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"
tools:context=".MainActivity">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/AppTheme.PopupOverlay"
/>
</android.support.design.widget.AppBarLayout>
<include layout="@layout/content_main" />
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="@dimen/fab_margin"
app:srcCompat="@android:drawable/ic_dialog_email" />
//下面才是重点
<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_marginLeft="64dp"
android:layout_marginTop="200dp"
android:id="@+id/GridLayout1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:columnCount="4"
android:orientation="horizontal"
android:rowCount="6" >
<TextView
android:id="@+id/resultText"
android:layout_columnSpan="4"
android:layout_gravity="fill"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:background="@android:color/holo_blue_bright"
android:textColor="@android:color/white"
android:textFontWeight="0.5"
android:text="0"
android:textSize="50sp" />
<Button
android:id = "@+id/buttonBack"
android:layout_columnSpan="2"
android:layout_gravity="fill"
android:text="回退" />
<Button
android:id = "@+id/buttonClear"
android:layout_columnSpan="2"
android:layout_gravity="fill"
android:text="清空" />
<Button
android:id = "@+id/plus"
android:text="+" />
<Button
android:id = "@+id/one"
android:text="1" />
<Button
android:id = "@+id/two"
android:text="2" />
<Button android:id = "@+id/three"
android:text="3" />
<Button
android:id = "@+id/subtract"
android:text="-" />
<Button
android:id = "@+id/four"
android:text="4" />
<Button
android:id = "@+id/five"
android:text="5" />
<Button
android:id = "@+id/six"
android:text="6" />
<Button
android:id = "@+id/multiply"
android:text="*" />
<Button
android:id = "@+id/seven"
android:text="7" />
<Button
android:id = "@+id/eight"
android:text="8" />
<Button
android:id = "@+id/nine"
android:text="9" />
<Button
android:id = "@+id/divide"
android:text="/" />
<Button
android:id="@+id/point"
android:layout_width="wrap_content"
android:text="." />
<Button
android:id = "@+id/zero"
android:text="0" />
<Button
android:id = "@+id/done"
android:text="=" />
</GridLayout>
</android.support.design.widget.CoordinatorLayout>
可以看到我设置的并没有填充满整个屏幕,只是放在了屏幕中间,以后再完成全屏操作。
下面是.java的代码
package com.example.myhelloworldapplication;
import android.graphics.Color;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Button;
import android.widget.TextView;
import org.w3c.dom.Text;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class MainActivity extends AppCompatActivity {
private Set<String> function = new HashSet<>();
private TextView result;
private List<String> keyList;
/*
* 这里创建四个key代表四个操作,放在集合里面,由于集合是不能存储重复属性的,所以防止了重复存储操作
*/
static String multiflyKey = "multifly";
static String divideKey = "divide";
static String plusKey = "plus";
static String substractKey = "substract";
static String doneKey = "done";
/*用于记录操作符前后两次的值
* 使用StringBuilder可以方便计算带小数点的数,而且比StringBuffer更加快速,抢占线程。
*/
private StringBuilder oldValue = null;
private StringBuilder newValue = null;
//使用该参数去监听是否点击等号,点击等号后如果用户没有点击操作符,而直接点击数字,这时候就需要刷新oldValue而不是去计算。
private boolean isClickDone = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
FloatingActionButton fab = findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
});
//创建按钮 并添加监听器
SetUp();
}
class click implements View.OnClickListener{
@Override
public void onClick(View v) {
//通过switch拿到不同按钮的点击事件
//应该有更好的方法?目前只接触了这个,全部按钮的点击事件放在一个地方处理并不是很好的设计
switch (v.getId()){
case R.id.buttonBack:
back();
break;
case R.id.buttonClear:
clear();
break;
case R.id.done:
done();
break;
case R.id.multiply:
//有操作需要进行
caculate(multiflyKey);
break;
case R.id.divide:
caculate(divideKey);
break;
case R.id.plus:
caculate(plusKey);
break;
case R.id.subtract:
caculate(substractKey);
break;
case R.id.zero:
pressNumberButton(0);
break;
case R.id.one:
pressNumberButton(1);
break;
case R.id.two:
pressNumberButton(2);
break;
case R.id.three:
pressNumberButton(3);
break;
case R.id.four:
pressNumberButton(4);
break;
case R.id.five:
pressNumberButton(5);
break;
case R.id.six:
pressNumberButton(6);
break;
case R.id.seven:
pressNumberButton(7);
break;
case R.id.eight:
pressNumberButton(8);
break;
case R.id.nine:
pressNumberButton(9);
break;
case R.id.point:{
if (oldValue == null){
oldValue = new StringBuilder(String.valueOf(0)).append(".");
result.setText("0.");
}else if (newValue == null&&!oldValue.toString().contains(".")){
oldValue.append(".");
result.setText(oldValue.toString());
}
if (newValue!=null && result.getText().toString() != "0" && !result.getText().toString().contains("."))
newValue.append(".");
}
}
}
}
///所有计算符号的操作
public void caculate(String key){
//oldValue如果为空那么就没有比较去操作计算,比如用户一进来就瞎点+-*/。
if (oldValue == null) return;
//判断是否为空,为空就直接添加key
if (!function.isEmpty()){
if (function.size() == 1){
//判断是否有小数点,有的话以double去计算,没有就以integer
//这里再判断一次oldValue!=null是为了防止第三个条件触发,这时候采用短路判断法,在第二步就把这个判断终结掉。否则1*+会出现崩溃
if (oldValue.toString().contains(".")||oldValue!=null||newValue.toString().contains(".")){
Double oldvalue = Double.valueOf(oldValue.toString());
//这里是否为null,在下面处理
//newvalue = 等于null的时候证明用户输入了一个数, 然后一直按加减乘除,而没有输入其他数
Double newvalue;
if (newValue != null )
newvalue = Double.valueOf(newValue.toString());
else newvalue = 0.0;
if (function.contains(multiflyKey))
{
String answer = String.valueOf(oldvalue*newvalue);
//注意下面*和/都有这个处理,5*2.0 = 10.0,我们这里把10.0的.0去掉,结果得到整数都可以去掉,美观点。
if (answer.endsWith(".0"))
result.setText((answer.substring(0,answer.length()-2)));
else
result.setText(answer);
function.remove(multiflyKey);function.add(key);oldValue = new StringBuilder(result.getText().toString()) ;newValue = null;return;
}
if (function.contains(divideKey))
{
if (newvalue == 0&&oldvalue!=0)
{
result.setText("Can't divide a 0");
}else
{
String answer = String.valueOf(oldvalue/newvalue);
if (answer.endsWith(".0"))
result.setText((answer.substring(0,answer.length()-2)));
else
result.setText(answer);
function.remove(divideKey);function.add(key);oldValue = new StringBuilder(result.getText().toString()) ;newValue = null;return;
}
}
if (function.contains(plusKey))
{
function.remove(plusKey);
function.add(key);
if (newValue == null) return;
result.setText(String.valueOf(newvalue+oldvalue));
oldValue = new StringBuilder(result.getText().toString());
newValue = null;return;
}
if (function.contains(substractKey))
{
function.remove(substractKey);
function.add(key);
//这里要放到后面,否则不能记录哪个是最后点击的操作符,如果用户连续点加减乘除
if (newValue == null) return;
result.setText(String.valueOf(oldvalue-newvalue));
oldValue = new StringBuilder(result.getText().toString()) ;
newValue = null;
return;
}
}else{
//整数,操作跟上面大致一样
Integer oldvalue = Integer.valueOf(oldValue.toString());
Integer newvalue;
//newvalue = 等于null的时候证明用户输入了一个数, 然后一直按加减乘除,而没有输入其他数
if (newValue != null )
newvalue = Integer.valueOf(newValue.toString());
else
newvalue = 0;
if (function.contains(multiflyKey))
{String answer = String.valueOf(oldvalue*newvalue);
if (answer.endsWith(".0"))
result.setText((answer.substring(0,answer.length()-2)));
else {result.setText(answer);};
function.remove(multiflyKey);function.add(key);oldValue = new StringBuilder(result.getText().toString()) ;newValue = null;return;}
if (function.contains(divideKey))
{ if (newvalue == 0&&oldvalue!=0)
{
result.setText("Can't divide a 0");
}else {
String answer = String.valueOf(oldvalue/newvalue);
if (answer.endsWith(".0"))
result.setText((answer.substring(0,answer.length()-2)));
else {result.setText(answer);};
function.remove(divideKey);function.add(key);oldValue = new StringBuilder(result.getText().toString()) ;newValue = null;return;}}
if (function.contains(plusKey))
{function.remove(plusKey);function.add(key);if (newValue == null) return;result.setText(String.valueOf(newvalue+oldvalue)); oldValue = new StringBuilder(result.getText().toString());newValue = null;return;}
if (function.contains(substractKey))
{function.remove(substractKey);function.add(key);if (newValue == null) return;result.setText(String.valueOf(oldvalue-newvalue)); oldValue = new StringBuilder(result.getText().toString()) ;newValue = null;return;}
}
}
}else function.add(key);
}
///数字按钮
public void pressNumberButton(int num) {
String resStr = result.getText().toString();
//判断是否有操作
if (function.isEmpty()) {
//判断是否为第一次输入的数
if (resStr.startsWith("0") && !result.getText().toString().contains("."))
{
oldValue = new StringBuilder(String.valueOf(num));
result.setText(String.valueOf(num));
//判断是否有小数
}
else if (result.getText().toString().contains("."))
{
oldValue.append(num);
result.setText(oldValue.toString());
} else if(newValue == null && isClickDone == true){
//这里处理经过一次等于操作后,用户直接输入,取代oldValue
oldValue = new StringBuilder(String.valueOf(num));
result.setText(oldValue.toString());
isClickDone = false;
}
else {
oldValue.append(String.valueOf(num));
result.setText(oldValue.toString());
}
} else {
//1+1=2 2->oldValue 1->newValue 这时候按+ 再按num
//处理第一个运算符过后的逻辑
if (oldValue.toString() == resStr) oldValue = new StringBuilder(resStr);
if (newValue == null){
newValue = new StringBuilder(String.valueOf(num));
result.setText(newValue.toString());
}else if (newValue.length() == 1 && newValue.toString().contains("0") && num == 1) {
newValue = new StringBuilder(String.valueOf(num));
result.setText("1");
} else if (newValue.length() == 1 && newValue.toString().contains("0") && num != 1) {
newValue = new StringBuilder(String.valueOf(num));
result.setText(newValue);
}
else {
newValue.append(num);
result.setText(newValue.toString());
}
}
}
///等于号
//跟caculate不同的是,他不用添加下一个操作符,只有remove之前的操作符就行。
public void done(){
if (oldValue == null) return;
if (newValue==null) return;;
if (function.size() == 1){
isClickDone = true;
//判断是否有小数点,有的话以double去计算,没有就以integer
if (newValue.toString().contains(".")||oldValue.toString().contains(".")){
Double newvalue = Double.valueOf(newValue.toString());
Double oldvalue = Double.valueOf(oldValue.toString());
if (function.contains(multiflyKey))
{
String answer = String.valueOf(oldvalue*newvalue);
if (answer.endsWith(".0"))
result.setText((answer.substring(0,answer.length()-2)));
else
result.setText(answer);
function.remove(multiflyKey);
oldValue = new StringBuilder(result.getText().toString()) ;
newValue = null;
return;
}
if (function.contains(divideKey))
{
if (newvalue == 0&&oldvalue!=0)
result.setText("Can't divide a 0");
else {
String answer = String.valueOf(oldvalue/newvalue);
if (answer.endsWith(".0"))
result.setText((answer.substring(0,answer.length()-2)));
else
result.setText(answer);
function.remove(divideKey);
oldValue = new StringBuilder(result.getText().toString()) ;newValue = null;return;
}
}
if (function.contains(plusKey))
{
result.setText(String.valueOf(newvalue+oldvalue));
function.remove(plusKey);
oldValue = new StringBuilder(result.getText().toString()) ;
newValue = null;return;
}
if (function.contains(substractKey))
{
result.setText(String.valueOf(oldvalue-newvalue));
function.remove(substractKey);
oldValue = new StringBuilder(result.getText().toString()) ;
newValue = null;
return;
}
}else{
Integer newvalue = Integer.valueOf(newValue.toString());
Integer oldvalue = Integer.valueOf(oldValue.toString());
if (function.contains(multiflyKey))
{String answer = String.valueOf(oldvalue*newvalue);
if (answer.endsWith(".0"))
result.setText((answer.substring(0,answer.length()-2)));
else {result.setText(answer);};
function.remove(multiflyKey);oldValue = new StringBuilder(result.getText().toString()) ;newValue = null;return;}
if (function.contains(divideKey))
{ if (newvalue == 0&&oldvalue!=0)
{
result.setText("Can't divide a 0");
}else {
String answer = String.valueOf(oldvalue/newvalue);
if (answer.endsWith(".0"))
result.setText((answer.substring(0,answer.length()-2)));
else {result.setText(answer);};
function.remove(divideKey);oldValue = new StringBuilder(result.getText().toString()) ;newValue = null;return;}}
if (function.contains(plusKey))
{ result.setText(String.valueOf(newvalue+oldvalue)); function.remove(plusKey);oldValue = new StringBuilder(result.getText().toString()) ;newValue = null;return;}
if (function.contains(substractKey))
{result.setText(String.valueOf(oldvalue-newvalue)); function.remove(substractKey);oldValue = new StringBuilder(result.getText().toString()) ;newValue = null;return;}
}
}
}
///清除
public void clear(){
oldValue = null;
newValue = null;
function.removeAll(keyList);
result.setText("0");
}
///撤退
public void back(){
}
public void SetUp(){
Button BtnBack = findViewById(R.id.buttonBack);
BtnBack.setOnClickListener(new click());
Button BtnClear = findViewById(R.id.buttonClear);
BtnClear.setOnClickListener(new click());
Button BtnDone = findViewById(R.id.done);
BtnDone.setOnClickListener(new click());
Button BtnMultiply = findViewById(R.id.multiply);
BtnMultiply.setOnClickListener(new click());
Button BtnDivide= findViewById(R.id.divide);
BtnDivide.setOnClickListener(new click());
Button BtnPlus = findViewById(R.id.plus);
BtnPlus.setOnClickListener(new click());
Button Btnsubtract = findViewById(R.id.subtract);
Btnsubtract.setOnClickListener(new click());
Button BtnZero = findViewById(R.id.zero);
BtnZero.setOnClickListener(new click());
Button BtnOne = findViewById(R.id.one);
BtnOne.setOnClickListener(new click());
Button BtnTwo = findViewById(R.id.two);
BtnTwo.setOnClickListener(new click());
Button BtnThree = findViewById(R.id.three);
BtnThree.setOnClickListener(new click());
Button BtnFour = findViewById(R.id.four);
BtnFour.setOnClickListener(new click());
Button BtnFive = findViewById(R.id.five);
BtnFive.setOnClickListener(new click());
Button BtnSix = findViewById(R.id.six);
BtnSix.setOnClickListener(new click());
Button BtnSeven = findViewById(R.id.seven);
BtnSeven.setOnClickListener(new click());
Button BtnEight = findViewById(R.id.eight);
BtnEight.setOnClickListener(new click());
Button BtnNine = findViewById(R.id.nine);
BtnNine.setOnClickListener(new click());
Button BtnPoint = findViewById(R.id.point);
BtnPoint.setOnClickListener(new click());
result = findViewById(R.id.resultText);
keyList = new ArrayList<>();
keyList.add(multiflyKey);
keyList.add(divideKey);
keyList.add(plusKey);
keyList.add(substractKey);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
下面是demo图,中间有个helloworld,不知道怎么去掉,已经把所有application.xml上面的label去掉了
![屏幕快照 2019-03-21 下午12.02.37.png](https://upload-images.jianshu.io/upload_images/103
69851-91ed2739877ea455.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)