スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。



iko2 Android版リリース!!

ここ数ヶ月、試行錯誤していたiko2のAndroid版アプリがリリースされました。

https://play.google.com/store/apps/details?id=jp.co.akerusoft.iko2

docomo、au、Softbankのどのキャリアでも、Androidスマホであればプレイできます。
内容は携帯版オリジナルをそのまま再現!

いままでキャリアが違うために出来なかった方も
この機会にプレイしてみて下さい!!
スポンサーサイト

テーマ : ゲーム開発
ジャンル : コンピュータ




ここへ来てANR...

ANR(Application Not Responding)が発生した。
アプリが固まったように見えるというものです。

ANRが発生するとtraces.txt(/data/anr/traces.txt)が出力されるので
その内容をみても何が問題かわからない。

内容は

DALVIK THREADS:
(mutexes: tll=0 tsl=0 tscl=0 ghl=0 hwl=0 hwll=0)
"main" prio=5 tid=1 NATIVE
| group="main" sCount=1 dsCount=0 obj=0x400281b8 self=0xd088
| sysTid=15344 nice=0 sched=0/0 cgrp=default handle=-1345006464
| schedstat=( 125016091399 45731889951 132037 )
at android.graphics.Canvas.native_drawBitmap(Native Method)
at android.graphics.Canvas.drawBitmap(Canvas.java:1045)
at android.graphics.drawable.BitmapDrawable.draw(BitmapDrawable.java:325)
at android.graphics.drawable.DrawableContainer.draw(DrawableContainer.java:48)
at android.widget.ImageView.onDraw(ImageView.java:854)
at android.view.View.draw(View.java:6933)
at android.view.ViewGroup.drawChild(ViewGroup.java:1646)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1373)
at android.view.View.draw(View.java:6936)
at android.widget.FrameLayout.draw(FrameLayout.java:357)
at android.view.ViewGroup.drawChild(ViewGroup.java:1646)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1373)
at android.view.ViewGroup.drawChild(ViewGroup.java:1644)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1373)
at android.view.View.draw(View.java:6936)
at android.widget.FrameLayout.draw(FrameLayout.java:357)
at com.android.internal.policy.impl.PhoneWindow$DecorView.draw(PhoneWindow.java:1905)
at android.view.ViewRoot.draw(ViewRoot.java:1530)
at android.view.ViewRoot.performTraversals(ViewRoot.java:1266)
at android.view.ViewRoot.handleMessage(ViewRoot.java:1868)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:123)
at android.app.ActivityThread.main(ActivityThread.java:3691)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:507)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:864)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:622)
at dalvik.system.NativeStart.main(Native Method)



画像の描画処理で固まったのか???
原因が全くわからない。

やっとリリースだぁと思ってた矢先。
原因がわからないから対策できないなぁ。



Android アプリ内課金でProguardを使用するとき

Androidのアプリ内課金を実装してProguardを使用するとき,
Proguardの設定ファイルに追記しておいてと言われているものがあります。

公式リファレンスには以下のように書かれています。

Note: If you use Proguard to obfuscate your code, you must add the following line to your Proguard configuration file:
-keep class com.android.vending.billing.**



ここでいう設定ファイルはプロジェクト直下にあるproguard-project.txt。
このファイルにリファレンス通りに設定を書いて難読化してみると
普通に難読化されてました。

へ?

リファレンスを信じて検証していませんでしたが,どうやらこの設定ではGoogleが意図していた
aidlから自動生成されたインタフェースなどが難読化から除外されないようです。

とりあえず直書きで以下のように設定して対応しました。

-keep public interface com.android.vending.billing.IMarketBillingService
-keep public class com.android.vending.billing.IMarketBillingService$Stub
-keep class com.android.vending.billing.IMarketBillingService$Stub$Proxy

Googleの設定ではclassをキープしたかったように見えますが
多分インタフェース名だと思います。
自動生成されたIMarcketBillingServiceの内部クラスStubのメンバにはDESCRIPTORが設定されていて,
ここにインターフェースのパッケージ名が書かれています。

サービス接続時(onServiceConnected)に渡される引数IBinderからインターフェースを検索取得して
IMarketBillingServiceへキャストしている。
難読化しているときはキャスト前の型チェック(instanceof)で失敗していると考えられる。

ただそのときはaidlによって自動生成されたIMarketBillingService.javaに定義されている
Proxyクラスによって購入リクエストが実行できるようになっている...と考えられる。

