计算器界面如下
一、创建empty activity
二、设置界面
1、values/themes.xml 修改主题
由于默认的主题不太好看,这里可以修改主题,如下所示,修改为
"Theme.MaterialComponents.DayNight.DarkActionBar.Bridge"
这里暂不对其进行解释。
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Base.Theme.Calculator" parent="Theme.MaterialComponents.DayNight.DarkActionBar.Bridge">
<!-- Customize your light theme here. -->
<!-- <item name="colorPrimary">@color/my_light_primary</item> -->
</style>
<style name="Theme.Calculator" parent="Base.Theme.Calculator"/>
</resources>
2、layout/activity_main.xml 设置布局、添加组件
完整代码
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/calculator_background"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:padding="@dimen/calculator_padding"
android:text="@string/app_name"
android:textSize="@dimen/calculator_text_size" />
<TextView
android:id="@+id/calculator_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/white"
android:gravity="end|bottom"
android:lines="@integer/calculator_text_lines"
android:text="@string/calculator_0"
android:textColor="@color/black"
android:textSize="@dimen/calculator_text_size" />
<GridLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:columnCount="4">
<Button
android:id="@+id/calculator_ce"
android:layout_width="0dp"
android:layout_height="@dimen/calculator_button_height"
android:layout_columnWeight="1"
android:layout_margin="@dimen/calculator_button_margin"
android:text="@string/calculator_ce"
android:textSize="@dimen/calculator_button_font_size" />
<Button
android:id="@+id/calculator_division"
android:layout_width="0dp"
android:layout_height="@dimen/calculator_button_height"
android:layout_columnWeight="1"
android:layout_margin="@dimen/calculator_button_margin"
android:text="@string/calculator_division"
android:textSize="@dimen/calculator_button_font_size" />
<Button
android:id="@+id/calculator_multiplication"
android:layout_width="0dp"
android:layout_height="@dimen/calculator_button_height"
android:layout_columnWeight="1"
android:layout_margin="@dimen/calculator_button_margin"
android:text="@string/calculator_multiplication"
android:textSize="@dimen/calculator_button_font_size" />
<Button
android:id="@+id/calculator_empty"
android:layout_width="0dp"
android:layout_height="@dimen/calculator_button_height"
android:layout_columnWeight="1"
android:layout_margin="@dimen/calculator_button_margin"
android:text="@string/calculator_empty"
android:textSize="@dimen/calculator_button_font_size" />
<Button
android:id="@+id/calculator_7"
android:layout_width="0dp"
android:layout_height="@dimen/calculator_button_height"
android:layout_columnWeight="1"
android:layout_margin="@dimen/calculator_button_margin"
android:text="@string/calculator_7"
android:textSize="@dimen/calculator_button_font_size" />
<Button
android:id="@+id/calculator_8"
android:layout_width="0dp"
android:layout_height="@dimen/calculator_button_height"
android:layout_columnWeight="1"
android:layout_margin="@dimen/calculator_button_margin"
android:text="@string/calculator_8"
android:textSize="@dimen/calculator_button_font_size" />
<Button
android:id="@+id/calculator_9"
android:layout_width="0dp"
android:layout_height="@dimen/calculator_button_height"
android:layout_columnWeight="1"
android:layout_margin="@dimen/calculator_button_margin"
android:text="@string/calculator_9"
android:textSize="@dimen/calculator_button_font_size" />
<Button
android:id="@+id/calculator_add"
android:layout_width="0dp"
android:layout_height="@dimen/calculator_button_height"
android:layout_columnWeight="1"
android:layout_margin="@dimen/calculator_button_margin"
android:text="@string/calculator_add"
android:textSize="@dimen/calculator_button_font_size" />
<Button
android:id="@+id/calculator_4"
android:layout_width="0dp"
android:layout_height="@dimen/calculator_button_height"
android:layout_columnWeight="1"
android:layout_margin="@dimen/calculator_button_margin"
android:text="@string/calculator_4"
android:textSize="@dimen/calculator_button_font_size" />
<Button
android:id="@+id/calculator_5"
android:layout_width="0dp"
android:layout_height="@dimen/calculator_button_height"
android:layout_columnWeight="1"
android:layout_margin="@dimen/calculator_button_margin"
android:text="@string/calculator_5"
android:textSize="@dimen/calculator_button_font_size" />
<Button
android:id="@+id/calculator_6"
android:layout_width="0dp"
android:layout_height="@dimen/calculator_button_height"
android:layout_columnWeight="1"
android:layout_margin="@dimen/calculator_button_margin"
android:text="@string/calculator_6"
android:textSize="@dimen/calculator_button_font_size" />
<Button
android:id="@+id/calculator_subtraction"
android:layout_width="0dp"
android:layout_height="@dimen/calculator_button_height"
android:layout_columnWeight="1"
android:layout_margin="@dimen/calculator_button_margin"
android:text="@string/calculator_subtraction"
android:textSize="@dimen/calculator_button_font_size" />
<Button
android:id="@+id/calculator_1"
android:layout_width="0dp"
android:layout_height="@dimen/calculator_button_height"
android:layout_columnWeight="1"
android:layout_margin="@dimen/calculator_button_margin"
android:text="@string/calculator_1"
android:textSize="@dimen/calculator_button_font_size" />
<Button
android:id="@+id/calculator_2"
android:layout_width="0dp"
android:layout_height="@dimen/calculator_button_height"
android:layout_columnWeight="1"
android:layout_margin="@dimen/calculator_button_margin"
android:text="@string/calculator_2"
android:textSize="@dimen/calculator_button_font_size" />
<Button
android:id="@+id/calculator_3"
android:layout_width="0dp"
android:layout_height="@dimen/calculator_button_height"
android:layout_columnWeight="1"
android:layout_margin="@dimen/calculator_button_margin"
android:text="@string/calculator_3"
android:textSize="@dimen/calculator_button_font_size" />
<ImageButton
android:id="@+id/evolution"
android:layout_width="0dp"
android:layout_height="@dimen/calculator_button_height"
android:layout_columnWeight="1"
android:layout_margin="@dimen/calculator_button_margin"
android:scaleType="fitCenter"
android:src="@drawable/evolution" />
<Button
android:id="@+id/calculator_reciprocal"
android:layout_width="0dp"
android:layout_height="@dimen/calculator_button_height"
android:layout_columnWeight="1"
android:layout_margin="@dimen/calculator_button_margin"
android:text="@string/calculator_reciprocal"
android:textSize="@dimen/calculator_button_font_size" />
<Button
android:id="@+id/calculator_0"
android:layout_width="0dp"
android:layout_height="@dimen/calculator_button_height"
android:layout_columnWeight="1"
android:layout_margin="@dimen/calculator_button_margin"
android:text="@string/calculator_0"
android:textSize="@dimen/calculator_button_font_size" />
<Button
android:id="@+id/calculator_point"
android:layout_width="0dp"
android:layout_height="@dimen/calculator_button_height"
android:layout_columnWeight="1"
android:layout_margin="@dimen/calculator_button_margin"
android:text="@string/calculator_point"
android:textSize="@dimen/calculator_button_font_size" />
<Button
android:id="@+id/calculator_equal"
android:layout_width="0dp"
android:layout_height="@dimen/calculator_button_height"
android:layout_columnWeight="1"
android:layout_margin="@dimen/calculator_button_margin"
android:text="@string/calculator_equal"
android:textSize="@dimen/calculator_button_font_size" />
</GridLayout>
</LinearLayout>
接下来分块讲解:
2.1、线性布局
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/calculator_background"
android:orientation="vertical">
</LinearLayout>
在最外层是线性布局,设置宽高属性为"match_parent",匹配界面;并通过background设置背景颜色,将颜色的值定义在colors.xml文件中;属性orientation设置对齐方式,"vertical"为垂直方向; Android学习笔记(五):设置视图宽高、间距、对齐方式https://blog.youkuaiyun.com/CJ_study/article/details/134109289?spm=1001.2014.3001.5501
2.2、TextView 显示文本内容
<TextView
android:id="@+id/calculator_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/white"
android:gravity="end|bottom"
android:lines="@integer/calculator_text_lines"
android:text="@string/calculator_0"
android:textColor="@color/black"
android:textSize="@dimen/calculator_text_size" />
这个TextView组件主要是用来输入内容和计算结果。通过id="@+id/calculator_content"给这个组件添加id,在java逻辑就可以对它进行操作;设置宽高,宽"match_parent"匹配上级布局,高"wrap_content"自适应内容设置;通过gravity="end|bottom"属性设置内容对齐方式,这里采用右下的对齐方式;lines属性设置显示的行数,我在integers.xml中定义了行数3;text属性设置显示默认内容,可以通过java逻辑修改其内容;textColor设置字体颜色,textSize设置字体尺寸,并在dimens.xml文件中统一定义各种尺寸;
2.3、GridLayout 网格布局
<GridLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:columnCount="4">
</GridLayout>
这里网格布局主要通过columnCoun设置列数,行数会根据组件数量自动添加;
2.4、Button 按钮
<Button
android:id="@+id/calculator_ce"
android:layout_width="0dp"
android:layout_height="@dimen/calculator_button_height"
android:layout_columnWeight="1"
android:layout_margin="@dimen/calculator_button_margin"
android:text="@string/calculator_ce"
android:textSize="@dimen/calculator_button_font_size" />
通过id属性添加id,可以在java逻辑中对其进行操作;宽设置为0,并通过layout_columnWeight属性设置其权重,这样在网格布局中各个组件的宽度根据权重平分;然后高度也设置为定值,这样每个组件就一样的大小;layout_margin属性设置当前组件与上一级或平级组件之间的间隔;
2.5、ImageButton 图形按钮
<ImageButton
android:id="@+id/evolution"
android:layout_width="0dp"
android:layout_height="@dimen/calculator_button_height"
android:layout_columnWeight="1"
android:layout_margin="@dimen/calculator_button_margin"
android:scaleType="fitCenter"
android:src="@drawable/evolution" />
因为根号需要通过图像显示,所以这里用到图形按钮,所以图形按钮的一个特点就是文字无法描述的内容可以通过图形的形式展示;scaleType属性设置图形的缩放比例;src属性设置图形的来源,图片放在drawable/ 目录下。
3、values/strings.xml 定义内容
<resources>
<string name="app_name">Calculator</string>
<string name="calculator_ce">CE</string>
<string name="calculator_division">÷</string>
<string name="calculator_multiplication">×</string>
<string name="calculator_empty">C</string>
<string name="calculator_add">+</string>
<string name="calculator_subtraction">-</string>
<string name="calculator_equal">=</string>
<string name="calculator_point">.</string>
<string name="calculator_reciprocal">1/X</string>
<string name="calculator_0">0</string>
<string name="calculator_1">1</string>
<string name="calculator_2">2</string>
<string name="calculator_3">3</string>
<string name="calculator_4">4</string>
<string name="calculator_5">5</string>
<string name="calculator_6">6</string>
<string name="calculator_7">7</string>
<string name="calculator_8">8</string>
<string name="calculator_9">9</string>
</resources>
4、values/colors.xml 定义颜色
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="black">#FF000000</color>
<color name="white">#FFFFFFFF</color>
<color name="calculator_background">#FFEEEEEE</color>
</resources>
5、values/dimens.xml 定义尺寸
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="calculator_text_size">30sp</dimen>
<dimen name="calculator_padding">5dp</dimen>
<dimen name="calculator_button_font_size">30sp</dimen>
<dimen name="calculator_button_height">75dp</dimen>
<dimen name="calculator_button_margin">3dp</dimen>
</resources>
可能默认创建的项目中没有这个文件,手动新建一个xml文件并命名为dimens;
6、values/integers.xml 定义数字常量
<?xml version="1.0" encoding="utf-8"?>
<resources>
<integer name="calculator_text_lines">3</integer>
</resources>
定义这些值在这些文件中统一管理,方便修改。
三、MainActivity 实现java逻辑
完整代码
package com.example.calculator;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity
implements View.OnClickListener {
/* 简易计算器
* 最多只支持2个数字的计算
* 不符合格式的数字不进行计算
*/
private final String TAG = "Calculator";
private Button m_btn_CE; // 回退键
private Button m_btn_division; // 除法键
private Button m_btn_multiplication; // 乘法键
private Button m_btn_empty; // 清空键
private Button m_btn_add; // 加法键
private Button m_btn_subtraction; // 减法键
private ImageButton m_image_btn_evolution; // 根号键
private Button m_btn_reciprocal; // 倒数键
private Button m_btn_point; // 小数点键
private Button m_btn_equal; // 等于键
private Button m_btn_0;
private Button m_btn_1;
private Button m_btn_2;
private Button m_btn_3;
private Button m_btn_4;
private Button m_btn_5;
private Button m_btn_6;
private Button m_btn_7;
private Button m_btn_8;
private Button m_btn_9;
private TextView m_textview_content; // 显示内容
private StringBuilder m_sb_ShowText; // 保存输入内容
private StringBuilder m_sb_ans; // 保存计算结果
private int m_method_index; // 保存计算符号位置
@Override
protected void onCreate(Bundle savedInstanceState) { // 进入应用
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initData(); // 初始化数据
initUI(); // 加载初始化界面布局
setClickListener(); // 设置监听
}
private void initData() { // 初始化数据
m_sb_ShowText = new StringBuilder();
m_sb_ans = new StringBuilder();
m_method_index = -1; /* -1 表示未添加计算符号 */
Log.i(TAG, "initData is ok!");
}
private void initUI() { // 加载初始化界面布局
/* 其实这里可以进行优化一下代码
* 如:findViewById(R.id.calculator_ce).setOnClickListener(this);
* 因为整个程序中都没有对m_btn_CE进行其它操作,所以可以不用单独定义一个变量;
* 只需要对这个按钮设置监听即可;
* m_textview_content 在后面需要操作,所以要单独定义成一个变量 */
m_btn_CE = findViewById(R.id.calculator_ce);
m_btn_division = findViewById(R.id.calculator_division);
m_btn_multiplication = findViewById(R.id.calculator_multiplication);
m_btn_empty = findViewById(R.id.calculator_empty);
m_btn_add = findViewById(R.id.calculator_add);
m_btn_subtraction = findViewById(R.id.calculator_subtraction);
m_image_btn_evolution = findViewById(R.id.evolution);
m_btn_reciprocal = findViewById(R.id.calculator_reciprocal);
m_btn_point = findViewById(R.id.calculator_point);
m_btn_equal = findViewById(R.id.calculator_equal);
m_btn_0 = findViewById(R.id.calculator_0);
m_btn_1 = findViewById(R.id.calculator_1);
m_btn_2 = findViewById(R.id.calculator_2);
m_btn_3 = findViewById(R.id.calculator_3);
m_btn_4 = findViewById(R.id.calculator_4);
m_btn_5 = findViewById(R.id.calculator_5);
m_btn_6 = findViewById(R.id.calculator_6);
m_btn_7 = findViewById(R.id.calculator_7);
m_btn_8 = findViewById(R.id.calculator_8);
m_btn_9 = findViewById(R.id.calculator_9);
m_textview_content = findViewById(R.id.calculator_content);
Log.i(TAG, "initUI is ok!");
}
private void setClickListener() { // 设置监听
m_btn_CE.setOnClickListener(this);
m_btn_division.setOnClickListener(this);
m_btn_multiplication.setOnClickListener(this);
m_btn_empty.setOnClickListener(this);
m_btn_add.setOnClickListener(this);
m_btn_subtraction.setOnClickListener(this);
m_image_btn_evolution.setOnClickListener(this);
m_btn_reciprocal.setOnClickListener(this);
m_btn_point.setOnClickListener(this);
m_btn_equal.setOnClickListener(this);
m_btn_0.setOnClickListener(this);
m_btn_1.setOnClickListener(this);
m_btn_2.setOnClickListener(this);
m_btn_3.setOnClickListener(this);
m_btn_4.setOnClickListener(this);
m_btn_5.setOnClickListener(this);
m_btn_6.setOnClickListener(this);
m_btn_7.setOnClickListener(this);
m_btn_8.setOnClickListener(this);
m_btn_9.setOnClickListener(this);
Log.i(TAG, "setClickListener is ok!");
}
@Override
public void onClick(View view) {
// 通过view.getId()获取到当前点击的按钮的id;并进行对应的操作
if (view.getId() == R.id.calculator_empty) { // 清空操作
emptyText();
} else if (view.getId() == R.id.calculator_ce) { // 回退操作
if (m_sb_ShowText.length() != 0) {
CEFreshMethod(m_sb_ShowText.charAt(m_sb_ShowText.length() - 1));
m_sb_ShowText.deleteCharAt(m_sb_ShowText.length() - 1);
} else
emptyText();
} else if (view.getId() == R.id.calculator_equal) { // 等于操作
try {
double ans = equal();
emptyText();
m_sb_ans.append(ans);
} catch (Exception e) {
Log.e(TAG, "equal is failed!");
return;
}
} else if (view.getId() == R.id.calculator_1 ||
view.getId() == R.id.calculator_2 ||
view.getId() == R.id.calculator_3 ||
view.getId() == R.id.calculator_4 ||
view.getId() == R.id.calculator_5 ||
view.getId() == R.id.calculator_6 ||
view.getId() == R.id.calculator_7 ||
view.getId() == R.id.calculator_8 ||
view.getId() == R.id.calculator_9 ||
view.getId() == R.id.calculator_0 ||
view.getId() == R.id.calculator_point) { // 数字和小数点直接添加到内容中
appendText(((TextView) view).getText().toString());
} else if (view.getId() == R.id.evolution && isDouble()) { // 根号操作
/* 根号操作需要判断当前内容是否为数字
* 计算完成后,清空内容,添加答案 */
double num = Double.parseDouble(m_sb_ShowText.toString());
double evolution_ans = Math.sqrt(num);
emptyText();
m_sb_ans.append(evolution_ans);
} else if (view.getId() == R.id.calculator_add ||
view.getId() == R.id.calculator_division ||
view.getId() == R.id.calculator_multiplication ||
view.getId() == R.id.calculator_subtraction) { // 添加计算符号
/* 这里没有限制添加计算符号
* 但记录了符号的位置
* 如果m_method_index == -1 说明未添加符号,或者符号被回退,因此记录当前符号的位置;
* 如果m_method_index == 0 说明在内容最前面添加了一个符号,考虑到可能是正负号,
* 因此再记录当前符号位置作为计算符号;
* 如果m_method_index > 0 说明添加了计算符号,后面再添加的符号都属于非法符号,不做记录 */
appendText(((TextView) view).getText().toString());
if (m_method_index <= 0) {
m_method_index = m_sb_ShowText.length() - 1;
Log.i(TAG, "current m_method_index = " + m_method_index);
}
} else if (view.getId() == R.id.calculator_reciprocal && isDouble()) { // 倒数操作
double reciprocal_ans = 1 / Double.parseDouble(m_sb_ShowText.toString());
emptyText();
m_sb_ans.append(reciprocal_ans);
}
flashText();
}
private void CEFreshMethod(char charAt) {
/* 回退时,刷新计算符号的位置;
* 考虑到可能输入多个符号,只有当回退了m_method_index记录的符号时才刷新为 -1
* */
if (m_method_index != m_sb_ShowText.length() - 1)
return;
switch (charAt) {
case '+':
case '-':
case '×':
case '÷':
m_method_index = -1;
Log.i(TAG, "current m_method_index = " + m_method_index);
break;
}
}
private boolean isDouble() {
/* 判断当前输入内容是否可以转换为数字
* 这里用到了try,如果解析到的内容非数字,会抛出异常
* 异常处理返回false
* 我这里采用偷懒的方法,也可以编写判断是否为数字的方法 */
try {
Double.parseDouble(m_sb_ShowText.toString());
return true;
} catch (NumberFormatException e) {
Log.e(TAG, "current m_sb_ShowText is no Double, (" + m_sb_ShowText.toString() + ")");
return false;
}
}
private double equal() {
/* 计算结果
* 获取第一个数字、计算符号、第二个数字*/
double first = Double.parseDouble(m_sb_ShowText.substring(0, m_method_index));
char method = m_sb_ShowText.charAt(m_method_index);
double second = Double.parseDouble(m_sb_ShowText.substring(m_method_index + 1, m_sb_ShowText.length()));
switch (method) {
case '+':
return first + second;
case '-':
return first - second;
case '×':
return first * second;
case '÷':
return first / second;
default:
Log.w(TAG, "the method is other!");
return 0;
}
}
private void emptyText() {
/* 清空内容、结果、并初始化符号位置 */
m_sb_ShowText.setLength(0);
m_sb_ans.setLength(0);
m_method_index = -1;
Log.i(TAG, "emptyText is ok!");
}
private void appendText(String text) {
/* 添加输入内容到m_sb_ShowText中显示出来 */
m_sb_ShowText.append(text);
Log.i(TAG, "current m_sb_ShowText = " + m_sb_ShowText.toString());
}
private void flashText() {
/* setText设置内容TextView显示内容
* 刷新输出内容,在m_textview_content界面显示出来
* m_sb_ShowText不等于0,说明还在输入,显示输入内容
* m_sb_ans不等于0,说明得到结果,输出结果
* 都为0,默认输出显示 0 */
if (m_sb_ShowText.length() != 0)
m_textview_content.setText(m_sb_ShowText.toString());
else if (m_sb_ans.length() != 0)
m_textview_content.setText(m_sb_ans.toString());
else
m_textview_content.setText("0");
}
}
代码中已经进行了部分注解,应该可以帮助你理解,这里就不再分步解释了;当然,这个项目中还存在一些BUG,大家可以对其进行优化改进;大家也可以自己尝试去编写,用自己的逻辑实现;其次,这个项目主要是加深前几章内容的理解,大家不要太深究其逻辑实现,在后续的学习中再进一步探究。
Android学习笔记(七):按钮Button的常用方法https://blog.youkuaiyun.com/CJ_study/article/details/135581554?spm=1001.2014.3001.5501Android学习笔记(八):ImageView加载图片与ImageButton图形按钮
https://blog.youkuaiyun.com/CJ_study/article/details/135714563?spm=1001.2014.3001.5501
1、Log 日志
我在代码中添加了部分Log日志,可以方便在实现逻辑中,遇到错误或者逻辑不通时,可以通过Log日志帮助我们分析定位代码逻辑。如下图所示,因为定义了TAG为calculator,可以筛选需要的日志查看,i可以表示重要日志,e表示错误日志,w表示警告日志;希望大家可以学会用日志分析,在工作中是一个重要的技能。
四、总结
本章实现了一个训练项目--简易计算器,可以对前几章学习内容进行复习巩固,加深理解。