たぼさんの部屋

いちょぼとのんびり

P003) 算数アプリ 現状は

f:id:donsuka_kk:20121130142305p:plain

Main.java

package com.efolab.p003;

import java.lang.ref.WeakReference;

import android.app.Activity;
import android.app.PendingIntent;
import android.app.PendingIntent.CanceledException;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.Window;
import android.widget.LinearLayout;

import com.efolab.p003.kekkaActivity.KekkaActivity;
import com.efolab.p003.mainActivity.MyFooter;
import com.efolab.p003.mainActivity.MyHeader;
import com.efolab.p003.mainActivity.MyKokuban;
import com.efolab.p003.mainActivity.NumberTable;
import com.efolab.p003.mainActivity.SubLinear;

public class Main extends Activity {
	Context context;
	// レイアウト
	private final int FP = LinearLayout.LayoutParams.FILL_PARENT;
	private final int WC = LinearLayout.LayoutParams.WRAP_CONTENT;

	LinearLayout layout_base;
	MyKokuban kokuban;
	NumberTable numberTable;
	SubLinear subLinear;
	MyHeader mHeader;
	MyFooter mFooter;
	// システム
	Thread thread;
	static Handler mainHandler;
	// Message WHAT
	public final static int WHAT_INPUT_CLICK = 0, WHAT_KOKUBAN_TOUCH = 1;
	/*
	 * [ゲーム制御の考え方] 
	 * scene について 
	 * ゲーム初期 : scene = S_TITLE ---(1)
	 * --inputPanel :入力反映不可 
	 * --kokuban : タッチ時にscene = S_PLAY に 変更する
	 * --mondai : 問題の生成を開始(別スレッドで)
	 * ゲーム開始 : scene = S_PLAY ---(2)
	 * --inputPanel : 問題生成が1問終了(mondaiArray > 0) になった時点で「入力反映する」
	 * 入力値が[ok]の場合は答え(mondaiArray)に格納して、次の問題へ移る。(問題と答えの表示は初期化する)
	 * あるいは、制限時間を超えたときは、答えをnull格納して、次の問題へ移る。
	 * --kokuban : タッチは受け付けない(注釈1)
	 * --mondai : 問題数すべて生成し終わった段階で終了している
	 * *すべての問題が終了したら ゲーム移行
	 * ゲーム終了 : scene = S_GAMEOVER ---(3)
	 * --inputPanel : 入力不可
	 * --kokuban : タッチ時に 「結果画面」KekkaActivity に移る
	 * --mondai : なにもしない
	 * 
	 * KekkaActivityから復帰時
	 * 変数の初期化する
	 * scene = S_TITLEにセットして (1)に戻る
	 * 
	 * 注釈1:タッチで答えを選ぶような問題を今後作るときには変更する
	 */
	// ゲーム制御
	public final static int S_TITLE = 0;
	public final static int S_PLAY = 1;
	public final static int S_GAMEOVER = 2;
	private static int scene = S_TITLE;

	// 問題制御
	int mondaiLength = 3; // 問題数
	int maxTime = 500; // 1問の制限時間(ミリ秒):500=5秒
	int keikaTime = 0; // 1問の「経過時間」:maxTimeになると、次の問題へ
	int countUpTime = 10; // 経過時間の加算単位:Thread.sleep()の引数として使う
	int score = 0; // 得点:正解の場合は、残り時間を得点とする。

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		requestWindowFeature(Window.FEATURE_NO_TITLE);
		
		context = getApplicationContext();
		layout_base = new LinearLayout(context);
		layout_base.setOrientation(LinearLayout.VERTICAL);
		layout_base.setBackgroundColor(Color.WHITE);
		setContentView(layout_base);

		// Handler instance
		mainHandler = new MainHandler(this);	//引数はthis

		//XXX Log
		Log.v("Main onClreate", "Thread" + Thread.currentThread().getId());
		
