2012年6月21日木曜日

[Android TIPS] iPhone みたいな ToggleButton

iPhone みたくスイッチ時にアニメーションする ToggleButton を自作してみました。
トグルボタンをタップすると8段階にアニメーションして状態が切り替わります。


サンプルの野良 apk はこちらからどうぞ。


パッケージ・エクスプローラーはこんなかんじ。


追加したのは下記のファイル
  • CustomToggleButton.java
  • toggle_off.xml
  • toggle_on.xml
  • toggle01~09.png

toggle01~09.png のボタンはこんなかんじ。


では、コードを順番に。まずは CustomToggleButton.java から。
package com.sample.togglesample;

import android.content.Context;
import android.graphics.drawable.AnimationDrawable;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ImageView;

/**
 * カスタムトグルボタンクラス
 */
public class CustomToggleButton extends ImageView {

 /** 値変更イベントリスナ */
 private OnToggleChangeListener mListener;

 /** チェック状態 */
 private boolean mChecked;

 /** チェック状態: setter */
 public boolean isChecked() {
  return mChecked;
 }

 /** チェック状態: getter */
 public void setChecked(boolean checked) {
  mChecked = checked;

  // チェック状態に応じて画像を変更
  if (mChecked) {
   this.setBackgroundResource(R.drawable.toggle09);
  } else {
   this.setBackgroundResource(R.drawable.toggle01);
  }

  // イベント発火
  if (mListener != null) {
   mListener.onChange(this, mChecked);
  }
 }

 /**
  * コンストラクタ
  * 
  * @param context
  * @param attrs
  */
 public CustomToggleButton(Context context, AttributeSet attrs) {
  super(context, attrs);
  
  // ウィジェットの初期化
  initWidget();
 }

 /**
  * ウィジェットの初期化
  */
 private void initWidget() {
  // 初期値は false に設定
  setChecked(false);

  // クリックされた際に自身の値を変更する
  setOnClickListener(new OnClickListener() {
   public void onClick(View v) {
    // 値を反転
    CustomToggleButton.this.setChecked(!CustomToggleButton.this.isChecked());

    // 実施するアニメーションを選択
    CustomToggleButton.this.setBackgroundResource(
      (CustomToggleButton.this.isChecked() ?
        R.drawable.toggle_on : R.drawable.toggle_off));

    // アニメーション開始
    AnimationDrawable frameAnimation = (AnimationDrawable)
      CustomToggleButton.this.getBackground();
    frameAnimation.start();
   }
  });
 }

 /**
  * イベントリスナの設定
  * 
  * @param listener
  */
 public void setOnToggleChangeListener(OnToggleChangeListener listener) {
  mListener = listener;
 }

 /**
  * 値変更イベントインタフェース
  */
 public interface OnToggleChangeListener {
  public void onChange(View v, boolean isChecked);
 }
}


次は toggle_off.xml。


    
    
    
    
    
    
    
    
    




次は toggle_on.xml。off の順番を逆にしただけ。


    
    
    
    
    
    
    
    
    




最後に main.xml と



    
    

    
    




MainActivity.java
package com.sample.togglesample;

import com.sample.togglesample.CustomToggleButton.OnToggleChangeListener;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;

/**
 * サンプル画面クラス
 */
public class MainActivity extends Activity {

 /** トグルボタンの状態表示テキストビュー */
 private TextView mTxtValue;

 /** カスタムトグルボタンウィジェット */
 private CustomToggleButton mToggleButton;

 /**
  * onCreate
  */
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.main);

  // トグルボタンが押された際に値をテキストビューに表示
  mToggleButton = (CustomToggleButton) findViewById(R.id.customToggle);
  mToggleButton.setOnToggleChangeListener(new OnToggleChangeListener() {
   public void onChange(View v, boolean isChecked) {
    // トグルボタンの値を表示
    refreshToggleValue();
   }
  });

  // トグルボタンの値を表示
  mTxtValue = (TextView) findViewById(R.id.txtValue);
  refreshToggleValue();
 }

 /**
  * トグルボタンの値を表示
  */
 private void refreshToggleValue() {
  if (mTxtValue != null && mToggleButton != null) {
   mTxtValue.setText(mToggleButton.isChecked() ? "On" : "Off");
  }
 }
}