難読化しても実行できるように組まれているんだけど,
リモート側の実装が変わったときに柔軟に対応できないんじゃないかな。

そのために難読化するときはIMarketBillingService.javaに定義されているクラスは除外してね
と言っているに違いない。
(ただIMarketBillingServiceへキャストできればよいので難読化対象外にするのはIMarketBillingServiceインタフェースだけで良さそう)

aidlで自動生成されたIMarketBillingService.java
/*
 * This file is auto-generated. DO NOT MODIFY. Original file:
 * /Users/junji/Develop
 * /workspace/BillingLibrary/src/com/android/vending/billing/
 * IMarketBillingService.aidl
 */
package com.android.vending.billing;

public interface IMarketBillingService extends android.os.IInterface
{
	/** Local-side IPC implementation stub class. */
	public static abstract class Stub extends android.os.Binder implements com.android.vending.billing.IMarketBillingService
	{
		private static final java.lang.String DESCRIPTOR = "com.android.vending.billing.IMarketBillingService";

		/** Construct the stub at attach it to the interface. */
		public Stub()
		{
			this.attachInterface(this, DESCRIPTOR);
		}

		/**
		 * Cast an IBinder object into an
		 * com.android.vending.billing.IMarketBillingService interface,
		 * generating a proxy if needed.
		 */
		public static com.android.vending.billing.IMarketBillingService asInterface(android.os.IBinder obj)
		{
			if((obj == null))
			{
				return null;
			}
			android.os.IInterface iin = (android.os.IInterface) obj.queryLocalInterface(DESCRIPTOR);

			// ここでリモート・クライアント間共通インターフェースで型チェックの後キャストしている。
			if(((iin != null) && (iin instanceof com.android.vending.billing.IMarketBillingService)))
			{
				return ((com.android.vending.billing.IMarketBillingService) iin);
			}
			return new com.android.vending.billing.IMarketBillingService.Stub.Proxy(obj);
		}

		public android.os.IBinder asBinder()
		{
			return this;
		}

		@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
		{
			switch(code)
			{
				case INTERFACE_TRANSACTION:
				{
					reply.writeString(DESCRIPTOR);
					return true;
				}
				case TRANSACTION_sendBillingRequest:
				{
					data.enforceInterface(DESCRIPTOR);
					android.os.Bundle _arg0;
					if((0 != data.readInt()))
					{
						_arg0 = android.os.Bundle.CREATOR.createFromParcel(data);
					}
					else
					{
						_arg0 = null;
					}
					android.os.Bundle _result = this.sendBillingRequest(_arg0);
					reply.writeNoException();
					if((_result != null))
					{
						reply.writeInt(1);
						_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
					}
					else
					{
						reply.writeInt(0);
					}
					return true;
				}
			}
			return super.onTransact(code, data, reply, flags);
		}

		private static class Proxy implements com.android.vending.billing.IMarketBillingService
		{
			private android.os.IBinder mRemote;

			Proxy(android.os.IBinder remote)
			{
				mRemote = remote;
			}

			public android.os.IBinder asBinder()
			{
				return mRemote;
			}

			public java.lang.String getInterfaceDescriptor()
			{
				return DESCRIPTOR;
			}

			/**
			 * Given the arguments in bundle form, returns a bundle for results.
			 */
			public android.os.Bundle sendBillingRequest(android.os.Bundle bundle) throws android.os.RemoteException
			{
				android.os.Parcel _data = android.os.Parcel.obtain();
				android.os.Parcel _reply = android.os.Parcel.obtain();
				android.os.Bundle _result;
				try
				{
					_data.writeInterfaceToken(DESCRIPTOR);
					if((bundle != null))
					{
						_data.writeInt(1);
						bundle.writeToParcel(_data, 0);
					}
					else
					{
						_data.writeInt(0);
					}
					mRemote.transact(Stub.TRANSACTION_sendBillingRequest, _data, _reply, 0);
					_reply.readException();
					if((0 != _reply.readInt()))
					{
						_result = android.os.Bundle.CREATOR.createFromParcel(_reply);
					}
					else
					{
						_result = null;
					}
				}
				finally
				{
					_reply.recycle();
					_data.recycle();
				}
				return _result;
			}
		}

		static final int TRANSACTION_sendBillingRequest = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
	}