		// レイアウトを生成
		String layoutNames[] = {"layout_header" , "layout_main" , "layout_input" , "layout_footer"};
		LinearLayout.LayoutParams params;
		LinearLayout[] mLayouts = { new LinearLayout(context),
				new LinearLayout(context), new LinearLayout(context),
				new LinearLayout(context) };
		int[][] paramsArray = { { FP, WC }, { FP, 0 }, { FP, WC }, { FP, WC } };
		float[] paramsWeight = { 0, 1.0f, 0, 0 };
		for (int i = 0; i < mLayouts.length; i++) {
			params = new LinearLayout.LayoutParams(paramsArray[i][0], paramsArray[i][1]);
			params.weight = paramsWeight[i];
			layout_base.addView(mLayouts[i], params);
			//setTag
			mLayouts[i].setTag(layoutNames[i]);
			/*
			 * 子レイアウトをセットする
			 */
			if(layoutNames[i].matches("layout_header")){
				mHeader = new MyHeader(context);
				params = new LinearLayout.LayoutParams(FP,WC);
				mLayouts[i].addView(mHeader,params);
			}
			if(layoutNames[i].matches("layout_main")){
				kokuban = new MyKokuban(context, mainHandler);
				params = new LinearLayout.LayoutParams(FP, WC);
				//LayoutParams.debug():レイアウト設定内容を表示
				Log.d("LayoutParams.debug()",params.debug("layout_main params:"));
				mLayouts[i].addView(kokuban,params);
				mLayouts[i].setPadding(10, 10, 10, 10);	//padding
			}
			if(layoutNames[i].matches("layout_input")){
				numberTable = new NumberTable(context, mainHandler);
				params = new LinearLayout.LayoutParams(0, FP , 0.6f);	//--(1)
				//LayoutParams()の引数(int width,int height, float weight)を使う
//				params.weight = 0.6f;
				mLayouts[i].addView(numberTable, params);

				subLinear = new SubLinear(context, mainHandler);
//				params = new LinearLayout.LayoutParams(0, FP , 0.4f);
//				params.weight = 0.4f;
				//addView()メソッドに直接LayoutParamsを書く
				mLayouts[i].addView(subLinear ,new LinearLayout.LayoutParams(0, FP , 0.4f));
			}
			if(layoutNames[i].matches("layout_footer")){
				mFooter = new MyFooter(context);
				params = new LinearLayout.LayoutParams(FP,WC);
				mLayouts[i].addView(mFooter,params);
			}
		}

		//Main Thread start
		thread = new Thread(runner);
		thread.start();

	}

	@Override
	protected void onResume() {
		// 結果アクティビティからの復帰のときによbレル
		super.onResume();

		//TODO 値を初期化する記述
		/*
		 * ここに
		 * 
		 */
		thread = new Thread(runner);
		thread.start();
	}
	Runnable runner = new Runnable() {
		@Override
		public void run() {
			//XXX Log
			Log.v("Main runner", "Thread" + Thread.currentThread().getId());
			// 初期化
			keikaTime = 0;

			String str;
			// 問題ループ開始
			while (thread != null) {
				// Log.v("run",""+thread.getId());
				// 初期画面(タイトル画面)のときは「げーむかいし」を表示してループ待機
				if (getScene() == S_TITLE) {
					str = "げーむをはじめる";
					kokuban.setMessage(str);
				} else if (getScene() == S_PLAY) {
					// 問題を行う
					doMondai();
				} else if (getScene() == S_GAMEOVER) {
					str = "おわり";
					kokuban.setMessage(str);
					thread = null;
				}
				try {
					Thread.sleep(50);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}

			}
			// 問題ループ終了
			thread = null;
			// 終了したら、画面にメッセージを表示し、scene = "S_GAMEOVER"をセットする

			setScene(S_GAMEOVER);
		}
	};

	private void doMondai() {
		for (int i = 0; i < mondaiLength; i++) {
			kokuban.setMondaiNo("" + (i + 1) + "問目");
			keikaTime = 0;
			while (keikaTime < maxTime) {

				try {
					Thread.sleep(countUpTime);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}

				// 経過時間を加算
				keikaTime += countUpTime;
			}
		}
		// 問題終了のセット
		setScene(S_GAMEOVER);
	}

	public static int getScene() {
		return scene;
	}

	public static void setScene(int scene) {
		Main.scene = scene;
	}

	/**
	 * MainHandler static 内部クラス
	 * 
	 * @呼び出し方:Handler handler = new MainHandler(this);
	 */
	private static class MainHandler extends Handler {
		private final WeakReference<Main> mActivity;

		public MainHandler(Main activity) {
			mActivity = new WeakReference<Main>(activity);

			//XXX Log
			Log.v("MainHandler Constractor", "Thread"
					+ Thread.currentThread().getId());
		}

		@Override
		public void handleMessage(Message message) {
			//XXX Log
			Log.v("MainHandler handleMessage", "Thread"
					+ Thread.currentThread().getId());

			Main activity = mActivity.get();
			if (activity == null)
				return;

			switch (message.what) {
			case WHAT_KOKUBAN_TOUCH: // 黒板ビューのタッチイベントのメッセージ
				// scene定数の値によって分岐
				if (Main.getScene() == Main.S_TITLE) {
					Main.setScene(Main.S_PLAY); // ゲームを開始する
				} else if (Main.getScene() == Main.S_GAMEOVER) {
					// 結果画面へ遷移する

					Intent intent = new Intent(
							activity.getApplicationContext(),
							KekkaActivity.class);
					/*
					 * PendingIntentで送信する:動作OK
					 * startActivityではエラー
					 */
					//					activity.getApplication().startActivity(intent);
					// getActivityでPendingIntentを生成
					PendingIntent pending = PendingIntent.getActivity(
							activity.getApplicationContext(), 0, intent, 0);
					try {
						pending.send();
					} catch (CanceledException e) {
						e.printStackTrace();
					}
				}

				//TODO タッチイベントで黒板の図形など選択結果を取得する場合はここに記述すること
				//				View view = (View) message.obj;

				break;
			case WHAT_INPUT_CLICK: // 入力パネルからのメッセージ
				// 回答を書き込む
				activity.kokuban.setKotae("" + message.obj);

				// getTag()が"ok"のとき:こたえボタンクリック時には、mondaiクラスへ書き込む
				break;

			}

		}
	}

}

