helloPe的android项目实战之连连看—实现篇(三)
前面两篇“实现篇”已经将程序后台框架基本实现了,今天将涉及程序的activity类,在这个类中,为了有一个比较好的视觉效果,将介绍一些android中动画效果,依靠animation来实现,以及简单介绍android中自定义dialog的实现;首先看一下游戏界面运行时的效果图(程序中图片使用了网上的网友的,仅当学习之用):
游戏运行时界面 用于显示游戏结果的自定义dialog显示
先看看用于显示程序的activity类中的代码(这里主要是一些调用等,实现的逻辑在前面两篇文章中已经包含了)
package nate.llk; //包得导入略去 public class GameActivity extends Activity implements OnToolsChangeListener,OnTimerListener, OnStateListener{ private ImageButton img_startPlay; private ImageView img_title; private ProgressBar progress; private MyDialog dialog; //visibility at first is "gone" private ImageView clock; private GameView gameView = null; private ImageButton img_tip; private ImageButton img_refresh; private TextView text_refreshNum; private TextView text_tipNum; //两个帮助按键的特效 private Animation anim = null; private Handler handler = new Handler(){ @Override public void handleMessage(Message msg) { switch(msg.what){ case 0: dialog = new MyDialog(GameActivity.this,gameView,"完成!",gameView.getTotalTime() - progress.getProgress() + 1); dialog.show(); break; case 1: dialog = new MyDialog(GameActivity.this,gameView,"失败!",gameView.getTotalTime() - progress.getProgress() + 1); dialog.show(); } } }; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.game_view); anim = AnimationUtils.loadAnimation(this, R.anim.shake); findView(); startView(); img_startPlay.setOnClickListener(new BtnClickListener()); gameView.setOnTimerListener(this); gameView.setOnStateChangeListener(this); gameView.setOnToolsChangedListener(this); img_refresh.setOnClickListener(new BtnClickListener()); img_tip.setOnClickListener(new BtnClickListener()); }//end of the OnCreate method! /** * 寻找对应资源控件 */ public void findView(){ clock = (ImageView)this.findViewById(R.id.clock); progress = (ProgressBar)this.findViewById(R.id.timer); img_title = (ImageView)this.findViewById(R.id.title_img); img_startPlay = (ImageButton)this.findViewById(R.id.play_btn); img_tip = (ImageButton)this.findViewById(R.id.tip_btn); img_refresh = (ImageButton)this.findViewById(R.id.refresh_btn); gameView = (GameView)this.findViewById(R.id.game_view); text_refreshNum = (TextView)this.findViewById(R.id.text_refresh_num); text_tipNum = (TextView)this.findViewById(R.id.text_tip_num); } /** * 程序开启界面显示 */ public void startView(){ Animation scale = AnimationUtils.loadAnimation(this,R.anim.scale_anim); img_title.startAnimation(scale); img_startPlay.startAnimation(scale); } /** * 游戏运行时界面显示,即连连看的布局 */ public void playingView(){ Animation scaleOut = AnimationUtils.loadAnimation(this, R.anim.scale_anim_out); img_title.startAnimation(scaleOut); img_startPlay.startAnimation(scaleOut); img_title.setVisibility(View.GONE); img_startPlay.setVisibility(View.GONE); clock.setVisibility(View.VISIBLE); progress.setMax(gameView.getTotalTime()); progress.setProgress(gameView.getTotalTime()); progress.setVisibility(View.VISIBLE); gameView.setVisibility(View.VISIBLE); img_tip.setVisibility(View.VISIBLE); img_refresh.setVisibility(View.VISIBLE); text_tipNum.setVisibility(View.VISIBLE); text_refreshNum.setVisibility(View.VISIBLE); Animation animIn = AnimationUtils.loadAnimation(this, R.anim.trans_in); gameView.startAnimation(animIn); img_tip.startAnimation(animIn); img_refresh.startAnimation(animIn); text_tipNum.startAnimation(animIn); text_refreshNum.startAnimation(animIn); //player.pause(); gameView.startPlay(); toast(); } /** * 一个处理开始游戏,刷新,帮助三个按钮的listener的类 * @author HelloPe || NatePan * */ class BtnClickListener implements OnClickListener{ @Override public void onClick(View v) { switch(v.getId()){ case R.id.play_btn: playingView(); break; case R.id.refresh_btn: img_refresh.startAnimation(anim); gameView.refreshChange(); gameView.invalidate(); break; case R.id.tip_btn: img_tip.startAnimation(anim); gameView.autoHelp(); break; } } } @Override public void onRefreshChanged(int count) { text_refreshNum.setText(""+gameView.getRefreshNum()); } @Override public void onTipChanged(int count) { text_tipNum.setText("" + gameView.getTipNum()); } @Override public void onTimer(int leftTime) { progress.setProgress(leftTime); } /** *用来控制音乐的播放 */ @Override public void OnStateChanged(int StateMode) { switch(StateMode){ case GameView.WIN: handler.sendEmptyMessage(0); break; case GameView.LOSE: handler.sendEmptyMessage(1); break; case GameView.PAUSE: //player.stop(); //gameView.player.stop(); gameView.stopTimer(); break; case GameView.QUIT: //player.release(); //gameView.player.release(); gameView.stopTimer(); break; } } public void quit(){ this.finish(); } /** * 用于提醒游戏开始,提醒总时间 */ public void toast(){ Toast.makeText(this, "游戏已经开始!总时间: " + gameView.getTotalTime() + "s", Toast.LENGTH_LONG).show(); } @Override protected void onPause() { super.onPause(); gameView.setMode(GameView.PAUSE); } @Override protected void onDestroy() { super.onDestroy(); gameView.setMode(GameView.QUIT); } @Override public boolean onCreateOptionsMenu(Menu menu) { menu.add(Menu.NONE, 1, Menu.NONE,"Replay").setIcon(R.drawable.buttons_replay); menu.add(Menu.NONE, 2, Menu.NONE, "Pause").setIcon(R.drawable.pause); menu.add(Menu.NONE, 3, Menu.NONE,"SoundOn").setIcon(R.drawable.volume); return super.onCreateOptionsMenu(menu); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch(item.getItemId()){ case 1: gameView.setTotalTime(100); progress.setMax(100); gameView.startPlay(); break; case 2: gameView.stopTimer(); if(item.getTitle().equals("Pause")){ item.setTitle("Continue"); item.setIcon(R.drawable.play); }else if(item.getTitle().equals("Continue")){ item.setTitle("Pause"); item.setIcon(R.drawable.pause); } AlertDialog.Builder dialog = new AlertDialog.Builder(this); dialog.setIcon(R.drawable.icon); dialog.setTitle("继续"); dialog.setMessage("继续游戏?"); dialog.setPositiveButton("继续", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { gameView.setContinue(); } }).setNeutralButton("重玩", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { gameView.startPlay(); } }).setNegativeButton("退出", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { Intent startMain = new Intent(Intent.ACTION_MAIN); startMain.addCategory(Intent.CATEGORY_HOME); startMain.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(startMain); System.exit(0); } }); dialog.show(); break; case 3: /////////////////////////// if(item.getTitle().equals("Mute")){ item.setTitle("SoundOn"); item.setIcon(R.drawable.volume); }else if(item.getTitle().equals("SoundOn")){ item.setTitle("Mute"); item.setIcon(R.drawable.mute); } break; } return super.onOptionsItemSelected(item); } /** * 监听后退按钮,以防止误按,按下back按钮后,程序应当处于暂停状态 */ @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if(keyCode == KeyEvent.KEYCODE_BACK){ AlertDialog.Builder dialog= new AlertDialog.Builder(GameActivity.this).setTitle("退出游戏") .setMessage("确定退出游戏?") .setPositiveButton("是",new DialogInterface.OnClickListener(){ @Override public void onClick(DialogInterface dialog, int which) { Intent startMain = new Intent(Intent.ACTION_MAIN); startMain.addCategory(Intent.CATEGORY_HOME); startMain.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(startMain); System.exit(0); } }).setNegativeButton("否", new DialogInterface.OnClickListener(){ @Override public void onClick(DialogInterface dialog, int which) { Toast.makeText(GameActivity.this, "重新开始了游戏", Toast.LENGTH_LONG).show(); gameView.startPlay(); } }); dialog.setIcon(R.drawable.icon); dialog.show(); } return super.onKeyDown(keyCode, event); } }
在此类中,如上次我们所说的,我们实现了之前定义的三个接口,引用了GameView类(在activity的布局文件中使用到),在activity布局文件中如下使用:
<nate.llk.view.GameView android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/game_view" android:visibility="gone" android:layout_below="@id/timer" />
同样对于我们自定义的dialog,跟自定义的GameView(继承自View)是一样的。MyDialog类继承自Dialog类,实现了OnClickListener的OnClick方法,使用一个布局文件,将自定义的dialog布局。布局文件很简单,MyDialog类如下:
package nate.llk; //导入包略去
public class MyDialog extends Dialog implements OnClickListener{ private GameView gameview; private Context context; public MyDialog(Context context, GameView gameview, String msg, int time) { super(context,R.style.dialog); this.gameview = gameview; this.context = context; this.setContentView(R.layout.dialog_view); TextView text_msg = (TextView) findViewById(R.id.text_message); TextView text_time = (TextView) findViewById(R.id.text_time); ImageButton btn_menu = (ImageButton) findViewById(R.id.menu_imgbtn); ImageButton btn_next = (ImageButton) findViewById(R.id.next_imgbtn); ImageButton btn_replay = (ImageButton) findViewById(R.id.replay_imgbtn); text_msg.setText(msg); text_time.setText(text_time.getText().toString().replace("$", String.valueOf(time))); btn_menu.setOnClickListener(this); btn_next.setOnClickListener(this); btn_replay.setOnClickListener(this); this.setCancelable(false); } @Override public void onClick(View v) { this.dismiss(); switch(v.getId()){ case R.id.menu_imgbtn: Dialog dialog = new AlertDialog.Builder(context) .setIcon(R.drawable.buttons_bg20) .setTitle(R.string.quit) .setMessage(R.string.sure_quit) .setPositiveButton(R.string.alert_dialog_ok, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { ((GameActivity)context).quit(); } }) .setNegativeButton(R.string.alert_dialog_cancel, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { gameview.startPlay(); } }) .create(); dialog.show(); break; case R.id.replay_imgbtn: gameview.startPlay(); break; case R.id.next_imgbtn: gameview.startNextPlay(); break; } } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if(keyCode == KeyEvent.KEYCODE_BACK){ this.dismiss(); } return super.onKeyDown(keyCode, event); } }
上面代码简单,不过还是实现了较好的效果。
在android中使用animation的动画包含四种,Tween animation的使用也能够使程序看起来效果好点:
在anim文件下:
实现当点击程序中两个工具按钮时,工具按钮出现抖动的效果:
<?xml version="1.0" encoding="utf-8"?> <translate xmlns:android="http://schemas.android.com/apk/res/android" android:fromXDelta="0" android:toXDelta="10" android:duration="1000" android:interpolator="@anim/cycle" />
<?xml version="1.0" encoding="utf-8"?> <cycleInterpolator xmlns:android="http://schemas.android.com/apk/res/android" android:cycles="7" /><!--用于控制上面颤动的次数-->
用于控制欢迎界面的图标逐渐变大的出场效果:
<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android"> <scale android:interpolator="@android:anim/accelerate_decelerate_interpolator" android:fromXScale="0.0" android:toXScale="1.0" android:fromYScale="0.0" android:toYScale="1.0" android:pivotX="50%" android:pivotY="50%" android:fillAfter="true" android:duration="1600" /> </set>
当然放大效果只是改一下android:fromXScale="1.0" 与 android:toXScale="0.0"即可;
至于透明效果,用于将GameView中的内容从透明慢慢展示出来:
<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android"> <alpha android:fromAlpha="0.0" android:toAlpha="1.0" android:duration="1000" /> </set>
而在activity中对于以上资源的使用上面activity类中已经给出,先载入,然后调用imageView或者其他View的startAnimation方法即可;
对于游戏中音效的播放方法比较简单,主要是在不同的状态播放不同的声音比较繁琐,android中有两种播放音效的方法:一种是SoundPool,一种是MediaPlayer。SoundPool适合短促音乐,但是反应速度要求比较高的情况;MediaPlayer则是是相反。使用步骤如:
// 初始化soundPool 对象,第一个参数是允许有多少个声音流同时播放,第2个参数是声音类型,第三个参数是声音的品质 soundPool = new SoundPool(25, AudioManager.STREAM_MUSIC, 100);//SoudPool的引用 soundPool.load(context, raw, 1);//调用load函数载入音乐资源 //之后调用play函数即可播放参数中相应的音乐资源
本程序算是完成了,向网友们学习了不少,毕竟是很多人都做过的小项目。记录下来,希望高手路过别喷~
while(success != try());