自定义圆形TextView
效果图
建立attr文件
通过对控件手势动作进行监听,改变按钮的样式;即按下为白色,松开为橙色
<resources> <declare-styleable name="SetCircle"> <attr name="CircleColor" format="color"/> <attr name="SelectCircle" format="color"/> </declare-styleable> </resources>
绘制圆形
protected void onDraw(Canvas canvas) { //判断手势动作,改变控件状态 if (isSelect){ CirclePaint.setColor( SelectCircle ); }else { CirclePaint.setColor( CircleColor ); } //设置填充方式 CirclePaint.setStyle( Paint.Style.FILL ); //设置抗锯齿 CirclePaint.setAntiAlias( true ); RectF rectF = new RectF(); //设置半径,比较长宽,取最大值 int radius = getMeasuredWidth() > getMeasuredHeight() ? getMeasuredWidth() : getMeasuredHeight(); rectF.set(getPaddingLeft(),getPaddingTop(),radius-getPaddingRight(),radius-getPaddingBottom()); //绘制圆弧 canvas.drawArc(rectF,0,360,false,CirclePaint); super.onDraw(canvas); }
字符拼接
通过StringBuilder将用户输入的数字进行拼接,段尾对复位按钮进行判断,将数字栈和符号栈以及拼接字符串的内容全部清空;
private class NumOnClick implements View.OnClickListener{ @Override public void onClick(View view) { switch (view.getId()){ case R.id.Zero: numBuilder.append('0'); break; case R.id.One: numBuilder.append('1'); break; case R.id.Two: numBuilder.append('2'); break; case R.id.Three: numBuilder.append('3'); break; case R.id.Four: numBuilder.append('4'); break; case R.id.Five: numBuilder.append('5'); break; case R.id.Six: numBuilder.append('6'); break; case R.id.Seven: numBuilder.append('7'); break; case R.id.Eight: numBuilder.append('8'); break; case R.id.Nine: numBuilder.append('9'); break; case R.id.Point: numBuilder.append('.'); break; case R.id.Reset: isReset = true; } if (isReset){ PopStack(); numBuilder.delete(0,numBuilder.length()); ResultBox.setText("0"); operatorStack.push('#'); isReset = false; }else { ResultBox.setText(numBuilder.toString()); } } }
清空栈内元素
清空数字栈和符号栈内元素
private void PopStack(){ while (numStack.isEmpty()){ numStack.pop(); } while (operatorStack.isEmpty()){ operatorStack.pop(); } }
运算执行
手势监听
此处以加运算符为例,对按下和松开两个事情进行监听,分别改变控件样式,并传入相应运算符进行运算
private class OperatorOnClick implements View.OnTouchListener{ @Override public boolean onTouch(View view, MotionEvent motionEvent) { boolean isPress = false; if (motionEvent.getAction() == MotionEvent.ACTION_DOWN){ isPress = true; } switch (view.getId()){ case R.id.Add: if (isPress){ mAdd.IsSelect(true); mAdd.setTextColor(getResources().getColor(R.color.normal)); StartOperation(ADD); }else { mAdd.IsSelect(false); mAdd.setTextColor(getResources().getColor(R.color.select)); } break;
入栈&&出栈
在初始化时,将符号栈压入‘#’符号,即代表第一次执行,不进行结果运算
operatorStack.push('#');
第一次运行时,即将符号栈栈顶元素取出,即‘#’,假如输入1和+,此时无法构成算式,因为确实另外一个运算数,即直接将其压入栈中,不进行结果运算;然后将拼接字符串清空,因为假如输入完了100和+,因为+号是不显示在用户界面的,如果不进行清空,之后输入的字符会追加在其之后,例如在输入50,即不清空为10050,会造成用户体验不良以及使用麻烦等缺点;
假如输入了100和+,然后输入50和-,构成100+50-算式,第一次不进行运算,如上释所示,第二次输入的-,即取出符号栈顶元素+,和运算数100和50,并将其传入EXEOperation()方法,开始运算结果
private void StartOperation(char symbol){ char operator = operatorStack.pop(); if (operator == EQUAL){ operatorStack.push(symbol); } else if (operator == '#'){ numStack.push(numBuilder.toString().isEmpty()?0:Double.parseDouble(numBuilder.toString())); operatorStack.push(symbol); numBuilder.delete(0,numBuilder.length()); }else { switch (operator){ case ADD: EXEOperation(numStack.pop(),numBuilder.toString().isEmpty()?0:Double.parseDouble(numBuilder.toString()),ADD); break; case SUB: EXEOperation(numStack.pop(),numBuilder.toString().isEmpty()?0:Double.parseDouble(numBuilder.toString()),SUB); break; case MULTIPLY: EXEOperation(numStack.pop(),numBuilder.toString().isEmpty()?0:Double.parseDouble(numBuilder.toString()),MULTIPLY); break; case DIVISION: EXEOperation(numStack.pop(),numBuilder.toString().isEmpty()?0:Double.parseDouble(numBuilder.toString()),DIVISION); break; case MOD: EXEOperation(numStack.pop(),numBuilder.toString().isEmpty()?0:Double.parseDouble(numBuilder.toString()),MOD); break; case EQUAL: EXEOperation(numStack.pop(),numBuilder.toString().isEmpty()?0:Double.parseDouble(numBuilder.toString())); break; } } }
运算结果
三个参数分别为第一个运算数,和另外一个运算数,以及符号栈栈顶元素,对符号进行判断,并进行结果运算,然后将结果压入栈中以及第二个运算符压人符合栈;
重点在于对等于键进行判断:
1:例如输入算式1+1-,对第二个运算符进行监听即可得到前一结果,然后在输入数字,符号,得到前一结果…
2:例如输入算式1+1=,此时并没有对等于符号进行监听,无法完成运算,解决办法为不将等于压入符号栈,直接结果入栈,相当于需要执行第一步才能完成运算
小数位判断
对结果字符串进行子串截取,判断小数点之后是否存在小数位,因为为double类型,默认会存在小数位。例如:
(1)1.0,则省略小数点后的0,直接输出0;
(2)1.05,则不进行小数位省略,直接输出
//判断小数位之后是否有数字 if (str.substring(str.indexOf('.') + 1, str.length() - 1).isEmpty()) { str = str.substring(0, str.indexOf('.')); }
private void EXEOperation(double front,double rear,char operator) { double result = 0; String str; /** * 对连续点击运算符,而运算数字并未符合标准时进行判断 * 例如:12+ * 此时12和+分别进栈,此时若再点击运算符+,则无法进行运算,因为rear运算数为空*/ switch (operator) { case ADD: result = front + rear; break; case SUB: result = front - rear; break; case MULTIPLY: result = front * rear; break; case DIVISION: result = front / rear; break; case MOD: result = front < rear ? front :front % rear; break; } numStack.push(result); if (isReturn){ operatorStack.push(EQUAL); }else { operatorStack.push(operator); isReturn = false; } str = String.valueOf(result); //判断小数位之后是否有数字 if (str.substring(str.indexOf('.') + 1, str.length() - 1).isEmpty()) { str = str.substring(0, str.indexOf('.')); } ResultBox.setText(str); //前运算符清空,为后运算符输入做准备 numBuilder.delete(0,numBuilder.length()); }