	/** Given the arguments in bundle form, returns a bundle for results. */
	public android.os.Bundle sendBillingRequest(android.os.Bundle bundle) throws android.os.RemoteException;
}

難読化したときのclassファイルをリバースエンジニアリングしたとき,以下のとおり。

package a.a.a.a;

import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.os.IInterface;
import android.os.Parcel;
import android.os.Parcelable.Creator;

public abstract class b extends Binder
  implements a
{
  public static a a(IBinder paramIBinder)
  {
    Object localObject;
    if (paramIBinder == null)
      localObject = null;
    while (true)
    {
      return localObject;
      IInterface localIInterface = paramIBinder.queryLocalInterface("com.android.vending.billing.IMarketBillingService");

      // ここでリモート・クライアント間共通インターフェースで型チェックの後キャストしていたが難読化で
	  // クラス名が変更されている。
	  if ((localIInterface != null) && ((localIInterface instanceof a)))
        localObject = (a)localIInterface;
      else
        localObject = new c(paramIBinder);
    }
  }

  public boolean onTransact(int paramInt1, Parcel paramParcel1, Parcel paramParcel2, int paramInt2)
  {
    boolean bool;
    switch (paramInt1)
    {
    default:
    case 1598968902:
      for (bool = super.onTransact(paramInt1, paramParcel1, paramParcel2, paramInt2); ; bool = true)
      {
        return bool;
        paramParcel2.writeString("com.android.vending.billing.IMarketBillingService");
      }
    case 1:
    }
    paramParcel1.enforceInterface("com.android.vending.billing.IMarketBillingService");
    Bundle localBundle1;
    if (paramParcel1.readInt() != 0)
    {
      localBundle1 = (Bundle)Bundle.CREATOR.createFromParcel(paramParcel1);
      label81: Bundle localBundle2 = a(localBundle1);
      paramParcel2.writeNoException();
      if (localBundle2 == null)
        break label122;
      paramParcel2.writeInt(1);
      localBundle2.writeToParcel(paramParcel2, 1);
    }
    while (true)
    {
      bool = true;
      break;
      localBundle1 = null;
      break label81;
      label122: paramParcel2.writeInt(0);
    }
  }
}
ただ外部のサービスからクラス情報までロードされる気がしない。 開発環境でデバッグしてみると結局,難読化されていようがいまいが クラスはキャストされず代理クラスで実行されている。 googleドキュメント側の間違いじゃないか!?



AndroidのHandlerを使用した遅延実行

Javaで遅延実行するときはjava.util.concurrentパッケージのものを使用していけば良いと思うのですが,
せっかくAndroidなのでHandlerで遅延実行できないか検証してみました。

Handlerにも〜Delayed()メソッドがあり,指定時刻に実行できます。
さらにhasMessages()がありキューに既に追加されているかも確認することができます。

以下のようなコードになりました。
注意点はMessage.obtain()でRunnableを渡すとHandler.CallbackのhandleMessage()がコールされません。


public class HandlerCallBackActivity extends Activity implements Handler.Callback, OnClickListener
{
	static final private String TAG = "HandlerCallBackActivity";
	
	private Handler mHandler = new Handler(this);
	private Button mButton = null;
	private Message mMessage = null;
	
	// テキストを設定する
	static final private int HANDLER_SET_TEXT = 1;
	
	@Override public void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_handler_call_back);
		
		mButton = (Button)findViewById(R.id.button);
		mButton.setOnClickListener(this);
	}

	@Override public boolean handleMessage(Message msg)
	{
		Log.d(TAG, "handleMessage() msg:" + msg);

		if(msg==null)
		{
			Log.e(TAG, "Invalid param. Message is null.");
			return false;
		}
		
		if(msg.what==HANDLER_SET_TEXT)
		{
			Log.d(TAG, "Message is HANDLER_SET_TEXT. " + msg.obj);
			setTextButton();
		}
		else
		{
			Log.d(TAG, "Not HANDLER_SET_TEXT message.");
		}
		
		return false;
	}

	@Override public void onClick(View v)
	{
		if(v==mButton)
		{
			if(mMessage!=null)
			{
				// 既にメッセージを追加しているとき削除する
				if(mHandler.hasMessages(HANDLER_SET_TEXT, this))
				{
					Log.d(TAG, "remove message.");
					mHandler.removeMessages(HANDLER_SET_TEXT, this);
				}
			}

			// Memo) Callbackが設定されているとhandleMessage()に通知されない。
			//       Handler.dispatchMessage()みるとわかる。
//			mMessage = Message.obtain(mHandler, new Runnable());
			mMessage = mHandler.obtainMessage(HANDLER_SET_TEXT, this);
			
			if(!mHandler.sendMessageDelayed(mMessage, 2000))
			{
				Log.e(TAG, "Failed send message.");
			}
			else
			{
				Log.i(TAG, "Success send message.");
			}
			
			return;
		}
	}
	
	public void setTextButton()
	{
		final long time = System.currentTimeMillis();
		SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS");
		mButton.setText(sdf.format(new Date(time)));
	}
}

