サンプルプログラム工場

AAKAKA Appで使われているプログラムのサンプルコードをそのまま公開!

サンプルプログラム工場 > android > 画像をスライドするサンプル#trySurfaceView01
Google Play AAKAKAxSOFTへ

画像をスライドするサンプル#trySurfaceView01

実行ファイル(APK)やサンプル(zip)をダウンロードする
trySurfaceView01の実行イメージtrySurfaceView01の実行イメージ

XMLレイアウト内に埋め込んだ拡張したSurfaceViewを使ってフリックすると画像がスライドする様にする。
SurfaceView以外はタッチイベント処理をしたくないので、SurfaceViewに対してタッチイベントを埋め込む。
SurfaceViewをフリックした強さによって速さをかえてだんだんスライドするスピードが遅くなる様にする。
なんかフレームレートが安定しない。やっぱりOpenGLを使った方がいいのかもしれない。

大事なところ
 main.xmlに追加したSurfaceViewをカスタマイズしたSurfaceViewに置き換える
  例えばtrial.sample.trysurfaceview01.SurfaceViewExtの様に。
 カスタマイズしたSurfaceViewのコンストラクタは3種類とも用意する。
 画像のファイル名に大文字は使えない trySurfaceView00GB.pngとかはだめ
  try_surface_view_00_bg.pngとかにする(小文字の0-9とa-zに_.だけ使える)

検索した単語
 android SurfaceView 画像を表示する
 android 親のレイアウト サイズ
 android SurfaceView Bitblt
 定期的に更新 Android
 Runnable Android
 Android ゲームループ

開発環境
 Eclipse IDE バージョン: 3.7 Indigo Service Release 2
 ターゲットプラットフォーム: 2.1
 API レベル: 7

package trial.sample.trysurfaceview01;

import java.util.ArrayList;
import java.util.List;

import android.app.Activity;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Bundle;
import android.util.AttributeSet;
import android.util.Log;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.View.OnTouchListener;

public class TrySurfaceView01Activity extends Activity {
	
	private SurfaceView mSurfaceView;
	
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }

    
}


//////////////////////////////////////////////////////////////
//サーフェイスビューの拡張
class SurfaceViewExt extends SurfaceView implements SurfaceHolder.Callback, GestureDetector.OnGestureListener, OnTouchListener, Runnable {

	private final static int ONE_FRAME_TICK = 1000 / 25;	// 1フレームの時間
	private final static int MAX_FRAME_SKIPS = 5;			// 時間が余ったとき最大何回フレームをスキップするか

	
	private SurfaceHolder mSurfaceHolder;	// サーフェイスホルダー
	private GestureDetector	mGestureDetector;	// ジェスチャー処理
	private ImageScroller mImageScroller;	// イメージスクロール
	private Thread mMainLoop;	// メインのゲームループの様なモノ
	
	// 画像を表示するためのモノ
	final Resources mRes = this.getContext().getResources();
	List<Bitmap>	mBitmapList;
	
	// ////////////////////////////////////////////////////////////
	// コンストラクタ
	public SurfaceViewExt(Context context) {
		super(context);
		
		// Bitmapをロードする
		this.loadBitmap();

		// SurfaceViewの初期化
		this.initSurfaceView(context);
	}
	
	// ////////////////////////////////////////////////////////////
	// コンストラクタ
	public SurfaceViewExt(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		
		// Bitmapをロードする
		this.loadBitmap();

		// SurfaceViewの初期化
		this.initSurfaceView(context);
	}

	// ////////////////////////////////////////////////////////////
	// コンストラクタ
	public SurfaceViewExt(Context context, AttributeSet attrs) {
		super(context, attrs);
		
		// Bitmapをロードする
		this.loadBitmap();
		
		// SurfaceViewの初期化
		this.initSurfaceView(context);
	}
	
