Android UI と OpenGL レンダラの共存 ( Android OpenGL フレームワーク “Rajawali” と戯れる #09 )

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

Android OpenGL フレームワーク "Rajawali" と戯れるシリーズ
第 09 回目は、Android 標準のユーザーインターフェイス ( 以後 UI ) と OpenGL レンダラとの共存について解説します。

レイアウトの使用

Rajawali の基本アクティビティクラスである RajawaliFragmentActivity 上には、Android 標準の UI を配置することが可能です。厳密には RajawaliFragmentActivity が保持する FrameLayout(mLayout) に任意のレイアウトを addView() するという実装により実現できます。処理のすべてを Java のコードで記述することも可能ですが、LayoutInflater クラスを使用することで、レイアウト XML を使用したレイアウトも可能です。

RajawaliFragmentActivity サブクラス内における実装例

LayoutInflater inflater = LayoutInflater.from(this);
LinearLayout layout = (LinearLayout)inflater.inflate(R.layout.main, mLayout, false); //main.xml
mLayout.addView(layout);

出力結果

以下のサンプルは、RajawaliFragmentActivity の FrameLayout に LinearLayout を追加して、LinearLayout 内にて定義済みのボタンとシークバーで 3D オブジェクトのパラメータを調整するものです。シークバーを操作するとき、値が常に更新されていることが確認できます。

ムービー

キャプチャ

ソース

main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity"
>

    <Button
        android:id="@+id/button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/button" />

    <SeekBar
        android:id="@+id/xSeekBar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:progress="0"
        android:max="359" />

    <SeekBar
        android:id="@+id/ySeekBar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:progress="0"
        android:max="359" />

    <SeekBar
        android:id="@+id/zSeekBar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:progress="0"
        android:max="359" />

    <TextView
        android:id="@+id/textView"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/textView"
        android:textAppearance="?android:attr/textAppearanceLarge" />

</LinearLayout>

MainActivity.java

package jp.classmethod.sample;

import javax.microedition.khronos.opengles.GL10;

import rajawali.RajawaliFragmentActivity;
import rajawali.animation.mesh.VertexAnimationObject3D;
import rajawali.lights.DirectionalLight;
import rajawali.parser.MD2Parser;
import rajawali.renderer.RajawaliRenderer;
import android.content.Context;
import android.content.res.Resources;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;

/**
 * MainActivity
 */
public class MainActivity extends RajawaliFragmentActivity {

    /** @private */
    private Lesson07_01Renderer mRenderer;

    /** @private */
    private TextView textView;

    /** @private */
    private Button button;

    /** @private */
    private SeekBar xSeekBar;

    /** @private */
    private SeekBar ySeekBar;

    /** @private */
    private SeekBar zSeekBar;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mRenderer = new Lesson07_01Renderer(this);
        mRenderer.setSurfaceView(mSurfaceView);
        setRenderer(mRenderer);

        LayoutInflater inflater = LayoutInflater.from(this);
        LinearLayout layout = (LinearLayout)inflater.inflate(R.layout.main, mLayout, false);

        mLayout.addView(layout);

        textView = (TextView)layout.findViewById(R.id.textView);

        OnClickListener clickListener = new OnClickListener() {
            @Override
            public void onClick(View v) {
                textView.setText("Swing negi!!");
                mRenderer.swingNegi();
            }
        };

        button = (Button)layout.findViewById(R.id.button);
        button.setOnClickListener(clickListener);

        OnSeekBarChangeListener seekBarChangeListener = new OnSeekBarChangeListener() {
            @Override
            /** トラッキング開始 */
            public void onStartTrackingTouch(SeekBar seekBar) {
            }
            @Override
            /** トラッキング中 */
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromTouch) {
                setRotationBySeekBar();
            }
            @Override
            /** トラッキング終了 */
            public void onStopTrackingTouch(SeekBar seekBar) {
                setRotationBySeekBar();
            }
        };

        xSeekBar = (SeekBar)layout.findViewById(R.id.xSeekBar);
        xSeekBar.setOnSeekBarChangeListener(seekBarChangeListener);

        ySeekBar = (SeekBar)layout.findViewById(R.id.ySeekBar);
        ySeekBar.setOnSeekBarChangeListener(seekBarChangeListener);

        zSeekBar = (SeekBar)layout.findViewById(R.id.zSeekBar);
        zSeekBar.setOnSeekBarChangeListener(seekBarChangeListener);
    }

    /** シークバーの値をレンダラに反映 */
    private void setRotationBySeekBar() {
        float rotX = (float)xSeekBar.getProgress();
        float rotY = (float)ySeekBar.getProgress();
        float rotZ = (float)zSeekBar.getProgress();
        mRenderer.setRotation(rotX, rotY, rotZ);
        textView.setText("rotX : " + Float.toString(rotX) + "\n" +
                         "rotY : " + Float.toString(rotY) + "\n" +
                         "rotZ : " + Float.toString(rotZ)   );
    }

    /** RajawaliRenderer のサブクラス */
    private class Lesson07_01Renderer extends RajawaliRenderer {

        /** 3D オブジェクト */
        protected VertexAnimationObject3D mObj;

        /** コンストラクタ */
        public Lesson07_01Renderer(Context context) {
            super(context);
            setFrameRate(60);
            setBackgroundColor(0x999999);
        }

        @Override
        /** シーン初期化 */
        protected void initScene() {
            Resources r = mContext.getResources();

            DirectionalLight light = new DirectionalLight();
            light.setPower(1.5f);
            light.setColor(0xff9900);
            light.setPosition(0f, 3f, -1f);

            MD2Parser parser = new MD2Parser(r, mTextureManager, R.raw.r_miku_md2);
            parser.parse();

            mObj = (VertexAnimationObject3D)parser.getParsedAnimationObject();
            mObj.addLight(light);
            mObj.setY(-.75f);
            mObj.setScale(.5f);

            addChild(mObj);

        }

        @Override
        /** フレーム描画 */
        public void onDrawFrame(GL10 glUnused) {
            super.onDrawFrame(glUnused);
        }

        /** 葱振り */
        public void swingNegi() {
            mObj.play("walk");
        }

        /** 3D オブジェクトの回転 */
        public void setRotation(float rotX, float rotY, float rotZ) {
            mObj.setRotation(rotX, rotY, rotZ);
        }
    }
}