// MessageQueueの削除条件はobjectを等価でなくて等値として評価している
public class MessageQueue {
	final boolean hasMessages(Handler h, int what, Object object) {
        if (h == null) {
            return false;
        }

        synchronized (this) {
            Message p = mMessages;
            while (p != null) {
                if (p.target == h && p.what == what && (object == null || p.obj == object)) {
                    return true;
                }
                p = p.next;
            }
            return false;
        }
    }
}

これでHandlerを使用して遅延実行ができます。

テーマ : ゲーム開発
ジャンル : コンピュータ




おわらな〜い

課金のテストをしているとGoogle側のサーバがエラーを起こしてテストができなくなる。
こっち側の問題かと思っていたけど,次の日に同じテストすると問題なく実行できる。

対処法はしばらく待つ以外なさそう><


課金部分はライブラリ化した方がいいなぁと思ってjarファイル化しました。
Android用のJARファイルをエクポートするとき,
いろいろな制約(AndroidManifest.xmlは入れるなとか)があって
GUI上から毎回エクスポートするのも面倒!!

Antでエクスポートしてみました。
Antの記述が全く???だったので苦戦したけど
ボタン一つでエクスポートできるようになったぁ!!
楽&安心!!


これで必要なファイルを忘れるなんてこともなくなったぁ。

ということで,いつになったらリリースできるんだろうと
自分で自分が心配になる今日この頃w

もうしばらくお待ちください!!

テーマ : ゲーム開発
ジャンル : コンピュータ




Add Native Supportを使用すると「unresolved inclusion」のエラーがでる

ADT r20からNDKの準備にポップアップメニューから選択して自動で実行することができる。
(Add Native Support)
しかし不具合があって「unresolved inclusion jni.h」のようなエラーがでてしまう。

プロジェクトを閉じてから,再度開くと直ることもあるようですが,
こちらに報告されているように不具合のようです。
(英語が正しく読めていないかもしれないので間違っているかもしれません)

どうもCDT 8.1.x以降とADT r20の組み合わせが悪いようです。
リンク先のコメント#5にも書かれていたように以下の方法で一時的に回避することができるようです。

1.プロジェクトの設定 [C/C++ ビルド]-[ツール・チェーン・エディター]の[現在のツールチェーン]を
 「Android GCC」から「Cygwin GCC」へ変更する。
2.[C/C++ 一般]-[パスおよびシンボル]の[インクルード]にNDKのインクルードパスを設定する。

これでエラーが消える。
リンク先にも書かれているが,既に問題は修正されているようで次のADT r21がリリースされれば
[Add Native Support]は問題なく使えるはず。

ちなみに[Add Native Support]を使ったときに追加されるNDKのパスは以下のとおり。(Macの場合)

/sources/cxx-stl/system/include
jni
/toolchains/arm-linux-androideabi-4.6/prebuilt/darwin-x86/lib/gcc/arm-linux-androideabi/4.6.x-google/include
/toolchains/arm-linux-androideabi-4.6/prebuilt/darwin-x86/lib/gcc/arm-linux-androideabi/4.6.x-google/include-fixed
/platforms/android-14/arch-arm/usr/include


Androidの開発環境とEclipseの開発環境が平行して進んでいるから
こういった問題がおきるということだろうか...。

Visual Studioのようにワンパッケージで提供してくれないかなw

テーマ : ゲーム開発
ジャンル : コンピュータ




お知らせサイトとして

こちらのブログは開発者向けの情報が多くなってきたので
アプリのリリースタイミング等の情報は
Facebookページでお知らせしていこうと思っています。

で,とりあえず空白のページを作りました。

Facebookページ

なにをどうすればいいのかはわからないので
今後成長していく予定ですw

ではでは。

テーマ : ゲーム開発
ジャンル : コンピュータ




アプリ内課金についてまとめたサイト。

