Android实现了括号乘方取余的计算器

最近在入门Android,写了一个计算器,学习了一些比较精妙的处理方法,拿出来供大家学习参考啦。

完整源代码和签名打包好的apk文件可以在这里下载,apk文件可以在Android5.0以上版本安装运行

先上图看看界面

 

这是在我手机上截的图,我们先来看看界面是怎么实现的,外层是LinearLayout布局,按钮采用GridLayout布局,另按钮跨两行或跨两列排布的代码是在Button属性里设置

android:layout_rowSpan="2"//跨两行
android:layout_columnSpan="2"//跨两列

让几个按钮平分占满GridLayout一行或一列的Button属性是

android:layout_rowWeight="1"//一个按钮占一行空间的比重
android:layout_columnWeight="1"//一个按钮占占一列空间的比重

这里1只是用作计算比例,如果想让按钮平分的话就都设置一样的数就好了,不一定非要是1;另外如果某一行的按钮要设置rowWeight属性的话,必须要这一行的每一个按钮都设置,否则会显示异常,columnWeight属性对每一列的按钮也一样。

这里TextView的大小是固定的,如果要显示的东西太多,就要求TextView可以滚动查看,这里只有为TextView设置一个监听就好了

tvDisplay=(TextView) findViewById(R.id.tvDisplay);//获取TextView的id
tvDisplay.setMovementMethod(ScrollingMovementMethod.getInstance());//设置滚动监听

简简单单一行代码就可以实现滚动功能呢,那新问题来了,当我们点击按钮向里面输入时,我们必须手动滑动到TextView最底部才能看到我们新输入的东西,那有没有办法让我们新输入时自动跳转到最后一行呢?这里用到了TextView的scrollTo(int x,int y);方法,我们在每一次文本长度改变后,加上这样一段代码

int tvDisplayHeight=tvDisplay.getHeight();//获取显示框高度
int heightDiff=tvDisplay.getLayout().getLineTop(tvDisplay.getLineCount())-tvDisplayHeight;//获取文本高度与显示框高度的差
    if(heightDiff>0)
      {
        //如果高度差大于零,就让显示框的左上角定位到刚好够显示文本最后一行的位置
         tvDisplay.scrollTo(0,heightDiff);
      }else{
        //否则显示到最开头
         tvDisplay.scrollTo(0,0);
      }

再上两张图

可以看到,为了让运算结果更突出,区别计算记录和新输入的表达式,我改变了部分字体的大小,这个可以用Spannable实现

spannable=new SpannableString(strRecord);
spannable.setSpan(new AbsoluteSizeSpan(15,true),0,str.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
tvDisplay.setText(spannable);

Spannable是可以改变部分文本属性(如大小,颜色等)的类,具体更详尽的用法大家可以查一下。

另外给TextView添加双击满屏的方法,以便我们更方便的查看运算记录,如图

 

 

 

 

 

 

 

 

 

需要给TextView设置点击监听,并通过获取两次点击时的系统时间判断两次点击的时间间隔,代码如下

//第一次点击时间在activity启动时初始化
private long firstClickTime= System.currentTimeMillis();
    private long secondClickTime;
/*
*//此处省略无关代码
*
*
*/
            tvDisplay.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
//第二次点击时间在点击TextView时初始化
                secondClickTime=System.currentTimeMillis();
                if(secondClickTime-firstClickTime<500)
                {
                    ViewGroup.LayoutParams layoutParams=tvDisplay.getLayoutParams();
                    if(tvExtent)//tvExtent是一个布尔值,用于判断此次双击执行放大还是恢复操作
                    {
                        Log.d("MainActivity",tvDisplay.getHeight()+"");
                        layoutParams.height=((LinearLayout)findViewById(R.id.activity_main)).getHeight();
                        tvExtent=false;
                    }else
                    {
                        layoutParams.height=300;
                        tvExtent=true;
                    }
                    tvDisplay.setLayoutParams(layoutParams);
                }
                firstClickTime=secondClickTime;
            }
        });

ok,到这里界面设置的处理技巧就介绍完了,接下来我们看看这个计算器的核心:如何实现计算。

计算基于字符串处理,当我们点击“=”按钮时,开始计算,首先提取出TextView里的字符串,定义两个栈,一个Double型一个Character行。再定义一个StringBuffer。然后遍历表达式字符串的每一个字符,如果字符是0~9或者小数点,就先存入StringBuffer,当遇到一个字符不属于上述范围,就一定是一个运算符了,这时候把StringBuffer里的字符串转化为Double

