Android开发入门实例:四则混合运算计算器

        开发Android应用主要使用的语言是Java,布局文件和界面则一般用XML来描述。整个应用的GUI开发与Java SWT、QT等其实区别不是特别大。如果有任何一种GUI程序开发经验,学习Android应用开发就是一件非常容易的事。这篇文章里我们来开发一个支持四则混合运算的简易计算器APP,这个APP演示了开发Android项目的一般流程以及基本方法。

        环境:Windows 7 x64、JDK8、adt-bundle-windows-x86_64-20140702。

一、安装JDK8与ADT

        JDK8是Java SDK的最新版本,ADT-bundle则是Android官方发布的以Eclipse为基础的Android开发环境。两个软件包都可以从官网下载,点击下载JDK8、adt-bundle,ADT官方被墙了,所以只能从其它地方下载。

        双击JDK8的安装程序,按照正常的步骤即可安装成功。ADT-bundle不需要安装,直接解压就可以启动eclipse.exe使用了,但是注意一定要设置JAVA_HOME到环境变量。

二、新建Android项目

        打开Eclipse后,File -> New -> Other,在弹出的对话框中选择Android项目中的Android Application Project,并点击 Next。然后输入Application Name、Project Name、Package Name,并分别选择最低要求的SDK版本、目标SDK版本、项目编译版本、应用主题,这里由于我们是用来测试的,所以前三项均选择Android 4.0,第四项默认。接下来的两个对话框主要是用来设置应用的图标及一个新的Activity。

        完成后,项目的目录结构差不多是下图这个样子:

Android开发入门实例:四则混合运算计算器_第1张图片

        最重要的是res目录、src目录,以及AndroidManifest.xml文件。res目录中的layout目录存放了项目中每个Activity的界面布局,values目录则存放项目中所有用到的明文字符串及主题等资源。src目录就是应用的所有java源代码。AndroidManifest.xml是项目的主工程文件。

        无论多简单的项目都少不了上面所列出的这些目录和文件。下面我们来逐个解析这些文件。

三、工程管理文件AndroidManifest.xml




    

    
        
        
            
                

                
            
        
        
        
        
    


        manifest中的package指定了本工程的源代码在哪个包里面。uses-sdk中的内容在之前新建项目的时候就已经配置好了,即说明本APP支持的最低版本及目标版本。application中的icon、label、theme等分别指定了APP的图标、名称、主题风格,这里用@符号来从文件中引用内容,如app_name是strings.xml中定义的一个字符串,在这里被引用为label。

        这个配置文件中最关键的是activity项。

        Android应用由一个或多个Activity组成,Activity有自己有界面布局和控制代码,不同的Activity可以互相跳转并组合完成各种任务。Activity需要指定name和label,name可以确定源代码中对应的Activity是哪一个,label是Activity显示在界面上的一个标题。

        不同的Activity之间跳转时,必须由intent来控制。用户启动APP跳转到APP的入口Activity时,也需要一个特定的intent来说明。上面代码中第一个Activity就有intent-filter字段,将action指定为"android.intent.action.MAIN"说明该Activity是当前APP的主Activity,将category指定为"android.intent.category.LAUNCHER"说明该Activity将由用户启动。