MyNumberTable.java

package com.efolab.p003.mainActivity;

import com.efolab.p003.Main;

import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TableLayout;
import android.widget.TableRow;

public class NumberTable extends TableLayout{
	private static final int 
	WC = ViewGroup.LayoutParams.WRAP_CONTENT,
	FP = ViewGroup.LayoutParams.FILL_PARENT;
	
	//UIスレッドのHandler
	Handler mainHandler ;
	
	public NumberTable(Context context , Handler h) {
		super(context);
		
		//mainHandler set
		mainHandler = h;
		
		//XXX thisはTableLayout
	    
	    /*
	     * TableLayolutの内容をセットする
	     */
	    
	    //1行目から3行目まで
	    Button[] buttons = new Button[9];
	    final int iMaxRow = 3;
	    final int iMaxCol = 3;
	    int index = 0;
	    for(int iRow=0;iRow<iMaxRow;iRow++){
	    	TableRow row = new TableRow(context);
	    	for(int iCol = 0;iCol<iMaxCol;iCol++){
	    		String value = ""+(index+1) % 10;
	    		//Buttonインスタンス
	    		buttons[index] = new Button(context);
	    		buttons[index].setText(value);
	    		buttons[index].setTag(value);
	    		//listener set
	    		buttons[index].setOnClickListener(inputListener);
	    		row.addView(buttons[index]);
	    		//buttons layout
	    		TableRow.LayoutParams _params = new TableRow.LayoutParams(0 , FP);
	    		_params.weight = 0.33f;
	    		buttons[index].setLayoutParams(_params);
	    		buttons[index].setGravity(Gravity.CENTER);
	    		
	    		index++;
	    	}
	    	this.addView(row);
	    	
	    }
	    //4行目に追加
	    TableRow row_4 = new TableRow(context);
	    Button btn_0 = new Button(context);
	    btn_0.setText("0");
	    btn_0.setTag("0");
	    row_4.addView(btn_0);
	    this.addView(row_4);
	    TableRow.LayoutParams lpRow_4 = new TableRow.LayoutParams(0 , WC);
	    lpRow_4.weight = 1.0f;	//横幅をいっぱいに
	    btn_0.setLayoutParams(lpRow_4);
	    
	    //listener set
	    btn_0.setOnClickListener(inputListener);
	}
	/*
	 * 入力(InputTable,SubLinear)を受け取るリスナ
	 * 受け取った値をmainHandlerへメッセージ送信する
	 * message.what = Main.WHAT_INPUT_CLICK
	 */
	OnClickListener inputListener = new OnClickListener() {

		@Override
		public void onClick(View v) {
			// 回答を書き込む
			Message msg = Message.obtain();
			msg.what = Main.WHAT_INPUT_CLICK;
			msg.obj = ("" + v.getTag());
			mainHandler.sendMessage(msg);
			// getTag()が"ok"のとき:こたえボタンクリック時には、mondaiクラスへ書き込む
		}

	};

}

SubLinear.java

package com.efolab.p003.mainActivity;

import com.efolab.p003.Main;

import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.LinearLayout;

public class SubLinear extends LinearLayout{
	@SuppressWarnings("unused")
	private static final int 
	WC = ViewGroup.LayoutParams.WRAP_CONTENT,
	FP = ViewGroup.LayoutParams.FILL_PARENT;
	
	//Handler
	Handler mainHandler;
	