こんなかんじー。 スワイプして切り替えれるようにしたいけど、まずはタップからやってみました :-)

2012年3月20日火曜日

[Android TIPS] Monkey Runner でアプリ自動実行

Monkey Runner について、これまでに
「一通りやったよ」というような記事があまりなかったと思うので、
今回は Monkey Runner でのアプリ自動実行をご紹介。


Monkey Runner というのは Andorid の操作を予め
決めておいて、それを基に自動実行できる仕組みです。
自動実行の途中でスクリーンショットもとれるので、
テストにはもってこいなわけです。

アプリのレイアウトに依存するところが大きいのですが、
複数の条件を切り替えてテストするような場面や、
長時間実行によるリソース使用量の変化を観測する
場合などに使えるツールです。


自動実行するアプリのサンプル
として、←のような四則演算を
行うアプリを取り上げます

アプリのソースは こちら からどうぞ。

まずは←のアプリをエミュレータで
実行できるように準備します。

また、Monkey Runner は
ボタンなどのウィジェットの操作を
座標を元に行うので、
エミュレータのサイズを 480x800
にします。




2012年1月30日月曜日

[Android TIPS] AlertDialogのボタンサイズを調整する

ダイアログのボタンサイズを調整したかったんすが、
ずっとやり方が分からなくて。苦手なテーマをいじったり、
自分でレイアウトを作ってみたり、色々やってたんすが、
ダイアログの下部に余白が残ってしまったりと、うまく
表示できなかったんです。

で、やっとやり方がわかったので書いてみました。

下記みたいなコードを書くと...

AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setIcon(android.R.drawable.ic_dialog_info);
builder.setTitle("こんばんわ");
builder.setMessage("一杯いかが?");
builder.setPositiveButton("ちょうだい", null);
builder.setNeutralButton("うーん", null);
builder.setNegativeButton("いいやまたあとで", null);
Dialog dialog = builder.create();
dialog.show();


こんな感じでボタン幅が均等になって、ボタンラベルが
2行になっちゃいます。うまくシュリンクしてくれれば
1行で収まるのに...

そんな時には、下記のように書くと...

final int WC = LinearLayout.LayoutParams.WRAP_CONTENT;
final int FP = LinearLayout.LayoutParams.FILL_PARENT;
final LinearLayout.LayoutParams layoutParams = 
 new LinearLayout.LayoutParams(WC, FP, 1);

AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setIcon(android.R.drawable.ic_dialog_info);
builder.setTitle("こんばんわ");
builder.setMessage("一杯いかが?");
builder.setPositiveButton("ちょうだい", null);
builder.setNeutralButton("うーん", null);
builder.setNegativeButton("いいやまたあとで", null);
Dialog dialog = builder.create();

dialog.setOnShowListener(new OnShowListener() {
 public void onShow(DialogInterface dialog) {
  Button btnPositive = ((AlertDialog)dialog).getButton(
   DialogInterface.BUTTON_POSITIVE);
  if (btnPositive != null) {
   btnPositive.setLayoutParams(layoutParams);
   btnPositive.setSingleLine();
  }
  
  Button btnNeutral = ((AlertDialog)dialog).getButton(
   DialogInterface.BUTTON_NEUTRAL);
  if (btnNeutral != null) {
   btnNeutral.setLayoutParams(layoutParams);
   btnNeutral.setSingleLine();
  }
  
  Button btnNegative = ((AlertDialog)dialog).getButton(
   DialogInterface.BUTTON_NEGATIVE);
  if (btnNegative != null) {
   btnNegative.setLayoutParams(layoutParams);
   btnNegative.setSingleLine();
  }
 }
});

dialog.show();


うまく収まってくれます。ポイントは ...

  • getButton() メソッドでボタンの参照がとれるけど、OnShowListener とかの DialogInterface 経由でとらないと null になっちゃいます。
  • setLayoutParams() メソッドで、幅を WRAP_CONTENT に、weight を1に設定します。
  • setSingleLine() メソッドで1行に収まるようにします。

そんなかんじー。

2012年1月9日月曜日

[Android TIPS] デバッグに有用な情報を取得する