	// ////////////////////////////////////////////////////////////
	// Bitmapをロードする
	private void loadBitmap() {
		this.mBitmapList = new ArrayList<Bitmap>();
		
		// Bitmapをロードする
		this.mBitmapList.add(BitmapFactory.decodeResource(this.mRes, R.drawable.try_surface_view01_img00));
		this.mBitmapList.add(BitmapFactory.decodeResource(this.mRes, R.drawable.try_surface_view01_img01));
		this.mBitmapList.add(BitmapFactory.decodeResource(this.mRes, R.drawable.try_surface_view01_img02));
		this.mBitmapList.add(BitmapFactory.decodeResource(this.mRes, R.drawable.try_surface_view01_img03));
		this.mBitmapList.add(BitmapFactory.decodeResource(this.mRes, R.drawable.try_surface_view01_img04));
		this.mBitmapList.add(BitmapFactory.decodeResource(this.mRes, R.drawable.try_surface_view01_img05));
		this.mBitmapList.add(BitmapFactory.decodeResource(this.mRes, R.drawable.try_surface_view01_img06));
		this.mBitmapList.add(BitmapFactory.decodeResource(this.mRes, R.drawable.try_surface_view01_img07));
		
		// スクローラーを作る
		this.mImageScroller= new ImageScroller(
				this.mBitmapList.get(0).getWidth(),
				this.mBitmapList.size());
	}
	
	// ////////////////////////////////////////////////////////////
	// SurfaceViewの初期化
	private void initSurfaceView(Context context) {
		// サーフェイスホルダーを取り出す
		this.mSurfaceHolder = this.getHolder();
		// コールバック関数を登録する
		this.mSurfaceHolder.addCallback(this);
		
		// タッチリスナーをセットする
		this.setOnTouchListener(this);
		// ジェスチャー処理
		this.mGestureDetector = new GestureDetector(this);
		
		// これがないとGestureDetectorが動かないよ!
		this.setClickable(true);
	}

	
	// ////////////////////////////////////////////////////////////
	// Bitmapの拡大率を出す
	private float calcBitmapScale(int canvasWidth, int canvasHeight, int bmpWidth, int bmpHeight) {

		// 最初は幅で調べる
		float scale = (float)canvasWidth / (float)bmpWidth;
		float tmp = bmpHeight * scale;

		// 画像の高さがキャンバスの高さより小さい(余白ができてしまう場合)高さの方で横幅をスケールする
		if (tmp < canvasHeight) {
			scale = (float)canvasHeight / (float)bmpHeight;
			return scale;
		}
		
		return scale;
	}
	
	// ////////////////////////////////////////////////////////////
	// 描画処理
	public void draw(final Canvas canvas) {
		
		List<ImageOffset> imageOffsetList = this.mImageScroller.getImageOffsetList();
		// Bitmapを表示する
		Paint paint = new Paint();
		
		// 順番にオフセットをとって表示する
		for (int i = 0, len = imageOffsetList.size(); i < len; i++) {
			ImageOffset currOffset = imageOffsetList.get(i);
			canvas.drawBitmap(this.mBitmapList.get(currOffset.imageIndex), currOffset.screenOffsetW, 0, paint);
		}
	}
	
	// ////////////////////////////////////////////////////////////
	// サーフェイスサイズの変更があったときとかに呼ばれる
	public void surfaceChanged(SurfaceHolder holder, int format, int width,
			int height) {
		
		this.mImageScroller.setScreenWidth(width);

	}

	// ////////////////////////////////////////////////////////////
	// サーフェイスが作られたときに呼ばれる
	public void surfaceCreated(SurfaceHolder holder) {
		// ワーカースレッドを作る
		this.mMainLoop = new Thread(this);
		this.mMainLoop.start();
	}

