从Helloworld开始,继续前进
[第二届 Google 暑期大学生博客分享大赛 - 2011 Android 成长篇]
配置好了开发环境,写出了第一个Helloworld程序,很高兴。但是高兴之余又发现对于接下来的学习有点不知所措。书上介绍了很多控件怎么使用,Layout怎么使用,看了一会就觉得好烦啊。
我觉得学习在一个平台开发程序,还是应该先自己摸索着写一点东西出来,有了一定的兴趣和成就感后,继续学习深入的开发,效果可能会更好。
写一个小程序先。
通过Intent实现Activity间的切换:
通过Button和返回键,在两个Activity之前切换,这样一个程序应该很好写,网上和书上都有教程。注意点就是要新建一个布局文件,并且要把新的Activity在清单文件中注册一下。
试着把这样一个简单的程序改成一个小游戏。
Intent的另外一个功能,就是在两个Activity之前传递数据。这个功能非常有用,假设第一个Activity是刚进入游戏时的界面,有开始游戏、选择难度、游戏帮助、退出游戏等选项,第二个Activity则是真正的游戏界面,那么当从第一个Activity进入到第二个Activity时,肯定是要传递一些数据的,比方说难度、玩家昵称。现在把这样一个传输的功能简单实现一下。
1.把第一个Activity的UI改成这样,即添加一个EditText控件,用ADT编辑布局文件即可。
2.修改代码,第一个Activity:
public class MoveActivity extends Activity {
private Button jump;
private EditText et;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
jump=(Button)findViewById(R.id.jump);//得到控件对象的引用
et=(EditText)findViewById(R.id.nickname);
jump.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
Intent i=new Intent();
i.setClass(MoveActivity.this, Game.class);//设置Intent
String nickName=et.getText().toString();//得到EditText中的内容
i.putExtra("NickName", nickName);//加入附加信息 nickname
i.putExtra("Grade", 10);//Grade
startActivity(i);//启动第二个Activity
}
});
}
}
记得import进相应的包,Ctrl+Shift+O即可
3.在第二个Activity中再加入两个TextView,然后修改代码:
public class Game extends Activity {
private TextView tv1,tv2,tv3;
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.game);
tv1=(TextView)findViewById(R.id.tv1);//得到控件对象的引用
tv2=(TextView)findViewById(R.id.tv2);//id已修改
tv3=(TextView)findViewById(R.id.tv3);
//得到Intent对象
Intent i=getIntent();
//从Intent对象中得到附加信息并在TextView中显示出来
String nickName=i.getStringExtra("NickName");
int iGrade=i.getIntExtra("Grade", 0);//默认是0
tv2.setText(nickName);//设置TextView
tv3.setText(""+iGrade);
}
}
同样注意import。
运行看结果后就可以看到数据传输是成功的。
4.在第二个Activity中添加16个Button,4x4。因为xml里面默认布局是vertical的LinearLayout,所以只要先在UI中加一个LinearLayout(Horizontal),然后再加四个Button,如此重复四次即可。
如果现在我说这已经是一个游戏了,你肯定不信。
还记得小时候玩过的那种移拼图的玩具吗?
应该有想法了吧。
5.修改第二个Activity中的代码,让它变成一个Android上的移拼图小游戏。其实这已经和Android无关了,看的是Java和算法的基础。文章最后给出代码。
说到写游戏,我想到的是游戏中的主循环。于是我试着在Android中模拟了一下,发现了一个问题。
原先的想法是:在一个TextView中显示一个数字,然后在Activity中启动一个线程,该线程每隔一定的时间让TextView中显示的数字加1。
看上去这十分简单,但是写了之后我才发现,单纯的这样做是不可行的,因为一个线程不能更改非该线程创建的控件的内容。网上看到的解决办法是使用Handler。具体请见这里。非常感谢原文作者的分享。
使用这个方法,我让第二个Activity中的tv3里面的内容每隔一定时间加1。
最终效果图:
这离一个正式的游戏当然还距离很远,但是,如果看过这篇文章后,一些原本不善于思考的人能变得善于思考,例如,你会不会开始思考Menu和返回键在这个程序中的作用,会不会思考NumberAdder线程到底应该在OnCreate、OnStart、OnRestart等的哪个生命周期函数中被创建,会不会试着加一个Service进去,会不会试着让Button间的间距减小然后把数字替换成图片,会不会增加代码以判断游戏是否结束,等等,只要能够增加一点想法,那看过这篇文章就算值得了吧。
不足之处,请不吝赐教,谢谢!
最后是第二个Activity的代码,供参考,项目源代码下载:
package Yuleo.Android;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
public class Game extends Activity {
private TextView tv1,tv2,tv3;//三个TextView的引用
private Button[][] allButtons;//所有Button对象的引用的二维数组
private int[][] allValues;//Button对象对应的数值,另外一种办法是用Button的继承类
//Handler
static final int UPDATE_TV2=1;//消息的定义
Handler h=new Handler()
{
@Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
super.handleMessage(msg);
switch(msg.what)
{
case UPDATE_TV2://处理tv3中数字的增加
{
String sNum=(String) tv3.getText();//得到String并转为int
Integer i=new Integer(sNum);
int iNum=i.intValue();
iNum++;
tv3.setText(""+iNum);//更新
break;
}
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.game);
tv1=(TextView)findViewById(R.id.tv1);//得到控件对象的引用
tv2=(TextView)findViewById(R.id.tv2);//id已修改
tv3=(TextView)findViewById(R.id.tv3);
Intent i=getIntent();
String nickName=i.getStringExtra("NickName");
int iGrade=i.getIntExtra("Grade", 0);//默认是0
tv2.setText(nickName);//设置TextView
tv3.setText(""+iGrade);
allButtons=new Button[5][5];//Button控件
allButtons[1][1]=(Button)findViewById(R.id.button11);
allButtons[1][2]=(Button)findViewById(R.id.button12);
allButtons[1][3]=(Button)findViewById(R.id.button13);
allButtons[1][4]=(Button)findViewById(R.id.button14);
allButtons[2][1]=(Button)findViewById(R.id.button21);
allButtons[2][2]=(Button)findViewById(R.id.button22);
allButtons[2][3]=(Button)findViewById(R.id.button23);
allButtons[2][4]=(Button)findViewById(R.id.button24);
allButtons[3][1]=(Button)findViewById(R.id.button31);
allButtons[3][2]=(Button)findViewById(R.id.button32);
allButtons[3][3]=(Button)findViewById(R.id.button33);
allButtons[3][4]=(Button)findViewById(R.id.button34);
allButtons[4][1]=(Button)findViewById(R.id.button41);
allButtons[4][2]=(Button)findViewById(R.id.button42);
allButtons[4][3]=(Button)findViewById(R.id.button43);
allButtons[4][4]=(Button)findViewById(R.id.button44);
//这个真没办法偷懒
allValues=new int[5][5];//数组对象
for(int r=1;r<=4;r++)//row column
{
for(int c=1;c<=4;c++)
{
allValues[r][c]=(r-1)*4+c;
if(allValues[r][c]<10)
{
allButtons[r][c].setText(" "+allValues[r][c]+" ");
//加空格固定Button的宽度,也可以通过布局文件来固定
}
else
{
allButtons[r][c].setText(" "+allValues[r][c]+" ");
}
}
}
//修改右下角那个Button
allValues[4][4]=111;
allButtons[4][4].setText(" ");
//设置监听器
for(int r=1;r<=4;r++)
{
for(int c=1;c<=4;c++)
{
GameListener gl=new GameListener(r,c);
allButtons[r][c].setOnClickListener(gl);
}
}
}
//Button监听器类,内部类可以方便地访问外部类中的成员变量
class GameListener implements OnClickListener
{
private int iRow,iColumn;
GameListener(int r, int c)
{
iRow=r;
iColumn=c;
}
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
if(iRow>1)//上面有按钮的话
{
if(allValues[iRow-1][iColumn]==111)//如果上面那个按钮为空
{
//交换数据
int iTemp=allValues[iRow][iColumn];
allValues[iRow][iColumn]=111;
allValues[iRow-1][iColumn]=iTemp;
//交换显示的内容
String sTemp=(String) (allButtons[iRow][iColumn]).getText();
allButtons[iRow][iColumn].setText(allButtons[iRow-1][iColumn].getText());
allButtons[iRow-1][iColumn].setText(sTemp);
return ;
}
}
if(iRow<4)//下面有按钮的话
{
if(allValues[iRow+1][iColumn]==111)
{
int iTemp=allValues[iRow][iColumn];
allValues[iRow][iColumn]=111;
allValues[iRow+1][iColumn]=iTemp;
String sTemp=(String) (allButtons[iRow][iColumn]).getText();
allButtons[iRow][iColumn].setText(allButtons[iRow+1][iColumn].getText());
allButtons[iRow+1][iColumn].setText(sTemp);
return ;
}
}
if(iColumn>1)//左边有按钮的话
{
if(allValues[iRow][iColumn-1]==111)
{
int iTemp=allValues[iRow][iColumn];
allValues[iRow][iColumn]=111;
allValues[iRow][iColumn-1]=iTemp;
String sTemp=(String) (allButtons[iRow][iColumn]).getText();
allButtons[iRow][iColumn].setText(allButtons[iRow][iColumn-1].getText());
allButtons[iRow][iColumn-1].setText(sTemp);
return ;
}
}
if(iColumn<4)//右边有按钮的话
{
if(allValues[iRow][iColumn+1]==111)
{
int iTemp=allValues[iRow][iColumn];
allValues[iRow][iColumn]=111;
allValues[iRow][iColumn+1]=iTemp;
String sTemp=(String) (allButtons[iRow][iColumn]).getText();
allButtons[iRow][iColumn].setText(allButtons[iRow][iColumn+1].getText());
allButtons[iRow][iColumn+1].setText(sTemp);
return ;
}
}
}
}
//线程类,用于让tv3中的数字增加
class NumberAdder implements Runnable
{
@Override
public void run() {
// TODO Auto-generated method stub
for(int j=1;j<20;j++)
{
try
{
Thread.sleep(1000);
Message msg = new Message();
msg.what = UPDATE_TV2;//给Handler发消息,让Handler去处理
h.sendMessage(msg);
}
catch (Exception e)
{
}
}
}
}
@Override
protected void onStart() {
// TODO Auto-generated method stub
super.onStart();
//建立线程
NumberAdder na=new NumberAdder();
new Thread(na).start();
}
}