アプリ内課金について簡単にまとめたサイトがありました。

http://www.slideshare.net/akaiprivate/noanimation

API1から2への変更点など。
日本語なのでわかりやすい。

テーマ : ゲーム開発
ジャンル : コンピュータ




Androidで直線グラデーションを描画するポイント

Androidだと座標がintからfloatに変わったのと
解像度によって画面の大きさを拡大・縮小するため
連続した塗りつぶしをすると,細い線が空いてしまう。

なのでグラデーションを使用していた箇所がプツプツと途切れて
汚くなってしまうのでAndroid特有のグラデーションの塗りつぶしを使うことにしてみた。

グラデーションの種類(円や円環)がいくつかあるけど
今回使用するのは直線グラデーション。

結果はこちら...
device-2012-10-06-131913.png

グラデーションクラスLinearGradientに渡す開始・終了座標は後から変えることができないので
変更が必要なとき再生成する必要がある...。

描画で使用するものを描画の度に再生成なんて
現実的じゃないので,他にやる方法はないかと検証してみました。

グラデーションクラスに渡す座標は
Canvasの座標変換(translate)には影響を受けないということがわかりました。

ってことは,LinearGradientクラスには幅または高さを渡しておいて,
塗りつぶすときにCanvas.translate()を実行すればいい
って使い方が現実的だろうな。

コードはこちら。
(検証用でコードは汚いですが...)

package com.example.gradationtest;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Shader;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.View;

public class GradationTestActivity extends Activity
{
	@Override public void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		setContentView(new ScreenView(this));
	}

	static private class ScreenView extends View
	{
		private final String tag = getClass().getSimpleName();
		
		private final LinearGradient linearGradient1;
		private final LinearGradient linearGradient2;
		private final LinearGradient linearGradient3;
		private final Paint paintBitmap;
		private final Paint paint1;
		private final Rect rect1;
		private final Rect rect2;
		private final Rect rect3;
		private final Rect rect4;
		private final Rect rect5;
		private final Rect rect6;
		
		static final private int HEIGHT = 30;
		static final private int MARGIN_X = 40;
		
		// 画面の大きさを取得
		private final DisplayMetrics dm;
		
		// 描画領域の幅
		private final float width;
		
		public ScreenView(Activity activity)
		{
			super(activity);
			
			dm = new DisplayMetrics();
			activity.getWindowManager().getDefaultDisplay().getMetrics(dm);
			
			// 描画領域の幅
			width = dm.widthPixels - (2*MARGIN_X);
			
			final float startX = 0;
			final float endX = width/4;
			final float endY = 0;
			
			Log.d(tag, "endX:" + endX + ", endY:" + endY);
			
			final int c0 = Color.RED;
			final int c1 = Color.BLUE;
			
			linearGradient1 = new LinearGradient(startX, 0, endX, endY, c0, c1, Shader.TileMode.CLAMP);
			linearGradient2 = new LinearGradient(startX, 0, endX, endY, c0, c1, Shader.TileMode.MIRROR);
			linearGradient3 = new LinearGradient(startX, 0, endX, endY, c0, c1, Shader.TileMode.REPEAT);

			Matrix matrix = new Matrix();
			linearGradient1.getLocalMatrix(matrix);
			Log.d(tag, "" + matrix);
			
			paint1 = new Paint(Paint.ANTI_ALIAS_FLAG);
			paint1.setShader(linearGradient1);
			paint1.setStyle(Paint.Style.FILL);
			paint1.setTextSize(40f);

			final int half = (int)width/2;
			final int quarter = (int)width/4;
			final int right = dm.widthPixels - (2*MARGIN_X);
			rect1 = new Rect(0, 0, right, HEIGHT);
			rect2 = new Rect(quarter, 0, right, HEIGHT);
			rect3 = new Rect(half, 0, right, HEIGHT);
			rect4 = new Rect(quarter, 0, right - quarter, HEIGHT);
			rect5 = new Rect(0, 0, half, HEIGHT);
			rect6 = new Rect(0, 0, half, HEIGHT);
			
			paintBitmap = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
		}

		@Override protected void onDraw(Canvas canvas)
		{
			super.onDraw(canvas);
			drawGradient(canvas);
		}
		
		private void drawGradient(Canvas c)
		{
			Bitmap bitmap = Bitmap.createBitmap(dm.widthPixels, dm.heightPixels, Bitmap.Config.ARGB_8888);
			bitmap.setDensity(DisplayMetrics.DENSITY_DEFAULT);
			
			Canvas canvas = new Canvas(bitmap);
			
			final int quarter = (int)width/4;
			
			canvas.drawColor(Color.BLACK);
			
			final float marginY = 10f;
			float y = marginY;

			final int saveCount0 = canvas.save();
			
			try
			{
				canvas.translate(MARGIN_X, y);
				paint1.setShader(linearGradient1);
				canvas.drawRect(rect1, paint1);
				
				y += HEIGHT;
				canvas.translate(0, y);
				canvas.drawRect(rect2, paint1);
				
				canvas.translate(0, y);
				canvas.drawRect(rect3, paint1);
				
				canvas.translate(0, y);
				canvas.drawRect(rect4, paint1);
				
				canvas.translate(0, y);
				canvas.drawRect(rect5, paint1);
				
				canvas.translate(0, y);
	
				final int saveCount1 = canvas.save();
	
				try
				{
					// x座標の開始位置を変換したときのグラデーションを確認する
					canvas.translate(quarter, 0);
					canvas.drawRect(rect6, paint1);
				}
				finally
				{
					canvas.restoreToCount(saveCount1);
				}
				
				canvas.translate(0, y);
				paint1.setShader(linearGradient2);
				canvas.drawRect(rect1, paint1);
				
				canvas.translate(0, y);
				paint1.setShader(linearGradient3);
				canvas.drawRect(rect1, paint1);
			}
			finally
			{
				canvas.restoreToCount(saveCount0);
			}

			// グラデーションの色をログに出力
			for(int i=0; i < width; i++)
			{
				final int color = bitmap.getPixel(MARGIN_X+i, (int)(marginY));
				Log.d(tag, "x:" + i + "(" + Integer.toHexString(color) + ")");
			}
			
			// グラデーション開始色を確認する
			// Memo) 文字列の描画位置は適当
			final int startColor = bitmap.getPixel(MARGIN_X, (int)(marginY));
			canvas.translate(MARGIN_X, (int)(marginY + (y * 10)));
			canvas.drawText(Integer.toHexString(startColor), 0, paint1.ascent(), paint1);
			
			Matrix matrix = new Matrix();
			c.drawBitmap(bitmap, matrix, paintBitmap);
		}
	}
}