	// ////////////////////////////////////////////////////////////
	// サーフェイスが破棄された時に呼ばれる
	public void surfaceDestroyed(SurfaceHolder holder) {
		this.mMainLoop = null;
	}
	
	
	// ////////////////////////////////////////////////////////////
	// 毎フレーム呼ばれるやつ
	public void move() {
		this.mImageScroller.move();
	}
	
// ////////////////////////////////////////////////////////////
// 毎フレーム呼ばれるやつ
// ちょっと上等なメインループ
	public void run() {
		Canvas canvas;
		long beginTime;	// 処理開始時間
		long pastTick;	// 経過時間
		int sleep = 0;
		int frameSkipped;	// 何フレーム分スキップしたか
		
		// フレームレート関連
		int frameCount = 0;
		long beforeTick = 0;
		long currTime = 0;
		String tmp = "";
		
		// 文字書いたり
		Paint paint = new Paint();
	    paint.setColor(Color.RED);
	    paint.setAntiAlias(true);
	    paint.setTextSize(60);
	
		// スレッドが消滅していない間はずっと処理し続ける
		while (this.mMainLoop != null) {
			
			canvas = null;

			// フレームレートの表示
			frameCount++;
			currTime = System.currentTimeMillis();
			if (beforeTick + 1000 < currTime) {
				beforeTick = currTime;
				tmp = "" + frameCount;
				frameCount = 0;
			}
			
			try {
				canvas = this.mSurfaceHolder.lockCanvas();
				canvas.drawColor(Color.WHITE);

				synchronized (this.mSurfaceHolder) {
					// 現在時刻
					beginTime = System.currentTimeMillis();
					frameSkipped = 0;
					
					// ////////////////////////////////////////////////////////////
					// ↓アップデートやら描画やら
					this.move();
					this.draw(canvas);
					// ////////////////////////////////////////////////////////////
					
					// 経過時間
					pastTick = System.currentTimeMillis() - beginTime;
					
					// 余っちゃった時間
					sleep = (int)(ONE_FRAME_TICK - pastTick);

					// 余った時間があるときは待たせる
					if (0 < sleep) {
						try {
							Thread.sleep(sleep);
						} catch (Exception e) {}
					}
					
					// 描画に時間係過ぎちゃった場合は更新だけ回す
					while (sleep < 0 && frameSkipped < MAX_FRAME_SKIPS) {
						// ////////////////////////////////////////////////////////////
						// 遅れた分だけ更新をかける
						this.move();
						// ////////////////////////////////////////////////////////////
						sleep += ONE_FRAME_TICK;
						frameSkipped++;
					}
					canvas.drawText("FPS:" + tmp, 1, 100, paint);
				}
			} finally {
				// キャンバスの解放し忘れに注意
				if (canvas != null) {
					this.mSurfaceHolder.unlockCanvasAndPost(canvas);
				}
			}
		}
	}
	
    // ////////////////////////////////////////////////////////////
    // サーフェイスビューがタッチされたときにこっちに来るようにする
	public boolean onTouch(View v, MotionEvent event) {
		return this.mGestureDetector.onTouchEvent(event);
	}

// ////////////////////////////////////////////////////////////
// ジェスチャー関係
// ////////////////////////////////////////////////////////////
	public boolean onDown(MotionEvent e) {
		// TODO 自動生成されたメソッド・スタブ
		return false;
	}

	// ////////////////////////////////////////////////////////////
	// フリック
	public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
			float velocityY) {
		this.mImageScroller.setVelocity(-(velocityX / 10.f));
		return false;
	}

	public void onLongPress(MotionEvent e) {
		// TODO 自動生成されたメソッド・スタブ
		
	}

	public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
			float distanceY) {
		// TODO 自動生成されたメソッド・スタブ
		return false;
	}

	public void onShowPress(MotionEvent e) {
		// TODO 自動生成されたメソッド・スタブ
		
	}

	public boolean onSingleTapUp(MotionEvent e) {
		// TODO 自動生成されたメソッド・スタブ
		return false;
	}


}


// ////////////////////////////////////////////////////////////
// 画像のオフセット
class ImageOffset {
	public int imageIndex;	// 画像のインデックス
	public int screenOffsetW;	// スクリーンオフセット
	
}


// ////////////////////////////////////////////////////////////
// イメージのスクロール管理
class ImageScroller {
	
	public enum ScrollProcess {
		SCROLL_MOVE,
		SCROLL_SNAP,
		SCROLL_STOP,
	}
	

	private float mCursor = 0.0f;	// 今の位置
	private float mVelocity = 0.0f;	// 加速度
	private int mImageWidth;	// 画像幅
	private int mScreenWidth;	// スクリーン幅
	private int mImageCount;	// 画像の枚数
	private int mTotalImageWidth;	// 画像全部合わせた画像のサイズ
	private int mInScreenImageCount;	// スクリーン内に入る画像の枚数
	
	private ScrollProcess mCurrProcess;	// 処理番号
	private float mSnapPos;	// スナップする位置
	
	// コンストラクタ
	public ImageScroller(int imageWidth, int imageCount) {
		this.mCursor = 0;
		this.mVelocity = 0.0f;
		this.mImageWidth = imageWidth;
		this.mScreenWidth = 0;
		this.mImageCount = imageCount;
		this.mTotalImageWidth = this.mImageCount * this.mImageWidth;
		this.mInScreenImageCount = (this.mScreenWidth / this.mImageCount) + 2;
		this.mCurrProcess = ScrollProcess.SCROLL_STOP;
	}
	
	// ////////////////////////////////////////////////////////////
	// スクリーンの幅を設定する
	public void setScreenWidth(int width) {
		this.mScreenWidth = width;
	}
	
