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调试功能,装好手机驱动,直接运行程序,就会自动发布到手机上了,默认手机优先~

好了,本篇先到这里,下一篇我们将改进一下我们的计算器程序。

posted @ 2013-03-09 18:41  Null Pointer  阅读(1265)  评论(2)    收藏  举报