What if there’s an easy way to use a calculator for your android app? That would be great! Right? Well, I’ll try to help you guys with that with these lessons.
Our post for today is about an android calculator tutorial and after the tutorial is the source code download, available in zip format.
Just a brief background on how this calculator works, we will have a TextView in which the user can click on. After clicking it, our calculator will be shown using a pop up or dialog.
The contents of this post include:
1.0 Source Code Output Video Demo
2.0 Java files structure
3.0 Java files source code
3.1 MainActivity.java
3.2 ButtonsClickListener.java
3.3 CalculatorContentView.java
3.4 CalculatorPopup.java
3.5 CustomStateListDrawable.java
3.6 ViewSize.java
3.0 Source code download
Alright, here we go!
1.0 Source Code Output Video Demo
youtube video here
2.0 Java files structure
src/com.example.calculator
MainActivity.java
src/calculator
ButtonsClickListener.java
CalculatorContentView.java
CalculatorPopup.java
CustomStateListDrawable.java
ViewSize.java
3.0 Java files source code
3.1 MainActivity.java code
MainActivity.java is under com.example.calculator package. This is where we have five example TextViews with OnClickListener set as the calculator pop up.
package com.example.calculator; import android.app.Activity; import android.os.Bundle; import android.view.Gravity; import android.view.View; import android.view.View.OnClickListener; import android.widget.LinearLayout; import android.widget.TextView; import calculator.CalculatorPopup; import calculator.ViewSize; public class MainActivity extends Activity implements OnClickListener { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // set the content as 5 text view setContentView(this.contentView()); } LinearLayout contentView(){ // container of 5 text views LinearLayout contentView = new LinearLayout(this); contentView.setOrientation(LinearLayout.VERTICAL); // generate 5 sample text views for(int x = 0 ; x < 5 ; x++){ // just some params for the text view LinearLayout.LayoutParams params = new LinearLayout.LayoutParams((int) ViewSize.computeWidth(100,this),(int) ViewSize.computeHeight(50,this)); params.setMargins(5, 5, 0, 0); // a text view TextView textView = new TextView(this); textView.setText(x+""); textView.setPadding(2, 0, 2, 0); textView.setGravity(Gravity.CENTER_VERTICAL | Gravity.RIGHT); textView.setLayoutParams(params); // set the onClick() method of this class textView.setOnClickListener(this); // add to linear layout contentView.addView(textView); } return contentView; } @Override public void onClick(View view) { // our calculator instance CalculatorPopup calculatorPopup = new CalculatorPopup(this); // set the text view where click happened calculatorPopup.getCalculator().setViewWhereClickHappen(view); // show the calculator pop up calculatorPopup.show(); } }
The following .java files are under the calculator package
3.1 CalculatorContentView.java
This is where you can customize the symbols of our calculator.
package calculator; import android.graphics.Color; import android.graphics.Typeface; import android.view.Gravity; import android.view.View; import android.widget.Button; import android.widget.LinearLayout; import android.widget.TextView; public class CalculatorContentView extends LinearLayout { // you can customize symbols in the calculator final static String ADD = "+"; final static String SUBTRACT = "-"; final static String MUTIPLY = "X"; final static String DIVIDE = "÷"; final static String DOT = "."; final static String EQUAL = "="; final static String CLEAR = "Clr"; final static String OK = "OK"; // buttons array, used for constructing the calculator ui final static String BUTTONS[][] = { { CLEAR, OK }, { "7", "8", "9", MUTIPLY }, { "4", "5", "6", DIVIDE }, { "1", "2", "3", ADD }, { "0", DOT, EQUAL, SUBTRACT } }; ButtonsClickListener clickListener; TextView display; View viewWhereClickHappen; CalculatorPopup calculatorPopup; public CalculatorContentView(CalculatorPopup calculatorPopup) { super(calculatorPopup.getContext()); this.setOrientation(LinearLayout.VERTICAL); this.calculatorPopup = calculatorPopup; clickListener = new ButtonsClickListener(this); this.addComponent(); } private void addComponent() { // initialize the display this.display = this.display(); this.addView(this.border()); // add the display to view this.addView(this.display); this.addView(this.border()); // make the buttons work int btnCount = BUTTONS.length; // construct the calculator buttons for (int x = 0; x < btnCount; x++) { LinearLayout linearLayout = new LinearLayout(this.getContext()); LinearLayout.LayoutParams linearParams = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, (int) ViewSize.computeHeight(50, this.getContext())); linearParams.setMargins(0, 1, 0, 0); LinearLayout.LayoutParams buttonParams = new LinearLayout.LayoutParams(0, LayoutParams.MATCH_PARENT); buttonParams.weight = 1; buttonParams.setMargins(1, 0, 1, 0); int innerBtnCount = BUTTONS[x].length; for (int y = 0; y < innerBtnCount; y++) { Button button = new Button(this.getContext()); // set the on click listener button.setOnClickListener(this.clickListener); button.setText(BUTTONS[x][y] + ""); button.setTag(BUTTONS[x][y] + ""); button.setBackgroundColor(Color.LTGRAY); // simple animation effect when clicking a button button.setBackground(new CustomStateListDrawable(button)); linearLayout.addView(button, buttonParams); } this.addView(linearLayout, linearParams); } } // add border to the pop up private View border() { LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, 1); View border = new View(this.getContext()); border.setLayoutParams(params); border.setBackgroundColor(Color.BLACK); return border; } /** * @return display = the white bar on the calculator where you show the numbers, etc. */ private TextView display() { LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, (int) ViewSize.computeHeight(50, this.getContext())); params.setMargins(1, 0, 1, 0); TextView display = new TextView(this.getContext()); display.setGravity(Gravity.CENTER_VERTICAL | Gravity.RIGHT); display.setText("0"); display.setTypeface(null, Typeface.BOLD); display.setTextSize(18); display.setLayoutParams(params); display.setPadding(2, 5, 2, 5); display.setBackgroundColor(Color.parseColor("#c7c9c5")); return display; } public void setViewWhereClickHappen(View viewWhereClickHappen) { this.viewWhereClickHappen = viewWhereClickHappen; // SET displayData VALUE clickListener.displayData = ((TextView) viewWhereClickHappen).getText().toString(); this.display.setText(clickListener.displayData); } }
3.2 CalculatorPopup.java
This class will help in showing our calculator in a pop up window.
package calculator; import android.app.Dialog; import android.content.Context; import android.view.Window; // used to show the calculator in a pop up dialog public class CalculatorPopup extends Dialog { CalculatorContentView calculator; public CalculatorPopup(Context context) { super(context); this.calculator = new CalculatorContentView(this); this.requestWindowFeature(Window.FEATURE_NO_TITLE); this.setContentView(calculator); } public CalculatorContentView getCalculator() { return calculator; } }
3.3 CustomStateListDrawable.java
This class is used by our calculator to have a simple click effect. The buttons used in our calculator are TextViews, so to make those TextViews behave like a Button, we will use this class.
package calculator; import android.graphics.Color; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.StateListDrawable; import android.view.View; // used for the click effect class CustomStateListDrawable extends StateListDrawable { /** * @param view = is the source of default background */ protected CustomStateListDrawable(View view) { int stateFocused = android.R.attr.state_focused; int statePressed = android.R.attr.state_pressed; int stateSelected = android.R.attr.state_selected; this.addState(new int[] { statePressed }, new ColorDrawable(Color.GRAY)); this.addState( new int[] { -stateFocused, -statePressed, -stateSelected }, view.getBackground() ); } @Override protected boolean onStateChange(int[] stateSet) { return super.onStateChange(stateSet); } }
3.4 ViewSize.java
These class is used to estimate the size of our calculator on different screens. I have tested the calculator on my phone and table, it looks great!
package calculator; import android.content.Context; // estimate the width and height of an element based on screen size or given value public class ViewSize { final static float LOW_LEVEL = 0.75f; final static float MEDIUM_LEVEL = 1.0f; final static float HIGH_LEVEL = 1.5f; final static float X_HIGH_LEVEL = 2.0f; final static float XX_HIGH_LEVEL = 3.0f; final static float XXX_HIGH_LEVEL = 4.0f; static public float computeWidth(float widthInMediumDensity, Context context) { float level = context.getApplicationContext().getResources().getDisplayMetrics().density; if (level == LOW_LEVEL) { return widthInMediumDensity * LOW_LEVEL; } else if (level == MEDIUM_LEVEL) { return widthInMediumDensity; } else if (level == HIGH_LEVEL) { return widthInMediumDensity * HIGH_LEVEL; } else if (level == X_HIGH_LEVEL) { return widthInMediumDensity * X_HIGH_LEVEL; } else if (level == XX_HIGH_LEVEL) { return widthInMediumDensity * XX_HIGH_LEVEL; } else if (level == XXX_HIGH_LEVEL) { return widthInMediumDensity * XXX_HIGH_LEVEL; } else { return widthInMediumDensity; } } static public float computeHeight(float heightInMediumDensity, Context context) { float level = context.getApplicationContext().getResources().getDisplayMetrics().density; if (level == LOW_LEVEL) { return heightInMediumDensity * LOW_LEVEL; } else if (level == MEDIUM_LEVEL) { return heightInMediumDensity; } else if (level == HIGH_LEVEL) { return heightInMediumDensity * HIGH_LEVEL; } else if (level == X_HIGH_LEVEL) { return heightInMediumDensity * X_HIGH_LEVEL; } else if (level == XX_HIGH_LEVEL) { return heightInMediumDensity * XX_HIGH_LEVEL; } else if (level == XXX_HIGH_LEVEL) { return heightInMediumDensity * XXX_HIGH_LEVEL; } else { return heightInMediumDensity; } } }
3.5 ButtonsClickListener.java
This is where all the operations happen, from clicking a calculator button to the computation of numbers.
package calculator; import java.util.regex.Matcher; import java.util.regex.Pattern; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.widget.TextView; import android.widget.Toast; // listens to every button click in our calculator public class ButtonsClickListener implements OnClickListener { boolean operationBtnClick = false; String displayData = "0"; int operationClickCount = 0; String firstOperationClick; CalculatorContentView calculatorView; public ButtonsClickListener(CalculatorContentView calculatorView) { this.calculatorView = calculatorView; } @Override public void onClick(View view) { String tag = view.getTag().toString(); String tempData = this.displayData; if (tag.equalsIgnoreCase(CalculatorContentView.ADD)) { displayData = this.operationClick(view, tag, tempData); this.calculatorView.display.setText(this.displayData); } else if (tag.equalsIgnoreCase(CalculatorContentView.SUBTRACT)) { displayData = this.operationClick(view, tag, tempData); this.calculatorView.display.setText(this.displayData); } else if (tag.equalsIgnoreCase(CalculatorContentView.MUTIPLY)) { displayData = this.operationClick(view, tag, tempData); this.calculatorView.display.setText(this.displayData); } else if (tag.equalsIgnoreCase(CalculatorContentView.DIVIDE)) { displayData = this.operationClick(view, tag, tempData); this.calculatorView.display.setText(this.displayData); } else if (tag.equalsIgnoreCase(CalculatorContentView.EQUAL)) { operationBtnClick = false; this.equalButtonClick(tempData); } else if (tag.equalsIgnoreCase(CalculatorContentView.DOT)) { tempData += ((TextView) view).getText().toString(); displayData = tempData; calculatorView.display.setText(displayData); } else if (tag.equalsIgnoreCase(CalculatorContentView.CLEAR)) { operationBtnClick = false; // *** REMOVE LAST CHARACTER IN tempData tempData = this.removeLastChar(tempData); // *** IF LENGTH IS LESS THAN 0. SET displayData TO 0 displayData = tempData.length() <= 0 ? "0" : tempData; // *** SET THE RESULT TO CALCULATOR DISPLAY calculatorView.display.setText(displayData); } else if (tag.equalsIgnoreCase(CalculatorContentView.OK)) { operationBtnClick = false; okButtonClick(tempData); } else { // *** NUMBER BUTTON CLICK operationBtnClick = false; if (tempData.equalsIgnoreCase("0")) { tempData = ""; } tempData += tag; displayData = tempData; calculatorView.display.setText(displayData); } } private void equalButtonClick(String tempData) { try { String data = tempData.substring(1, tempData.length()); // *** GET THE INDEX OF FIRST OPERATION. ADD 1 BECAUSE WE HAVE // REMOVE THE FIRST CHARACTER int indexOfFirstOperation = this.indexOfFirstOperation("[^0-9\\.]", data) + 1; // *** GET THE INDEX OF FIRST OPERATION String firstOperation = tempData.charAt(indexOfFirstOperation) + ""; // *** COMPUTE THE RESULT double result = this.compute(firstOperation, tempData); // *** REMOVE UNWANTED DECIMAL PLACES this.displayData = this.removeUnwantedValueInDecimal(result + ""); // *** SET THE RESULT TO CALCULATOR DISPLAY this.calculatorView.display.setText(this.displayData + ""); } catch (Exception e) { e.printStackTrace(); } } private void okButtonClick(String tempData) { try { String data = tempData.length() > 2 ? tempData.substring(1, tempData.length()) : tempData; // *** GET THE INDEX OF FIRST OPERATION int indexOfFirstOperation = this.indexOfFirstOperation("[^0-9\\.]", data) + 1; if (isFirstOperationTextInBetweenNumber(indexOfFirstOperation, data)) { // *** COMPUTE // *** GET THE INDEX OF FIRST OPERATION String firstOperation = tempData.charAt(indexOfFirstOperation) + ""; // *** COMPUTE THE RESULT double result = this.compute(firstOperation, tempData); // *** REMOVE UNWANTED DECIMAL PLACES this.displayData = this.removeUnwantedValueInDecimal(result + ""); // *** UPDATE TEXT OF TEXTVIEW WHERE CLICK HAPPEN ((TextView) this.calculatorView.viewWhereClickHappen) .setText(displayData + ""); // *** DISMISS CALCULATOR POPUP this.calculatorView.calculatorPopup.dismiss(); } else { // *** DATA IS A REAL MAY A REAL NUMBER tempData = (isLastTextOperation(tempData) || isLastTextDot(tempData)) && tempData.length() > 2 ? removeLastChar(tempData) : tempData; if (isValidNumber(tempData)) { tempData = Double.parseDouble(tempData) + ""; tempData = removeUnwantedValueInDecimal(tempData); this.displayData = tempData; // *** UPDATE TEXT OF TEXTVIEW WHERE CLICK HAPPEN ((TextView) this.calculatorView.viewWhereClickHappen).setText(displayData + ""); // *** DISMISS CALCULATOR POPUP this.calculatorView.calculatorPopup.dismiss(); } else { Toast.makeText(this.calculatorView.getContext(), "Not a valid number " + tempData, Toast.LENGTH_SHORT).show(); } } } catch (Exception e) { e.printStackTrace(); Log.e("error", "error"); } } /** * * @param view * @param operation * @param tempData * @return */ private String operationClick(View view, String operation, String tempData) { try { if (!this.allowToAppendOperationText(true, tempData, operation)) { Toast.makeText(view.getContext(), "Invalid", Toast.LENGTH_SHORT).show(); return "0"; } if (tempData.equalsIgnoreCase("0") && operation.equalsIgnoreCase(CalculatorContentView.SUBTRACT)) { return operation; } if (this.isFirstTextNegativeSign(tempData) && tempData.length() <= 1) { return tempData; } if (!this.doDataContainOperationText(tempData)) { // no it doesn't tempData += operation; return tempData; } // *** APPEND THE operation TO tempData tempData += operation; // *** REMOVE FIRST CHARACTER IN tempData BECAUSE IT MAY A NEGATIVE value String data = tempData.substring(1, tempData.length()); if (this.doComputeTotal("([^0-9\\.])", data)) { // *** COMPUTE TOTAL // *** GET THE INDEX OF FIRST OPERATION. ADD 1 BECAUSE WE HAVE REMOVED THE FIRST CHARACTER int indexOfFirstOperation = this.indexOfFirstOperation("[^0-9\\.]", data) + 1; // *** GET THE FIRST OPERATION String firstOperation = tempData.charAt(indexOfFirstOperation) + ""; // *** REMOVE LAST CHARACTER IN tempData tempData = this.removeLastChar(tempData); // *** COMPUTE THE RESULT double result = this.compute(firstOperation, tempData); // *** REMOVE UNWANTED DECIMAL VALUE tempData = this.removeUnwantedValueInDecimal(result + "") + operation; } else { // *** REPLACE THE OPERATION IN LAST CHAR IN tempData tempData = this.removePreviousOperationText(tempData); } } catch (Exception e) { e.printStackTrace(); tempData = ""; this.calculatorView.display.setText("Error"); } return tempData; } /** * * @param indexOfFirstOperation * @param tempData * @return */ private boolean isFirstOperationTextInBetweenNumber( int indexOfFirstOperation, String tempData) { int tempDataLength = tempData.length(); if (indexOfFirstOperation > 1 && indexOfFirstOperation < (tempDataLength)) { return true; } return false; } /** * * @param data * @return */ private boolean isValidNumber(String data) { try { Double.parseDouble(data); return true; } catch (Exception e) { return false; } } /** * * @param result * @return */ private String removeUnwantedValueInDecimal(String result) { String resutlTmp = result.substring(result.lastIndexOf("."), result.length()); if (this.containNumberNotZero("[1-9]", resutlTmp)) { // **** OF DECIMAL VALUES CONTAIN ANY NUMBER BETWEEN 1 - 9 return result; } else { // *** IF DECIMAL VALUES CONTAIN ZEROS. REMOVE DECIMAL VALUES return result.substring(0, result.lastIndexOf(".")); } } /** * * @param firstOperation * @param tempData * @return * @throws Exception */ private double compute(String firstOperation, String tempData) throws Exception { double firstNumber = Double.parseDouble(tempData.substring(0, tempData.indexOf(firstOperation))); double secondNumber = Double.parseDouble(tempData.substring( tempData.indexOf(firstOperation) + 1, tempData.length())); double result = 0; if (firstOperation.equalsIgnoreCase(CalculatorContentView.ADD)) { result = firstNumber + secondNumber; } else if (firstOperation .equalsIgnoreCase(CalculatorContentView.SUBTRACT)) { result = firstNumber - secondNumber; } else if (firstOperation .equalsIgnoreCase(CalculatorContentView.MUTIPLY)) { result = firstNumber * secondNumber; } else if (firstOperation .equalsIgnoreCase(CalculatorContentView.DIVIDE)) { result = firstNumber / secondNumber; } return result; } /** * Check if need to compute data * * @param theRegex * @param stringToCheck * @return */ private boolean doComputeTotal(String theRegex, String stringToCheck) { boolean computeTotal = false; Pattern pattern = Pattern.compile(theRegex); Matcher matcher = pattern.matcher(stringToCheck); int operationCount = 0; int firstOperationEndIndex = 0; int secondOperationStartIndex = 0; while (matcher.find()) { if (operationCount == 0) { firstOperationEndIndex = matcher.end(); } else if (operationCount == 1) { secondOperationStartIndex = matcher.start(); } operationCount++; } if (operationCount > 1 && firstOperationEndIndex < secondOperationStartIndex) { computeTotal = true; } else if (operationCount > 1 && firstOperationEndIndex >= secondOperationStartIndex) { computeTotal = false; } return computeTotal; } /** * Get the index of first operation * * @param theRegex * @param stringToCheck * @return */ private int indexOfFirstOperation(String theRegex, String stringToCheck) { Pattern pattern = Pattern.compile(theRegex); Matcher matcher = pattern.matcher(stringToCheck); while (matcher.find()) { return matcher.start(); } return 0; } /** * @param theRegex * @param stringToCheck * @return */ private boolean containNumberNotZero(String theRegex, String stringToCheck) { Pattern pattern = Pattern.compile(theRegex); Matcher matcher = pattern.matcher(stringToCheck); while (matcher.find()) { return true; } return false; } /** * * @param isOperationButtonHasClicked * @param tempData * @return */ private boolean allowToAppendOperationText( boolean isOperationButtonHasClicked, String tempData, String operation) { if (tempData.length() <= 0 && isOperationButtonHasClicked && !operation.equalsIgnoreCase(CalculatorContentView.SUBTRACT)) { return false; } return true; } /** * * @param data * @return */ private boolean isFirstTextNegativeSign(String data) { if (data.length() > 0) { if ((data.charAt(0) + "") .equalsIgnoreCase(CalculatorContentView.SUBTRACT)) { return true; } } return false; } /** * * @param data * @return */ private boolean isLastTextOperation(String data) { if (data.length() > 0) { String lastText = data.substring(data.length() - 1, data.length()); if (lastText.equalsIgnoreCase(CalculatorContentView.DOT)) { return true; } } return false; } /** * * @param data * @return */ private boolean isLastTextDot(String data) { if (data.length() > 0) { String lastText = data.substring(data.length() - 1, data.length()); if (lastText.equalsIgnoreCase(CalculatorContentView.ADD) || lastText .equalsIgnoreCase(CalculatorContentView.SUBTRACT) || lastText .equalsIgnoreCase(CalculatorContentView.SUBTRACT) || lastText.equalsIgnoreCase(CalculatorContentView.MUTIPLY)) { return true; } } return false; } /** * * @param data * @return */ private String removePreviousOperationText(String data) { return data.substring(0, data.length() - 2) + data.substring(data.length() - 1, data.length()); } /** * * @param data * @return */ private String removeLastChar(String data) { if (data.length() > 0) { data = data.substring(0, data.length() - 1); } return data; } /** * Check if data contain operation * * @param data * @return */ private boolean doDataContainOperationText(String data) { if (data.length() > 1) { String testData = new String(data.substring(1, data.length())); if (testData.contains(CalculatorContentView.ADD) || testData.contains(CalculatorContentView.SUBTRACT) || testData.contains(CalculatorContentView.MUTIPLY) || testData.contains(CalculatorContentView.DIVIDE)) { return true; } } return false; } }
If you have any comments and suggestions that can improve this android calculator source code, please let us know in the comments section below. Thank you very much!
*Note: The code is working but this write up is not yet in its final form. It will be improved.
12 responses to “Android Calculator Tutorial and Source Code Example”
great
grear
Thanks @ahmedbas:disqus!
The final output video is missing.
Code Of Ninja site is wonderful.
This tutorial was very helpful to build my basics. I was able to build my own app for Play store. Thank you Sir.
App Link : https://goo.gl/oMlqmn
Hello @avinashbg:disqus, thanks for the kind words and sharing your app! It is great to see that you built it with the help of our source code tutorial. :)
Impressed with your story. nice work and love your homepage video playback feature and workstations, i wish I could have one like that.
Hello @mehrajmalik:disqus , thanks for the kind words! Yes, I put a lot of effort in our home page, glad to know some people like you appreciate it. :)
Please subscribe or share our site to one of your friends if you have time. Thank you!
By all means. :)
You do such a great job please keep it up.
Its a good way to get started, you can modify and add new features later on exactly what i did with my Calculator app..thanks code of ninja.
Link : https://goo.gl/oqhLK2
https://uploads.disquscdn.com/images/0628e32dc234dd15c32b215c95dcd3a32f47fdbd6c3a5057f6b65d0142aaefee.png
Thanks for sharing your app. Glad to help you with your project!
Please update expense calculator in android source code