	// ////////////////////////////////////////////////////////////
	// 毎フレームの処理
	public void move() {
		
		// 次の位置へ移動させる
		this.mCursor += this.mVelocity;
				
		switch (this.mCurrProcess) {
		case SCROLL_MOVE:
			{
				// だんだん減速
				this.mVelocity *= 0.98f;
				
				// 速度が適当に落ち着いたらスナップ処理へ
				if (Math.abs(this.mVelocity) < 20.0f) {
					
					// スナップする位置を決める
					int nearSnapPosLeft = (int)(this.mCursor / this.mImageWidth) * this.mImageWidth;
					int nearSnapPosRight = (int)((this.mCursor / this.mImageWidth) + 1) * this.mImageWidth;
					
					// 近い方へスナップする
					if (Math.abs(nearSnapPosLeft - this.mCursor) < Math.abs(nearSnapPosRight - this.mCursor)) {
						this.mSnapPos = nearSnapPosLeft;
					} else {
						this.mSnapPos = nearSnapPosRight;
					}
					
					// スナップ速度を設定
					this.mVelocity += (this.mSnapPos - this.mCursor) / 10;
					
					this.mCurrProcess = ScrollProcess.SCROLL_SNAP;
				}
				
				// スクリーンがすべてマイナス範囲に入っているときは右端にカーソルを移動させる
				if (this.mCursor + this.mScreenWidth < 0) {
					this.mCursor += this.mTotalImageWidth;
				}
				
				// スクリーンが全部の画像のサイズを合わせた位置より大きくなってる場合は左端に戻す
				if (this.mTotalImageWidth < this.mCursor) {
					this.mCursor -= this.mTotalImageWidth;
				}
			}
			break;
		case SCROLL_SNAP:
			{
				// 減速
				this.mVelocity *= 0.94f;
				// 速度を作る
				this.mVelocity += (this.mSnapPos - this.mCursor) / 10;

				// ほとんど動かなくなったらぴったりくっつけて終了
				if (Math.abs(this.mVelocity) < 2.0f && Math.abs(this.mSnapPos - this.mCursor) < 2.0f) {
					this.mCursor = this.mSnapPos;
					this.mVelocity = 0.0f;
					this.mCurrProcess = ScrollProcess.SCROLL_STOP;
				}
			}
			break;
		case SCROLL_STOP:
			{
				// 何もしない
			}
			break;
		}
	}
	

	// ////////////////////////////////////////////////////////////
	// 最初のイメージのインデックスを取り出す
	private int getFirstImageIndex() {
		// 画面の開始位置
		int screenStartPos = (int)this.mCursor;
		
		// カーソルが左端を越えている(スクロールして一番最後のイメージのほうへ)
		if (screenStartPos < 0) {
			screenStartPos += this.mTotalImageWidth;
		}
		
		return (int)(screenStartPos / this.mImageWidth);
	}
	
	
	public List<ImageOffset> getImageOffsetList() {
		List<ImageOffset> imageOffsetList = new ArrayList<ImageOffset>();
		
		// 最初のイメージのインデックス番号を取り出す
		int firstImageIndex = this.getFirstImageIndex();
		int imageCount = this.mImageCount;
		int offsetBase = (int)this.mCursor;
		
		// カーソルがマイナス、後ろへスクロールしている場合は全部プラスとして考える
		if (offsetBase < 0)
			offsetBase += this.mTotalImageWidth;
		offsetBase = (offsetBase % this.mImageWidth) * -1;
		
		// 画面内の入っている画像のオフセット値を表示する
		for (int i = 0, len = this.mInScreenImageCount; i < len; i++) {
		
			ImageOffset	offset = new ImageOffset();
			
			offset.imageIndex = (firstImageIndex + i) % imageCount;
			offset.screenOffsetW = offsetBase + (this.mImageWidth * i);
			
			// 画像のインデックス番号を取り出す
			imageOffsetList.add(offset);
		}
		
		return imageOffsetList;
	}
	
	// ////////////////////////////////////////////////////////////
	// 速度を設定する
	public void setVelocity(float speed) {
		this.mVelocity += speed;
		this.mCurrProcess = ScrollProcess.SCROLL_MOVE; 
	}
}

























サンプルプロジェクトをダウンロード APKファイルをダウンロード

, , , , ,

カスタムダイアログで値を入力するサンプル#tryCustomDialog00 GLSurfaceViewで画像をスライドするサンプル#tryGLES2005

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です


*

次のHTML タグと属性が使えます: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>