型数值压入存储运算数的栈。然后比较新获取的运算符与运算符栈里的最顶部的运算符的优先级(如果运算符为空的话就是新获得的运算符优先级高啦),如果新获得的优先级高于最栈里最顶部的,就取出一个运算符两个数进行运算(运算结果压入数字栈),直到顶部的运算符高于新获得的这个,然后将新获得的运算符压入栈。直到所有运算完成。这是数值栈里唯一的一个数就是最后结果

下面时定义运算符优先级的代码:

private boolean compare(char symbol){

        if(symbolStack.isEmpty())
        {
            return false;
        }else{
            char pop=symbolStack.peek();
            switch (pop)
            {
                case '^':
                    if (symbol!='(')
                    return true;
                case 'x':
                case '÷':
                case '%':
                    if(symbol!='^'&&symbol!='(')
                        return true;
                case '+':
                case '-':
                    if(symbol=='+'||symbol=='-'||symbol==')'||symbol=='=')
                        return true;
                 default://pop=='('
                     return false;
            }
        }
    }

字符串处理算法代码:

 private Double getResult  (String str)throws Exception
    {

        str=str.substring(strRecord.length(),str.length());
        str.trim();
       Log.d("MainActivity","str:"+str+"\nstrRecord:"+strRecord);
        Double result=0.0;
        numberStack=new Stack<Double>();
        symbolStack=new Stack<Character>();
        StringBuffer number=new StringBuffer();
        for(int i=0;i<str.length();i++)
        {
            char ch=str.charAt(i);
            if((ch>='0'&&ch<='9')||ch=='.')
            {
                number.append(ch);
            }else{
                if(number.length()!=0)
                {
                    numberStack.push(Double.parseDouble(number.toString()));
                    number.setLength(0);
                }
                //数字入栈,处理是否运算
                while(compare(ch))
                {
                    Double b=numberStack.pop();
                    Double a=numberStack.pop();
                    switch ((char)symbolStack.pop())
                    {
                        case '+':
                            numberStack.push(a+b);
                            break;

                        case '-':
                            numberStack.push(a-b);
                            break;
                        case 'x':
                            numberStack.push(a*b);
                            break;
                        case '÷':
                            numberStack.push(a/b);
                            break;
                        case'%':
                            numberStack.push(a%b);
                            break;
                        case '^':
                            numberStack.push(Math.pow(a,b));
                            break;
                    }
                }
                //运算结束,处理运算符ch
                if(ch=='='&&!numberStack.isEmpty())
                    result=numberStack.pop();
                else {
                    if(ch==')')
                    {
                        symbolStack.pop();
                    }else {
                        symbolStack.push(ch);
                    }
                }
            }
        }

        return result;
    }

可以看到,getResult方法可能会抛出异常,这个异常会在处理表达式字符串出错时抛出,抛出的可能有很大,只有表达式不符合规范,无法运算,就会抛出,这是在“=”点击监听里捕获,给用户一个提示

效果如图:

 

代码如下:

 btnEqual.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    String str=tvDisplay.getText().toString();
                    if(str.length()==strRecord.length())
                        return;
                    str+="=";
                    double result=0;
                    try{
                         result= getResult(str);
                        if(result%1==0){
                            strRecord=str+(int)result+"\n";
                        }
                        else {
                            strRecord=str+result+"\n";
                        }
                        spannable=new SpannableString(strRecord);
                        spannable.setSpan(new AbsoluteSizeSpan(15,true),0,str.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
                        tvDisplay.setText(spannable);
                        int tvDisplayHeight=tvDisplay.getHeight();
                        int heightDiff=tvDisplay.getLayout().getLineTop(tvDisplay.getLineCount())-tvDisplayHeight;
                        if(heightDiff>0)
                        {
                            tvDisplay.scrollTo(0,heightDiff);
                        }else{
                            tvDisplay.scrollTo(0,0);
                        }
                    }catch(Exception e){
                        Toast.makeText(MainActivity.this,"表达式有误",Toast.LENGTH_SHORT).show();
                    }


                }
            });

接下来最后一个就是换掉软件图标啦,这个很简单,相信大家都会了,但是我遇到了一个问题,就是我真机测试的时候图标无法显示,但是换一个手机就可以,百度了一下,这个可能是还没有换图标时我装过这个软件,手机系统缓存了软件图标,当同签名同工程名的软件再次安装时,手机系统自动调用缓存图标,就导致无法正常显示了,这个可以解决方法据说有三个,一个是改打包时用的签名,一个是改工程名(这两个方法我都没试过,不知道行不行),什么都不想改的话就换一下手机主题,软件图标就可以同步了,这个方法亲测有效。

好了,分享这么多,第一次写博客,难免有瑕疵,希望大家留言指正

  • 0
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值