2015-08-30

ActionBar で Android アプリのメニューを作成

スポンサーリンク

前回作成したボール転がしゲームにメニューを追加してゲームのリトライ処理ができるように変更します。

ActionBar の表示


メニューを表示するために ActionBar が表示されるように MainActivity のコードを変更します。前回までは以下の一文で ActionBar を表示しないようにしていました。
requestWindowFeature(Window.FEATURE_NO_TITLE);
これを以下に書き換えます。
requestWindowFeature(Window.FEATURE_ACTION_BAR);
(※ この部分は削除しても ActionBar は表示されましたが、念のためコードには記述しておきます。)

ActionBar が表示されるようになりました。しかし ActionBar の高さの分だけゲーム画面が下にずれてしまっているため、ボールがゲーム画面外へと消えてしまいました。


FrameLayout から画面サイズを取得


画面サイズは FrameLayout から取得するように変更しました。このとき取得するタイミングに注意が必要で onCreate() で取得すると値が 0 になってしまうため、onWindowFocusChanged() で取得するようにします。BallHole の生成も同じメソッド内で実施しています。
@Override
public void onWindowFocusChanged(boolean hasFocus) {
    super.onWindowFocusChanged(hasFocus);
    width = framelayout.getWidth();
    height = framelayout.getHeight();

    ball = new Ball(this);
    ball.x = width / 2;
    ball.y = height / 2;

    hole = new Hole(this);
    Random rnd = new Random();
    hole.x = rnd.nextInt(
        width-(2*(hole.r+ball.radius)))+hole.r+ball.radius;
    hole.y = rnd.nextInt(
        height-(2*(hole.r+ball.radius)))+hole.r+ball.radius;

    framelayout.addView(hole);
    framelayout.addView(ball);
}

ボールが上手くゲーム画面内に収まるようになりました。


"menu_main.xml" の編集


最初にプロジェクトを作成した際に、一緒に作成されるメニュー項目の設定ファイルを編集します。[res/menu/menu_main.xml] を開き以下のように修正します。"Settings" は最初から定義されているので、リトライ処理用のメニュー項目を追加しています。
<menu xmlns:android=
    "http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    tools:context=".MainActivity">
    <item
        android:id="@+id/action_settings"
        android:title="@string/action_settings"
        android:orderInCategory="100"
        android:showAsAction="never" />
    <item
        android:id="@+id/action_retry"
        android:title="Retry" />
</menu>

ActionBar にメニュー項目が表示されるようにするため MainActivity に以下のコードを追加します。
@Override
public boolean onCreateOptionsMenu(Menu menu) {
    MenuInflater inflater = getMenuInflater();
    inflater.inflate(R.menu.menu_main, menu);
    return super.onCreateOptionsMenu(menu);
}

"Retry""Settings" のメニュー項目が表示されました。

(※ 表示がおかしくなっていますが、後で対処します。)

リトライ処理の追加


メニュー項目の "Retry" を押したときの処理を MainActivity に追加します。先ほどの状態だとメニュー項目の表示と非表示を繰り返すたびに addView() が実行され、表示がおかしくなっていたためフラグを使って最初と "Retry" を押したときだけ addView() が実行されるように修正しました。
@Override
public boolean onOptionsItemSelected(MenuItem item) {
    if (item.getItemId() == R.id.action_retry) {
        framelayout.removeAllViews();
        handler.removeCallbacks(this);
        init_flag = false;
    }
    return super.onOptionsItemSelected(item);
}

Boolean init_flag;

@Override
public void onWindowFocusChanged(boolean hasFocus) {
    super.onWindowFocusChanged(hasFocus);

    if (!init_flag) {
        width = framelayout.getWidth();
        height = framelayout.getHeight();

        ball = new Ball(this);
        ball.x = width / 2;
        ball.y = height / 2;

        hole = new Hole(this);
        Random rnd = new Random();
        hole.x = rnd.nextInt(
            width - (2 * (hole.r + ball.radius)))
            + hole.r + ball.radius;
        hole.y = rnd.nextInt(
            height - (2 * (hole.r + ball.radius)))
            + hole.r + ball.radius;

        framelayout.addView(hole);
        framelayout.addView(ball);

        init_flag = true;
        handler.postDelayed(this, 3000);
    }
}