テーマ : ゲーム開発
ジャンル : コンピュータ




UI改善時のあれこれ

課金テストの環境が整わないので先にUI改善について調査を始めました。
小さいな検証を重ねているところで,そのときに気になったことをメモっておきます。

・R.idの予約
自分のパッケージのRクラス(android.Rじゃない)のidは自動的に生成される。
コードでViewを生成したときsetId()に設定する値は何がよいか...。
できればR.idでid管理したいんだけど...というとき。

http://developer.android.com/intl/ja/guide/topics/resources/more-resources.html#Id

タグにidを設定することができる。
これを使用すればR.id内で管理することができる。

・カスタムコンポーネントの作り方
公式のAndroidサイトで説明されている。

http://developer.android.com/intl/ja/guide/topics/ui/custom-components.html

日本語訳してくれているサイト
https://sites.google.com/a/techdoctranslator.com/jp/android/guide/ui/custom-components

親View(ViewGroup)からコントロールのサイズを計算するのにonMeasuer()が呼ばれるのだけど,
この引数はただのサイズではないということ。

この引数にはサイズとモードが設定されている。
その値の取得方法は以下のメソッドを使用する。

http://developer.android.com/intl/ja/reference/android/view/View.MeasureSpec.html

モードは3種類あるようです。
ざっくりサイズを変更できないとき, もっと大きくできるとき, 自由にサイズを設定できるとき。

問題なければsetMeasuredDimension()を設定する。

最終的に描画位置が決まったらonLayout()がコールされるので
ここで位置と幅などを計算しておいて後はonDraw()時に描画する。

不思議なのはonDraw()時にクリッピングエリアがonLayout()で設定された位置ではないこと。
onDraw()時に自前でクリッピングエリアを設定すればいいのだけど...。
最初から設定された状態にすることができないだろうか???

テーマ : ゲーム開発
ジャンル : コンピュータ




アクセス
あなたは
キーワード
カテゴリー
最近の記事
リンク
月別アーカイブ
ブロとも申請フォーム

この人とブロともになる

WEB検索
Google

RSSフィード
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。