Android开发学习日志(三)
上一篇中我们已经把界面给整出来了,先运行一下看看。

虚拟机启动的有点慢,原来是C盘空间不够了。。。唉,以后系统盘分区至少得50G才行,这么多开发环境太占空间了。注:系统盘可用空间过少,或者内存不够的情况下虚拟机可能不能正常启动。好了,虚拟机自动开机,系统启动后会自动运行我们的程序:

乍一看还挺大气的,发现个问题,标题咋不对呢。xx的,明明图形界面上是对的,囧。这里的MainActivity应该是我在新建Activity画面填入的Title。。。那么怎么办呢,既然这个名字适合Activity有关的,那我就很自然的想到了打开MainActivity.java看了看,很遗憾,除了两个重写方法,我们没有发现任何关于设置Title的痕迹。在上一篇我曾经介绍过AndroidManifest.xml这个文件,不知道大家记不记得,“我们添加的每一个Activity,都要在这个文件中注册才能正常使用。”哈哈,可能就是这里的问题,打开看一下,果然,因为我们开始添加的MainActivity程序自动给我们注册到了AndroidManifest.xml中,所以我们不晓得发生了什么。

喏,就红框里面的属性最可疑了,@string 的意思就是取资源文件中的字符串的意思,斜杠后面是key,字符串的资源文件就是res/values/strings.xml(我们可以在里面新增自己用到的字符串),我们可以打开看一下,果然找到了“ <string name="title_activity_main">MainActivity</string>”,我们改一下,改成中文得了,计算器。再次启动虚拟机,终于变过来了。
下面我们就要实打实的开始写代码了。
我先从Activity说起,我们打开MainActivity.java,可以看到只有两个重写方法onCreate和onCreateOptionsMenu,onCreate这个方法,我目前掌握的资料是当Activity创建的时候会执行,而这个创建包括启动程序、切换窗口、横竖屏切换等等。所以我们窗口的一些初始化工作就可以放在onCreate中调用。onCreateOptionsMenu这个自然就是创建应用程序目录了,后面我们会讲到。
这里还得说一下onCreate的参数,“Bundle savedInstanceState”,Bundle类型的是KV键值对存储结构的MAP,参数名就很好理解了,savedInstanceState,保存的实例状态,这里面存储的就是上次关闭Activity时的画面状态,比如我们看小说看到某一页退出了,下次再看的时候还停留在上次看的位置。这就是从savedInstanceState中去除了上次保存的信息,当然了,要想取出信息,就得在Activity关闭的时候保存信息才行(重写Activity的saveInsanceState方法,参数同样是Bundle savedInstanceState)。
迫不及待想要赶快实现安卓平台上的第一个程序了,这确实将是我写的第一个安卓App,严格意义上讲是第一个手机App。。。我们启动了之前的程序后发现点击按钮并没有反映,好吧,我们确实没有给这些按钮注册单击事件。。。好吧,下面我们给按钮注册一下他们的单击事件。笔者设计模式不怎么熟练,感觉按钮的注册用策略模式比较好一点,不知道各位有什么好的见解,欢迎指出。(这里不讲解设计模式)
前面说过@string是引用资源文件中的字符串,谷歌也是推崇使用资源文件而不使用硬编码的,所以我这里把activity_main.xml中的android:text属性的硬编码的字符串都换成了@string引用,同时在strings.xml中添加相应的键值对。