以上で、正常にリトライ処理ができるようになりました。"MainActivity.java" のソースコードは以下のようになっています。
public class MainActivity extends Activity implements Runnable, SensorEventListener {
    SensorManager manager;
    Ball ball;
    Hole hole;
    Handler handler;
    int width, height, time;
    float gx, gy, dpi;
    FrameLayout framelayout;
    Boolean init_flag;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getWindow().addFlags(
                WindowManager.LayoutParams.FLAG_FULLSCREEN);
        requestWindowFeature(Window.FEATURE_ACTION_BAR);
        framelayout = new FrameLayout(this);
        framelayout.setBackgroundColor(Color.GREEN);
        setContentView(framelayout);

        time = 10;
        handler = new Handler();
        init_flag = false;

        dpi = getResources().getDisplayMetrics().densityDpi;
    }
    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        super.onWindowFocusChanged(hasFocus);

        if (!init_flag) {
            width = framelayout.getWidth();
            height = framelayout.getHeight();

            ball = new Ball(this);
            ball.x = width / 2;
            ball.y = height / 2;

            hole = new Hole(this);
            Random rnd = new Random();
            hole.x = rnd.nextInt(width - (2 * (hole.r + ball.radius))) + hole.r + ball.radius;
            hole.y = rnd.nextInt(height - (2 * (hole.r + ball.radius))) + hole.r + ball.radius;

            framelayout.addView(hole);
            framelayout.addView(ball);

            init_flag = true;
            handler.postDelayed(this, 3000);
        }
    }
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.menu_main, menu);
        return super.onCreateOptionsMenu(menu);
    }
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        if (item.getItemId() == R.id.action_retry) {
            framelayout.removeAllViews();
            handler.removeCallbacks(this);
            init_flag = false;
        }
        return super.onOptionsItemSelected(item);
    }
    @Override
    public void run() {
        ball.vx += (float) (gx * time / 1000);
        ball.vy += (float) (gy * time / 1000);
        ball.x += dpi * ball.vx * time / 25.4;
        ball.y += dpi * ball.vy * time / 25.4;

        if (ball.x <= ball.radius) {
            ball.x = ball.radius;
            ball.vx = -ball.vx / 3;
        } else if (ball.x >= width - ball.radius) {
            ball.x = width - ball.radius;
            ball.vx = -ball.vx / 3;
        }

        if (ball.y <= ball.radius) {
            ball.y = ball.radius;
            ball.vy = -ball.vy / 3;
        } else if (ball.y >= height - ball.radius) {
            ball.y = height - ball.radius;
            ball.vy = -ball.vy / 3;
        }

        if ((hole.x - hole.r < ball.x && ball.x < hole.x + hole.r) &&
                (hole.y - hole.r < ball.y && ball.y < hole.y + hole.r)) {
            ball.x = hole.x;
            ball.y = hole.y;
            ball.vx = ball.vy = 0;
            ball.invalidate();
        } else {
            ball.invalidate();
            handler.postDelayed(this, time);
        }
    }
    public void onDestroy() {
        super.onDestroy();
        handler.removeCallbacks(this);
    }
    @Override
    protected void onResume() {
        super.onResume();
        manager = (SensorManager)getSystemService(
                SENSOR_SERVICE);
        List<Sensor> sensors =
                manager.getSensorList(
                        Sensor.TYPE_ACCELEROMETER);
        if (0 < sensors.size()) {
            manager.registerListener(
                    this, sensors.get(0),
                    SensorManager.SENSOR_DELAY_NORMAL);
        }
    }
    @Override
    protected void onPause() {
        super.onPause();
        manager.unregisterListener(this);
    }
    @Override
    public void onSensorChanged(SensorEvent event) {
        gy = event.values[0];
        gx = event.values[1];
    }
    @Override
    public void onAccuracyChanged(
            Sensor sensor, int accuracy) {
    }
}


リトライ処理が実装でき、ゲームとしての体裁がさらに整ってきました。次は設定画面の作成手順を紹介します。
スポンサーリンク

0 件のコメント:

コメントを投稿