四、res资源目录及其包含的文件

        先看values/strings.xml文件。这个文件中定义了APP需要用到的各种字符串。APP使用这些字符串的时候最好以引用的方式从这个文件中包含进来,而不要直接硬编码到程序中。strings.xml内容很简单,类似于下面这样:




    iCalculator
    iCalculator
    关于
    关于
    返回
    Hail to Me
    The World Will See My Might !
    
    错误
    
    退出

        当需要引用这些字符串的时候,用@string/about这样的方式就可以了。例如@string/about实际表示“关于”这个字符串。

        values/styles.xml文件中指定了APP的主题风格,一般没有特殊要求的话可以不必改动。

        跟界面布局有关的最重要的文件都在res/layout目录中,例如我们可以把主Activity的布局写在res/layout/activity_main.xml文件中,内容如下:





    
	
		
		
	
	
	
		
	    
        这个文件中实际包含了两类跟界面有关的内容:layout和widget。LinearLayout属于layout,layout指明了该如何在界面上排列组件。这里用到的LinearLayout说明使用线性布局来部署所有的组件,LinearLayout在指定orientation属性(orientation=“vertical"或orientation="horizontal")后,可以分别以垂直或水平的方式进行线性布局。layout_width和layout_height指定了此布局的宽度和高度,其值为"match_parent"时,子布局(或组件)的宽度/高度与其父布局/父组件保持一致,值为"wrap_content"时,子布局(或组件)将按照自己的实际大小来显示。

        文件中的EditText和Button属于widget,即组件(或构件),用来完成某个特定的动作(如输入文字、显示文字、作为一个按钮接收点击信号等)。EditText是编辑框组件,通常用来输入文本,在设置editable="false"后禁止编辑,用来作为显示屏使用。gravity属性决定了编辑框中的文本对齐方式,有"center""right""left"等。Button是按钮组件。

        所有的widget都有一个id,这个id非常重要,因为id是源代码中获取组件句柄的惟一方式。@+id/digit_plus_button表示新增一个名为"digit_plus_button"的id,并将此id指定给相关联的widget。

        layout和widget的weight(权重)属性也值得说明一下。例如有三个layout,名称分别为layout_1/layout_2/layout_3,这三个layout的weight分别设置1、1、1时,则三个layout分别各占据了父layout(或父widget)三分一的空间,如果它们的wieght分别设置为1、2、1,,则父layout或父widget的空间被平均分为4份,layout_1和layout_3各占据1份,layout_2则占据2份。上面这个activity_main.xml文件所绘制的界面如下图所示:

Android开发入门实例:四则混合运算计算器_第2张图片

        左上角那个五颜六色的图标是我从网上找来的,可以在设置APP的图标时指定,也可以在Androidmanifest.xml文件application项的icon中指定。

        本示例涉及到的另一个activity_about.xml内容如下:



    
    
    
    

五、Java类代码

        在Android应用中,基本的项目开发单位就是Activity,一个Activity分别对应一个java类和一个activity.xml布局文件。

        Activity的类代码控制所有的资源和APP流程。

        本示例中主Activity相关联的类MainActivity位于com.example.icalculator包中,代码清单如下:

package com.example.icalculator;

import com.example.icalculator.R.id;

import android.app.Activity;
import android.os.Bundle;

import android.view.View;
import android.view.View.OnClickListener;

import android.view.Menu;
import android.view.MenuItem;

import android.widget.Button;
import android.widget.EditText;

import android.content.Intent;

/**
 * 
 * @author Administrator
 *
 * 由于按钮较多,需要监听的类似事件比较多,所以用Switch方法来监听,
 * 需要实现OnClickListener接口
 * 
 */

public class MainActivity extends Activity implements OnClickListener{

	/**
	 * 这个是全局的字符串,用来存储显示在显示屏上的计算式及结果
	 */
	private String calculator_string;
	
	private static final int quit_menu_item = Menu.FIRST;
	
	private EditText displayEditText;
	private Button aboutButton;
	private Button leftButton;
	private Button rightButton;
	private Button ceButton;
	private Button dig7Button;
	private Button dig8Button;
	private Button dig9Button;
	private Button digDivButton;
	private Button dig4Button;
	private Button dig5Button;
	private Button dig6Button;
	private Button digMultiButton;
	private Button dig1Button;
	private Button dig2Button;
	private Button dig3Button;
	private Button digMinusButton;
	private Button dig0Button;
	private Button digDotButton;
	private Button digEqualButton;
	private Button digPlusButton;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		calculator_string = "";
		
		displayEditText = ( EditText )findViewById( R.id.display_edittext );
		
		aboutButton = ( Button )findViewById( R.id.digit_about_button );
		aboutButton.setOnClickListener( this );
		
		leftButton = ( Button )findViewById( R.id.digit_left_button );
		leftButton.setOnClickListener( this );
		
		rightButton = ( Button )findViewById( R.id.digit_right_button );
		rightButton.setOnClickListener( this );
		
		ceButton = ( Button )findViewById( R.id.digit_ce_button );
		ceButton.setOnClickListener( this );
		
		dig7Button = ( Button ) findViewById( R.id.digit_7_button );
		dig7Button.setOnClickListener( this );
		
		dig8Button = ( Button ) findViewById( R.id.digit_8_button );
		dig8Button.setOnClickListener( this );
		
		dig9Button = ( Button ) findViewById( R.id.digit_9_button );
		dig9Button.setOnClickListener( this );
		
		digDivButton = ( Button ) findViewById( R.id.digit_div_button );
		digDivButton.setOnClickListener( this );
		
		dig4Button = ( Button ) findViewById( R.id.digit_4_button );
		dig4Button.setOnClickListener( this );
		
		dig5Button = ( Button ) findViewById( R.id.digit_5_button );
		dig5Button.setOnClickListener( this );
		
		dig6Button = ( Button ) findViewById( R.id.digit_6_button );
		dig6Button.setOnClickListener( this );
		
		digMultiButton = ( Button ) findViewById( R.id.digit_multi_button );
		digMultiButton.setOnClickListener( this );
		
		dig1Button = ( Button ) findViewById( R.id.digit_1_button );
		dig1Button.setOnClickListener( this );
		
		dig2Button = ( Button ) findViewById( R.id.digit_2_button );
		dig2Button.setOnClickListener( this );
		
		dig3Button = ( Button ) findViewById( R.id.digit_3_button );
		dig3Button.setOnClickListener( this );
		
		digMinusButton = ( Button ) findViewById( R.id.digit_minus_button );
		digMinusButton.setOnClickListener( this );
		
		dig0Button = ( Button ) findViewById( R.id.digit_0_button );
		dig0Button.setOnClickListener( this );
		
		digDotButton = ( Button ) findViewById( R.id.digit_dot_button );
		digDotButton.setOnClickListener( this );
		
		digEqualButton = ( Button ) findViewById( R.id.digit_equal_button );
		digEqualButton.setOnClickListener( this );
		
		digPlusButton = ( Button ) findViewById( R.id.digit_plus_button );
		digPlusButton.setOnClickListener( this );
	}
	
	/**
	 * 新建Menu
	 * 
	 */
	@Override
	public boolean onCreateOptionsMenu( Menu menu ){
		super.onCreateOptionsMenu(menu);
		menu.add( 0, Menu.FIRST, 0, R.string.menu_quit );
		
		return true;
	}
	
	/**
	 * Menu被选择的时候,捕获及处理相关事件
	 * 
	 */
	@Override
	public boolean onOptionsItemSelected( MenuItem item ){
		switch( item.getItemId() ){
		case quit_menu_item:
			/**
			 * 选择了“退出”的菜单项,则退出
			 * 
			 */
			finish();
			System.exit( 0 );
			break;
		}
		
		return true;
	}
	
	/**
	 * 捕获及处理按钮事件
	 * 
	 */
	public void onClick( View v ){
		
		/**
		 * 通过区分不同的id来确定当前监听到的是哪个控件的事件
		 * 
		 */
		switch( v.getId() ){
		case R.id.digit_about_button:
			onAbout( v );
			break;
		case R.id.digit_left_button:
			appendToDisplay( "(" );
			break;
		case R.id.digit_right_button:
			appendToDisplay( ")" );
			break;
		case R.id.digit_ce_button:
			cleanDisplay();
			break;
		case R.id.digit_7_button:
			appendToDisplay( "7" );
			break;
		case R.id.digit_8_button:
			appendToDisplay( "8" );
			break;
		case R.id.digit_9_button:
			appendToDisplay( "9" );
			break;
		case R.id.digit_div_button:
			appendToDisplay( "/" );
			break;
		case R.id.digit_4_button:
			appendToDisplay( "4" );
			break;
		case R.id.digit_5_button:
			appendToDisplay( "5" );
			break;
		case R.id.digit_6_button:
			appendToDisplay( "6" );
			break;
		case R.id.digit_multi_button:
			appendToDisplay( "*" );
			break;
		case R.id.digit_1_button:
			appendToDisplay( "1" );
			break;
		case R.id.digit_2_button:
			appendToDisplay( "2" );
			break;
		case R.id.digit_3_button:
			appendToDisplay( "3" );
			break;
		case R.id.digit_minus_button:
			appendToDisplay( "-" );
			break;
		case R.id.digit_0_button:
			appendToDisplay( "0" );
			break;
		case R.id.digit_dot_button:
			appendToDisplay( "." );
			break;
		case R.id.digit_equal_button:
			calculating();
			break;
		case R.id.digit_plus_button:
			appendToDisplay( "+" );
			break;
		default:
			;
		}
	}
	
	/**
	 * 将传来的字符追加到显示屏字符串上,并重新写到显示屏上
	 * 
	 */
	private void appendToDisplay( String keyChar ){
		calculator_string = calculator_string + keyChar;
		displayEditText.setText( calculator_string );
	}
	
	/**
	 * 显示屏复位
	 * 
	 */
	private void cleanDisplay(){
		calculator_string = "";
		displayEditText.setText( "" );
	}
	
	/**
	 * 
	 * 用户点击了“关于”按钮后,激活About的Activity
	 */
	private void onAbout( View v ){
		/**
		 * 创建一个Intent来跳转
		 * 
		 */
		
		Intent intent = new Intent();
		intent.setClass( MainActivity.this, AboutActivity.class );
		startActivity( intent );
	}
	
	/**
	 * 执行这个方法说明用户点击了等于号,要开始根据calculator_string进行计算了,
	 * 并将执行结果显示在显示屏上
	 * 
	 */
	private void calculating(){
		/**
		 * 这里我们获得了一个原始的、尚未确定准备性的运算表达式,
		 * 包含左右括号、加、减、乘、除,第一步要进行准备性验证,
		 * 如果验证未通过,则在显示屏上提出出错及错误原因,
		 * 验证通过后,即可进行计算并将结果输出到显示屏上。
		 * 
		 * 表达式验证和计算需要用到的数据结构是栈,计算时的算法
		 * 则跟处理前缀表达式有关。
		 * 
		 */
	}
}

        可以看到Android开发的流程和一些特点。第一步当然是导入开发中用到的各种包,android.app.Activity和android.os.Bundle必不可少,前者是所有自定义Activity的基类,后者被用来在Activity之间传递数据。根据需要还应当导入android.view.Menu和android.widget.Button等各种类。

        然后就是新建自己的Activity,从android.app.Activity继承而来。注意本例的MainActivity同时实现了OnClickListener接口,以方便在界面存在大量组件的时候监听事件。自定义Activity类中首先要做的就是执行一个父类的onCreate方法,目的是作一些初始化操作。setContentView函数用来设置本Activity对应的布局,这里要注意,系统自动生成了一个名为R的类,这个类中包含了所有已经按照要求定义的资源。在类代码中引用资源时可以随时使用R类。然后findViewById方法顾名思义,就是根据layout_..xml文件中的id来获取组件的句柄。获取到句柄后就可以对其进行操作了,本例中在获取到句柄后直接定义了所有组件的监听函数。

        由于MainActivity实现了OnClickListener接口,我们可以直接在类的内部实现onClick方法,根据传来的View来确定是哪一个组件发出了onClick的信号,并作对应处理。

        onCreateOptionsMenu方法用来新建一个菜单,该菜单在用户按下菜单键/设置键时被呼出。

        onOptionsItemSelected方法用来响应菜单选项,里面有一个Switch结构可以区分出是哪个菜单项被选择了,然后就可以作对应处理。

        这里要注意onAbout方法。当界面上的“关于”按钮被按下时,onAbout方法被调用。该方法中新建并激活了一个Intent,用来启动一个指定的新的Activity。intent.setClass()第一个参数是发起Intent的源Activity,第二个参数是目标Activity。startActivity( intent )将新建一个目标Activity。源Activity是否关闭取决于是否在startActivity方法之后执行了finish()方法。如果执行finish(),则关闭当前Activity,并跳到新的Activity;如果不执行finish(),则当前Activity会被保存到系统的"Back Stack"中,然后跳到新Activity,当新Activity关闭后,源Activity就又会被激活变为当前Activity。


        主界面上有个“关于”按钮,点击之后就会跳到另一个名为AboutActivity的Activity中,代码清单如下:

package com.example.icalculator;

import android.app.Activity;
import android.os.Bundle;

import android.widget.Button;
import android.content.Intent;

import android.view.View;
import android.view.View.OnClickListener;

public class AboutActivity extends Activity {

	private Button backButton;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_about);
		
		backButton = ( Button ) findViewById( R.id.about_info_back_button );
		backButton.setOnClickListener( new OnClickListener(){
			public void onClick( View v ){
				/**
				 * 这个按钮的目的仅仅是关闭“关于”的Activity。
				 * 如果这里又新建立了一个Intent,则在跳转的时候又会新建立一个MainActivity。
				 * 因此这里不应该新建Intent,直接关闭AboutActivity就可以了。
				 * 因为之前从MainActivity跳转过来的时候并没有把该MainActivity,
				 * 本AboutActivity关闭后,就会自动恢复到之前的那个MainActivity。
				 * 
				 */
				
				/*
				Intent intent = new Intent();
				intent.setClass( AboutActivity.this, MainActivity.class );
				startActivity( intent );
				*/
				finish();
			}
		});
	}
}

        看到这里大家肯定会奇怪,整个程序中并没有包含对运算表达式进行计算的代码。当用户按下等于号"="时,APP会调用calculating()方法,可以看到这个方法仍然是空的。这部分代码的可移植性比较强,但是写起来需要考虑的东西比较多。很久以前我用C++写过一个,有空再移植到Java下面吧。


你可能感兴趣的:(Java与Android)