	public SubLinear(Context context ,Handler h) {
		super(context);
		// TODO Auto-generated constructor stub
		
		//handler set
		mainHandler = h;
		
		LinearLayout.LayoutParams params;
		this.setOrientation(LinearLayout.VERTICAL);
		
	    Button btn_cls = new Button(context);
	    btn_cls.setText("けす");
	    //LayoutParams インスタンス生成(1)
	    params = new LinearLayout.LayoutParams(FP,0);
	    params.weight = 0.25f;
	    this.addView(btn_cls ,params);
	    
	    Button btn_ok = new Button(context);
	    btn_ok.setText("こたえ");
	    //LayoutParams インスタンス生成(2) --それぞれにインスタンス生成必要
	    params = new LinearLayout.LayoutParams(FP,0);
	    params.weight = 0.75f;
	    this.addView(btn_ok, params);
	    
	    
	    //setTag
	    btn_cls.setTag("clear");
	    btn_ok.setTag("ok");
	    //set Listener
	    btn_cls.setOnClickListener(inputListener);
	    btn_ok.setOnClickListener(inputListener);
	}
	/*
	 * 入力(InputTable,SubLinear)を受け取るリスナ
	 * 受け取った値をmainHandlerへメッセージ送信する
	 * message.what = Main.WHAT_INPUT_CLICK
	 */
	OnClickListener inputListener = new OnClickListener() {

		@Override
		public void onClick(View v) {
			// 回答を書き込む
			Message msg = Message.obtain();
			msg.what = Main.WHAT_INPUT_CLICK;
			msg.obj = ("" + v.getTag());
			mainHandler.sendMessage(msg);
			// getTag()が"ok"のとき:こたえボタンクリック時には、mondaiクラスへ書き込む
		}

	};

}

MyKokuban.java

package com.efolab.p003.mainActivity;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

import com.efolab.p003.Main;

@SuppressLint("ViewConstructor")
public class MyKokuban extends View{
	
	//XXX String hoge = null;はdrawText時にエラーになるよ
	String kotaeStr = "";
	String mondaiStr = "";
	String message = "";
	String mondaiNo = "";
	Paint paint;
	
	//Handler
	Handler mainHandler;
	
	public MyKokuban(Context context , Handler h) {
		super(context);
		
		//handler set
		mainHandler = h;
		
		this.setBackgroundColor(Color.DKGRAY);	//黒板の色
		
		//paint
		paint = new Paint();
		paint.setColor(Color.WHITE);
		paint.setAntiAlias(true);
		paint.setTextSize(30);
//		paint.setTypeface(Typeface.SANS_SERIF);	//フォント指定
		//test
//		mondaiStr = "3 + 2 = ";
		
		//Viewにタッチリスナをセットする
		this.setOnTouchListener(kokubanTouchListener);
		
	}


	@Override
	protected void onDraw(Canvas canvas) {
		// TODO Auto-generated method stub
		super.onDraw(canvas);
		
		canvas.drawText(mondaiStr, 10, 100, paint);
		canvas.drawText(kotaeStr , 100 , 200 , paint);
		canvas.drawText(message, 50, 100, paint);
		canvas.drawText(mondaiNo, 10, 30, paint);
		
		
	}
	
	//ゲームを開始するメッセージを表示して、クリックするとゲーム開始
	Handler mHandler_1 = new Handler();
	public void setMessage(final String str){
		//XXX Runスレッドから呼ばれるため、Handlerを使う
		mHandler_1.post(new Runnable(){

			@Override
			public void run() {
				// TODO Auto-generated method stub
				message = str;
				
				invalidate();
				
			}});
	}
	//問題を表示する
	public void setMondai(String mondai){
		message = null;
	}
	//入力パネルからの答え入力を表示する
	public void setKotae(String ans){
		Log.v("","ans"+ans);
		if(ans.matches("ok")){
			mondaiStr += kotaeStr;
			kotaeStr = "";
		}
		else if(ans.matches("clear")){
			kotaeStr = "";
		}
		else{
			kotaeStr += ans;
		}
		invalidate();
	}
	//今、何問目かを表示する
	Handler mHandler_2 = new Handler();
	public void setMondaiNo(final String str){
		mHandler_2.post(new Runnable(){

			@Override
			public void run() {
				// TODO Auto-generated method stub
				mondaiNo = str;
				invalidate();
				
			}});
	}
	/*
	 * 黒板(View)のタッチイベントを取得するリスナ
	 */
	OnTouchListener kokubanTouchListener = new OnTouchListener() {
		@Override
		public boolean onTouch(View v, MotionEvent event) {
			if(event.getAction() == MotionEvent.ACTION_DOWN){
				//UIスレッドへメッセージ送信
				Message msg = Message.obtain();
				msg.what = Main.WHAT_KOKUBAN_TOUCH;
				msg.obj = v;		//タッチされたviewをセットしておく
				//TODO タッチされた、文字や図形を取得できるか?
				mainHandler.sendMessage(msg);
			}
			return true;
		}
	};

}