源码:http://pan.baidu.com/share/link?shareid=300640&uk=2684548402
下面讲解一下主要代码:
按钮抽象类(CalculatorButton.java):
1 package com.nullpointer.calculator.buttons; 2 3 /** 4 * 点击电钮抽象类 5 * 6 * @author Null Pointer 7 * 8 */ 9 public abstract class CalculatorButton { 10 11 /** 12 * 获取点击按钮执行结果 13 * 14 * @param originalText 15 * 原始数据 16 * @param buttonValue 17 * 按钮参数 18 * @return 执行结果 19 */ 20 public abstract String getResult(String originalText, String buttonValue); 21 22 }
按钮分类集合(ButtonCellection.java):
1 package com.nullpointer.calculator.buttons; 2 3 import java.util.Arrays; 4 import java.util.List; 5 6 public class ButtonCollection { 7 /** 数字键集合 */ 8 public static final List<String> NUMBER_BUTTONS = Arrays.asList("1", "2", 9 "3", "4", "5", "6", "7", "8", "9", "0", "00"); 10 11 /** 运算符键集合 */ 12 public static final List<String> OPERATOR_BUTTONS = Arrays.asList("+", "-", 13 "×", "÷", "."); 14 15 /** 功能键集合 */ 16 public static final List<String> FUNCTION_BUTTONS = Arrays.asList("Clear", 17 "←", "="); 18 }
具体类型的按钮:
数字键(NumberButton.java):
1 package com.nullpointer.calculator.buttons; 2 3 /** 4 * 数字按钮 5 * 6 * @author Null Pointer 7 * 8 */ 9 public class NumberButton extends CalculatorButton { 10 11 /** 12 * 获取点击按钮执行结果 13 * 14 * @param originalText 15 * 原始数据 16 * @param buttonValue 17 * 按钮参数 18 * @return 执行结果 19 */ 20 @Override 21 public String getResult(String originalText, String buttonValue) { 22 return originalText + buttonValue; 23 } 24 25 }
运算符键(OperatorButton.java):
1 package com.nullpointer.calculator.buttons; 2 3 /** 4 * 运算符按钮 5 * 6 * @author Null Pointer 7 * 8 */ 9 public class OperatorButton extends CalculatorButton { 10 11 /** 12 * 获取点击按钮执行结果 13 * 14 * @param originalText 15 * 原始数据 16 * @param buttonValue 17 * 按钮参数 18 * @return 执行结果 19 */ 20 @Override 21 public String getResult(String originalText, String buttonValue) { 22 return originalText + buttonValue; 23 } 24 25 }
功能键(FunctionButton.java):
1 package com.nullpointer.calculator.buttons; 2 3 import java.math.BigDecimal; 4 import java.util.ArrayList; 5 import java.util.Arrays; 6 import java.util.List; 7 8 import com.nullpointer.calculator.common.constants.AppConstants; 9 import com.nullpointer.calculator.common.utils.StringUtils; 10 11 /** 12 * 功能按钮 13 * 14 * @author Null Pointer 15 * 16 */ 17 public class FunctionButton extends CalculatorButton { 18 19 /** 20 * 获取点击按钮执行结果 21 * 22 * @param originalText 23 * 原始数据 24 * @param buttonValue 25 * 按钮参数 26 * @return 执行结果 27 */ 28 @Override 29 public String getResult(String originalText, String buttonValue) { 30 if (AppConstants.CLEAR_STRING.equals(buttonValue)) { 31 return AppConstants.EMPTY_STRING; 32 } else if (AppConstants.DELETE_STRING.equals(buttonValue)) { 33 if (StringUtils.isNotEmpty(originalText)) { 34 return originalText.substring(0, originalText.length() - 1); 35 } else { 36 return AppConstants.EMPTY_STRING; 37 } 38 } else if (AppConstants.EQUAL_STRING.equals(buttonValue)) { 39 return eval(originalText); 40 } else { 41 return AppConstants.EMPTY_STRING; 42 } 43 } 44 45 /** 46 * 计算结果(此方法为计算表达式的结果,先计算乘除,再计算加减, 此算法有很多可以优化的地方, 47 * 可能存在未知Bug,未经过全面测试,仅作演示使用) 48 * 49 * @param expression 50 * 表达式 51 * @return 计算结果 52 */ 53 private String eval(String expression) { 54 List<String> items = new ArrayList<String>(Arrays.asList(expression.split("\\+|\\-|×|÷"))); 55 56 char[] operators = expression.replaceAll("[^\\+\\-×÷]", AppConstants.EMPTY_STRING).toCharArray(); 57 58 if (items.size() != operators.length + 1) { 59 return AppConstants.ERROR_MESSAGE; 60 } 61 62 double result = 0d; 63 // 计算乘除 64 for (int i = operators.length - 1; i >= 0; i--) { 65 switch (operators[i]) { 66 case AppConstants.OPERATOR_MULTI: 67 result = this.multi(Double.valueOf(items.get(i)), Double.valueOf(items.get(i + 1))); 68 items.remove(i + 1); 69 items.set(i, String.valueOf(result)); 70 break; 71 72 case AppConstants.OPERATOR_DIVIDED: 73 result = this.divided(Double.valueOf(items.get(i)), Double.valueOf(items.get(i + 1))); 74 items.remove(i + 1); 75 items.set(i, String.valueOf(result)); 76 break; 77 78 default: 79 break; 80 } 81 } 82 if (items.size() == 1) { 83 return items.get(0); 84 } 85 86 // 计算加减 87 operators = expression.replaceAll("[^\\+\\-]", AppConstants.EMPTY_STRING).toCharArray(); 88 for (int i = operators.length - 1; i >= 0; i--) { 89 switch (operators[i]) { 90 case AppConstants.OPERATOR_PLUS: 91 result = this.plus(Double.valueOf(items.get(i)), Double.valueOf(items.get(i + 1))); 92 items.remove(i + 1); 93 items.set(i, String.valueOf(result)); 94 break; 95 96 case AppConstants.OPERATOR_MINUS: 97 result = this.minus(Double.valueOf(items.get(i)), Double.valueOf(items.get(i + 1))); 98 items.remove(i + 1); 99 items.set(i, String.valueOf(result)); 100 break; 101 102 default: 103 break; 104 } 105 } 106 if (items.size() == 1) { 107 return items.get(0); 108 } else { 109 return AppConstants.ERROR_MESSAGE; 110 } 111 } 112 113 private double plus(double v1, double v2) { 114 BigDecimal b1 = new BigDecimal(Double.toString(v1)); 115 BigDecimal b2 = new BigDecimal(Double.toString(v2)); 116 return b1.add(b2).doubleValue(); 117 } 118 119 private double minus(double v1, double v2) { 120 BigDecimal b1 = new BigDecimal(Double.toString(v1)); 121 BigDecimal b2 = new BigDecimal(Double.toString(v2)); 122 return b1.subtract(b2).doubleValue(); 123 } 124 125 private double multi(double v1, double v2) { 126 BigDecimal b1 = new BigDecimal(Double.toString(v1)); 127 BigDecimal b2 = new BigDecimal(Double.toString(v2)); 128 return b1.multiply(b2).doubleValue(); 129 } 130 131 private double divided(double v1, double v2) { 132 BigDecimal b1 = new BigDecimal(Double.toString(v1)); 133 BigDecimal b2 = new BigDecimal(Double.toString(v2)); 134 return b1.divide(b2, 10, BigDecimal.ROUND_HALF_UP).doubleValue(); 135 } 136 }
注:求表达式值的算法可能存在bug,此处做教程使用,请勿直接正式使用。浮点数、除数不能为0等都未做处理。
常量类(AppConstants.java):
1 package com.nullpointer.calculator.common.constants; 2 3 /** 4 * 常量 5 * 6 * @author Null Pointer 7 * 8 */ 9 public class AppConstants { 10 11 /** 12 * 空字符串 13 */ 14 public static final String EMPTY_STRING = ""; 15 16 /** 17 * 清除按钮 18 */ 19 public static final String CLEAR_STRING = "Clear"; 20 21 /** 22 * 删除按钮 23 */ 24 public static final String DELETE_STRING = "←"; 25 26 /** 27 * 等号按钮 28 */ 29 public static final String EQUAL_STRING = "="; 30 31 /** 32 * 错误信息 33 */ 34 public static final String ERROR_MESSAGE = "Error!"; 35 36 /** 37 * 加号 38 */ 39 public static final char OPERATOR_PLUS = '+'; 40 41 /** 42 * 减号 43 */ 44 public static final char OPERATOR_MINUS = '-'; 45 46 /** 47 * 乘号 48 */ 49 public static final char OPERATOR_MULTI = '×'; 50 51 /** 52 * 除号 53 */ 54 public static final char OPERATOR_DIVIDED = '÷'; 55 56 }
字符串工具类(StringUtils.java):
1 package com.nullpointer.calculator.common.utils; 2 3 /** 4 * 字符串工具类 5 * 6 * @author Null Pointer 7 * 8 */ 9 public class StringUtils { 10 11 /** 12 * 判断字符串是否为空 13 * 14 * @param value 15 * 值 16 * @return 判断结果 17 */ 18 public static boolean isNotEmpty(String value) { 19 if (value != null && value.trim().length() > 0) { 20 return true; 21 } 22 return false; 23 } 24 }
Button上下文类(ButtonContext.java):
1 package com.nullpointer.calculator; 2 3 import com.nullpointer.calculator.buttons.ButtonCollection; 4 import com.nullpointer.calculator.buttons.CalculatorButton; 5 import com.nullpointer.calculator.buttons.FunctionButton; 6 import com.nullpointer.calculator.buttons.NumberButton; 7 import com.nullpointer.calculator.buttons.OperatorButton; 8 9 /** 10 * 按钮上下文 11 * 12 * @author Null Pointer 13 * 14 */ 15 public class ButtonContext { 16 17 /** Button抽象类 */ 18 private CalculatorButton button = null; 19 20 /** 键值 */ 21 private String buttonValue; 22 23 /** 24 * 构造函数 25 * 26 * @param buttonValue 27 * 键值 28 * @throws Exception 29 * 异常 30 */ 31 public ButtonContext(String buttonValue) throws Exception { 32 if (ButtonCollection.NUMBER_BUTTONS.contains(buttonValue)) { 33 button = new NumberButton(); 34 } else if (ButtonCollection.OPERATOR_BUTTONS.contains(buttonValue)) { 35 button = new OperatorButton(); 36 } else if (ButtonCollection.FUNCTION_BUTTONS.contains(buttonValue)) { 37 button = new FunctionButton(); 38 } else { 39 throw new Exception(); 40 } 41 this.buttonValue = buttonValue; 42 } 43 44 /** 45 * 获取结果 46 * 47 * @param originalText 48 * 原始值 49 * @return 反馈结果 50 */ 51 public String getResult(String originalText) { 52 return this.button.getResult(originalText, this.buttonValue); 53 } 54 }
以上的都是业务代码,我将会着重讲一下下面这个类,也就是MainActivity.java。
1 package com.nullpointer.calculator; 2 3 import android.app.Activity; 4 import android.os.Bundle; 5 import android.view.Gravity; 6 import android.view.Menu; 7 import android.view.View; 8 import android.view.View.OnClickListener; 9 import android.widget.Button; 10 import android.widget.EditText; 11 import android.widget.LinearLayout; 12 13 /** 14 * 15 * @author Null Pointer 16 * 17 */ 18 public class MainActivity extends Activity { 19 20 /** 文本编辑框 */ 21 EditText inputText; 22 23 @Override 24 public void onCreate(Bundle savedInstanceState) { 25 super.onCreate(savedInstanceState); 26 setContentView(R.layout.activity_main); 27 // 获取文本框 28 inputText = (EditText) findViewById(R.id.inputString); 29 // 隐藏光标 30 inputText.setCursorVisible(false); 31 // 设置文本右对齐 32 inputText.setGravity(Gravity.CENTER | Gravity.RIGHT); 33 // 注册事件 34 this.registEvent(R.id.LayoutMain); 35 } 36 37 @Override 38 public boolean onCreateOptionsMenu(Menu menu) { 39 getMenuInflater().inflate(R.menu.activity_main, menu); 40 return true; 41 } 42 43 /** 44 * 注册事件 45 * 46 * @param id 47 * 布局控件id 48 */ 49 private void registEvent(int id) { 50 // 根据id获取控件 51 LinearLayout layout = (LinearLayout) findViewById(id); 52 // 对于容器控件,循环遍历内部控件 53 for (int i = 0, j = layout.getChildCount(); i < j; i++) { 54 // 找到是Button的控件 55 if (layout.getChildAt(i) instanceof Button) { 56 // 注册点击事件 57 ((Button) layout.getChildAt(i)).setOnClickListener(new OnClickListener() { 58 // 实现点击的具体逻辑 59 public void onClick(View v) { 60 try { 61 ButtonContext context = new ButtonContext(((Button) v).getText().toString()); 62 // 设置显示文本 63 inputText.setText(context.getResult(inputText.getText().toString())); 64 } catch (Exception e) { 65 e.printStackTrace(); 66 } 67 68 } 69 }); 70 } else if (layout.getChildAt(i) instanceof LinearLayout) { 71 registEvent(layout.getChildAt(i).getId()); 72 } 73 } 74 } 75 76 }
从27行开始时我们自己写的代码,findViewById是父类Activity实现的方法,可以直接用Id来获取控件,它的参数,我们之前说过,图形界面上声明了@id的控件的ID将会被编译进R.java,这里我们直接这样使用是不是很方便啊。当然获取到之后还要做一下强制类型转换,其他的有注释并且显而易见的代码我就不想细说了,要设置或者获取控件的各种属性可以直接查开发者文档,SDK目录下就已经包含了本地的开发者文档,android-sdk_r20.0.1-windows/android-sdk-windows/docs/guide/components/index.html,不同版本SDK目录可能不同。
我们有很多的Button,我们要给每个Button注册点击事件,当然我们可以根据Id来获取到每个Button,然后再给它们注册点击事件,这对于处理程序没有通用点,或者Button数量较少的程序是无可厚非的,,但是我们的计算器程序显然Button多了点。。。并且很多都是相同的逻辑,所以我分成了三类(如果你喜欢,也可以分成两类。。),所以我这里使用了递归来遍历画面上所有的控件,如果是Button的话就给他们注册统一的事件处理程序。这里使用的是匿名类,实现OnClickListener接口的onClick方法,你也可以不适用匿名类的方式(关于java的语法就不做过多介绍了),onClick的参数是出发事件的对象,也就是我们点击的Button,一般画面上的所有项目都是继承自View类。因为接受点击事件的控件不只有Button,所以此处无法具体到哪种控件,我们需要自己做强制类型转换。
我们运行一下我们的程序,此时已经可以做正常的计算,在虚拟机上比较卡,我直接发布到手机上就很流畅(注:我发布到手机上之后布局不是整齐,和虚拟机的不同),如果你想发布到手机上试试,很简单,USB连接手机和电脑,开始手机的 USB调试功能,装好手机驱动,直接运行程序,就会自动发布到手机上了,默认手机优先~
好了,本篇先到这里,下一篇我们将改进一下我们的计算器程序。

浙公网安备 33010602011771号