前回の更新から1ヶ月半あけてしまった... まだ2回目なのに...
というようなことは最初から予想できたので気を取り直して... ;-)

今回は開発者がデバッグする際に有用になる情報を取得する方法です。

青空読手では下記の場合にデバッグ情報を GAE 経由で自分の Gmail にメールを投げています。

  • UncaughtExceptionHandler で補足したエラーが前回起動時にあった場合
  • 設定画面からコメント機能を通じてメッセージが送られた場合

収集しているデバッグ情報は下記のとおりです。

  • 端末情報
    • ブランド
    • 製造元
    • モデル
  • OS 情報
    • バージョン
    • SDK
    • ロケール
  • アプリ情報
    • バージョン
    • SharedPreferences の設定値

これらは下記のようなメソッドにより収集可能です。

public static String getDeviceInfo(Activity activity)
{
 String brand = Build.BRAND;
 String manufacturer = Build.MANUFACTURER;
 String model = Build.MODEL;
 
 DisplayMetrics metrics = new DisplayMetrics();
 activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
 int w = metrics.widthPixels;
 int h = metrics.heightPixels;
 
 return String.format("%s - %s - %s (%dx%d)", brand, manufacturer, model, w, h);
}

public static String getOsInfo()
{
 String relese = Build.VERSION.RELEASE;
 String sdk = Build.VERSION.SDK;
 Locale locale = Locale.getDefault();
 
 return String.format("%s (api: %s) %s", relese, sdk, locale);
}

public static String getVersion(Context context)
{
 try
 {
  PackageManager manager = context.getPackageManager();
  PackageInfo packInfo = manager.getPackageInfo(context.getPackageName(), 0);
  return packInfo.versionName;
 }
 catch (Exception e)
 {
  e.printStackTrace();
 }
 return "1.0.0";
}

とれる情報はこんな感じです。

  • KDDI - HTC - PC36100 (480x800), 2.3.4 (api: 10) ja_JP
  • KDDI - HTC - ISW12HT (540x960), 2.3.4 (api: 10) ja_JP
  • KDDI - KYOCERA - ISW11K (480x800), 2.3.5 (api: 10) ja_JP
  • KDDI - FUJITSU TOSHIBA MOBILE COMMUNICATIONS LIMITED - IS04 (480x854), 2.2.2 (api: 8) ja_JP
  • KDDI - FUJITSU TOSHIBA MOBILE COMMUNICATIONS LIMITED - ISW11F (720x1280), 2.3.5 (api: 10) ja_JP
  • KDDI - PANTECH - IS06 (480x800), 2.2.1 (api: 8) ja_JP
  • KDDI - SHARP - IS11SH (540x960), 2.3.3 (api: 10) ja_JP
  • samsung - samsung - SC-02C (480x800), 2.3.3 (api: 10) ja_JP
  • docomo - Sony Ericsson - SO-01C (480x854), 2.3.2 (api: 9) ja_JP
  • docomo - Sony Ericsson - SO-01D (480x854), 2.3.4 (api: 10) ja_JP
  • docomo - Sony Ericsson - SO-03C (480x854), 2.3.4 (api: 10) ja_JP
  • DOCOMO - FUJITSU - F-01D (1280x752), 3.2 (api: 13) ja_JP
  • dell - Dell Inc - 001DL (480x800), 2.2.2 (api: 8) ja_JP
  • softbank_jp - HTC - 001HT (480x800), 2.3.3 (api: 10) ja_JP
  • SBM - SHARP - SBM003SH (800x480), 2.2.1 (api: 8) ja_JP
  • LGE - LGE - L-04C (320x480), 2.2.2 (api: 8) ja_JP
  • acer - Acer - E140 (240x320), 2.2 (api: 8) ja_JP
  • Huawei - HUAWEI - Ideos (240x320), 2.2.1 (api: 8) ja_JP
  • ZiiLABS - Creative Technology Ltd - ZiiO7 (800x480), 2.2.1 (api: 8) ja
  • emxx - renesas - Full Android (800x480), 2.2.1 (api: 8) ja_JP

上記メソッドではアプリのバージョンは AndroidManifest.xml の android:versionName 属性の値がとれてきます。

そんなかんじー。