2010年12月10日金曜日

2010年11月29日月曜日

傾き、加速度の手振れノイズをフィルターする

人間の手の震えもありますが、端末で取れる加速度センサーの値は、結構細かく震えています。
この加速度センサーの値をもとにLinearで中心にあるボールを動かしたかったのですが、この震えのせいで、
きれいに移動しないで、ぴょこぴょこと跳ねているような動きに見えてしまうので困りました。

センサーの精度かと思って、そちらを調査してみたり、
アニメーションの処理飛びなのかと思ってログを見てみたりしていたのですが、
特に解決策は見つからず。

結局のところこのブレを減らすための解決策が必要だろうと推測して、ノイズフィルターを探していたところ身近なところに解決策がありました。

DJミキサーでよくある、ハイパスフィルターとローパスフィルターです。

ハイパスフィルターは、つまみを回すとだんだんシャキシャキの音になってくあれですw。
ローパスフィルターは、つまみを回すとだんだんモコモコの音になってくあれですw。


WikiPedia ローパスフィルター(ハイカット)
ローパスフィルタ(Low-pass filter: LPF)はフィルタ回路の一種で、低周波を良く通し、ある遮断周波数より高い周波数の帯域を通さない(減衰させる)フィルタである。

WikiPedia ハイパスフィルター(ローカット)
ハイパスフィルタ(High-pass filter: HPF)はフィルタ回路の一種で、高周波を良く通し、遮断周波数より低い周波数の帯域を通さない(減衰させる)フィルタである。 日本語では「高域通過濾波器」とも言われる。また英語では「low-cut filter」とも言われ、これは「bass-cut filter」または「rumble filter」としてオーディオ機器などで使用される。


加速度は、重力加速度+手振れなどの実際の端末の動きが加速度が影響してきます。
あるXYZで考えてどれか一方向についての変化
重力加速度の変化:端末の大体の傾きになりますので周波数で考えるとだんだん傾いていれば低い周波数です。
端末の動きの変化:手振れなので重力加速度の変化より高い周波数のノイズになります。

ぴょこぴょこと動いてしまうのは、「端末の動きの変化」が影響しているので、高い周波数をカットすればいい。すなわち低い周波数だけ通せばいいということになります。

ってことで、ロジック。
一つ前の値と比較しながら、値を取ります。
XYだけの例です。

部分的に抜き出すとわけわからなくなったので、全部載せます。

イメージを最初に真ん中において、ローパスフィルタをかけた加速度で、イメージを移動しています。

package jp.mediba.android.xxxxxx;

import android.content.Context;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.util.Log;
import android.widget.ImageView;

public class PointViewOverlay extends ImageView {

 private Matrix mMatrix = new Matrix();
 private Matrix mSavedMatrix = new Matrix();
 private float[] currentOrientationValues = {0.0f, 0.0f};
 
 private PointF startPoint = new PointF();
 
 private ImageView imageView;
 private int frameWidth = 0;
 private int frameHeight = 0;
 
 public PointViewOverlay(Context context) {
  super(context);
  // TODO 自動生成されたコンストラクター・スタブ
 }

 public void setImageView(ImageView imageView) {this.imageView = imageView;}
 public void setFrameSize(int frameWidth, int frameHeight) {
  this.frameWidth = frameWidth;
  this.frameHeight = frameHeight;
 }
 
 public void resetStartPoint() {
  mMatrix.set(mSavedMatrix);
  this.startPoint = new PointF((float)this.frameWidth/2,(float)this.frameHeight/2);
  mMatrix.postTranslate(this.startPoint.x, this.startPoint.y);
  imageView.setImageMatrix(mMatrix);
  Log.v("resetStartPoint.",String.valueOf("X:" + this.startPoint.x) + "Y:" + String.valueOf(this.startPoint.y));
 }

 @Override
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  // TODO 自動生成されたメソッド・スタブ
  super.onMeasure(widthMeasureSpec, heightMeasureSpec);
 }

 public void animateView( SensorEvent event ) {
  
  if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {

   // Lowpass Filter ハイカット これ!
   currentOrientationValues[0] = event.values[0] * 0.1f + currentOrientationValues[0] * (1.0f - 0.1f);
   currentOrientationValues[1] = event.values[1] * 0.1f + currentOrientationValues[1] * (1.0f - 0.1f);

   // 重力加速度
   float accX = currentOrientationValues[0];
   float accY = currentOrientationValues[1];

   // 横画面なのでちょっと大きめにしました。
   float moveX = Math.round(accY*48);
   float moveY = Math.round(accX*32);

   float pointX = this.startPoint.x+moveX;
   float pointY = this.startPoint.y+moveY;
   
   mMatrix.set(mSavedMatrix);
   mMatrix.postTranslate(pointX,pointY);
   
   imageView.setImageMatrix(mMatrix);
   Log.v("animateView","pointX,pointY"+String.valueOf(pointX)+","+String.valueOf(pointY));
   
  }
 }
 
}

2010年11月16日火曜日

DroidDraw Android LayoutXMLのWYSIWYGエディタ

http://www.droiddraw.org/
http://code.google.com/p/droiddraw/

機能を完全には使っていないのですが、レイアウトがWYSIWYGでDrag&Dropできて便利です。
Mac OS X, Windows, Linuxのマルチプラットフォームサポートしています。
使い方さえ覚えればもしかするとデザイナさんでも一人でUI作れるかも?

ADTについているレイアウトエディタだとRelativeLayoutの中に入れた、2つのLinearLayoutを左右にふって上合わせにするプロパティーがどこにあるか見つけにくい(ってかあるのか?)のですが、それをマウス操作で上にあわせたり下にあわせたりできます。
(* (ADT) Android Development Tools for the Eclipse IDE )

これだけで、非常にストレスが減る!
Eclipseと連携して操作する所まで入っていないのですが、ファイルの場所を合わせればリソースファイルも読めるようです。

出力は、.apkパッケージで出力したり、XML単体で出力したりできます。
GUIで要素のIDを付ける操作は、いまいち使い勝手が悪いので、ひと通りの配置まで決めたあとは、XMLをEclipseのADTに貼りつけて、名前を付け直したりしています。

どう書いたらうまく並べられるのかの勉強替わりにもなりますね。
ひと通り慣れたら、プロパティーが覚えられそうなのでHTMLのようにタグがきができるようになれるような気がしています。

一点分かりにくいのが、Layout要素を入れ子にすると、外を囲むレイアウトがなんだったかが、GUI上から見えにくいところかな。

でも何にせよ使う利点は十分にあります。

MoonGiftの紹介記事

2010年11月15日月曜日

XCodeとInterfaceBuilderで10分でモックアプリを作る方法(3)

前回の続き。
全部で5回の予定です。
おそらく5回書き終わってからでないとあまり意味のない記事になっちゃいました…。
でも頑張ります。

では、ボタンを押したら画面遷移する動作を紐付けていきましょう。

まずは、XCodeの左側のツリーから、一番最初の画面の .xib ファイルをダブルクリックして、IB(InterfaceBuilderを立ち上げます。)

立ち上げると、こんな状態です。

1.動作の名前を決める

まず、メインの画面にボタンを押したときの操作を定義します。
先に、それぞれのボタンを押したときの操作を決めておきましょう。

3つボタンがあるので、こんな名前にしてみました。


ヘルプへ = changeViewHelp
設定へ = changeViewSetting
実行へ = changeViewMain

2.「File's Owner」に動作の名前を定義する。
これらを、IBをつかって定義していきます。
これからウィンドウを行ったり来たりするので混乱しないように。www
Document 画面の「File's Owner」をクリックします。



そうすると、右側の「インスペクタ」でこの画面に対するいろいろな設定ができるようになります。が、ここではまだちゃんとは使いません。w
上部にタブが4つ見えると思いますが、( i ) の所を選択してください。
その内容の一番上の「Class」のところに 「NavigationTestViewController」という名前が出ていると思います。これが、この画面の操作の統括をするプログラムの名前です。
(対応して、XCodeの中にも同じ名前のxxx.h xxx.mというファイルがあります。)





それを確認したら、「Class」のボックスにある、矢印をクリックしてください。
そうすると今度は、左側の「Library」のWindowの下側に、 「NavigationTestViewController」というボックスが現れます。


このプルダウンを「Actions」に合わせてください。

その状態で、「+」のボタンが下に現れます。

これをクリックして、3つのボタン操作を定義します。

こんな感じに。



3.「File's Owner」に動作の名前を定義したものを保存する。

これを、実際のファイルに保存します。
下のプルダウンから、「Write Updated Class File」を選択してください。

保護んウィンドウがでるのでこれで、Saveを押します。


もうあるけど、どうする?って聞かれますので「Replace」を押してください。
(置き換えられちゃいます。w まだファイルにはなにもかいていないので、大丈夫。)


4.ボタンの動作を「File's Owner」につなげる。

次に、それぞれのボタンを押した操作を紐付けます。
まずに、「実行へ」のボタンを、Mainの画面機能につなげます。

IBで「実行へ」のボタンをクリックして触ってください。
そうすると、右側の「インスペクタ」でボタンに対するいろいろな設定ができるようになります。




ここで、右側の「Connections」のタブを表示します。
ここには、ボタンの動作がすでに定義されており、押したら、どうとか、おして、指が離れたらどうとか、いろいろつなぐことができます。



ここでは、押して、指が離れたら、画面遷移するということにして、「Touch Up Inside」を使います。
これの右側にある◯をクリックし、ドラッグして、「Document」ウィンドウの「File's Owner」につなげます。「File's Owner」にまで、持って行ってマウスを外します。


と、グレーのサブウィンドウが現れるので、「実行へ」に対応する
changeViewMainをクリックします。



ちゃんとつながると、「Connections」の画面がこんなふうになります。



この状態で、.xibファイルを保存してください。

続いて、XCodeに戻り、画面遷移のプログラムを書きます。
そろそろ時間切れなので、また次…。中途半端でごめんなさい。


10分は無理だなこれ。www 
スゲー速さで作業してYoutubeではや回しビデオでもアップして帳尻合わせやろうかと思ってきました。wwww

2010年11月12日金曜日

師匠からの課題

Android Widgetからダイアログを出す。w

2010年11月11日木曜日

アプリの起動時に、着信音量とMedia音量をあわせる

メタルメガネ(Metal Megane)の「森羅万象おみくじうらない」ですが、Android版で音が出るようになりました。
着信音の音量に合わせて音量コントロールしていたら音楽がイヤフォンと本体の両方から出るようになってしまいました。

さすがにこれは、iPhoneユーザー的にはありえないなーと思ったのですが、Androidでは結構浸透している仕様のようですね。

ということで、これを避けるために、AudioManagerのストリームをAudioManager.STREAM_MUSICに、再度変更したのですがこうすると今度は、着信音量をオフにしていても、アプリが立ち上がると音量が、音楽再生用の音量として出ます。

着信音量をOFFにして、音楽を聞いたときに、音量をUPすると、
AudioManager.STREAM_MUSICとして効果音再生しているアプリは効果音が再生されてしまいます。

さすがにこれだと、iPhoneから移行してきたユーザーや、ガラケーから移行してきたユーザーはビックリするんじゃないかということで、アプリの起動時には、着信音量が0だったら、音楽再生音量を0にするという処理を入れました。

ということで、ソースコード。

この実装は、android.app.Applicationクラスのサブクラスとして、onCreate時に呼び出しています。

// Audio Managerを取得
AudioManager mAudio = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
// 着信の音量を取得
int vol = mAudio.getStreamVolume(AudioManager.STREAM_RING);

// 着信の音量が0だったら
if ( vol == 0 ) {
 // 音楽の再生音量を同じく0に。
 mAudio.setStreamVolume(AudioManager.STREAM_MUSIC, vol, AudioManager.FLAG_SHOW_UI);
}

2010年10月29日金曜日

RingerVolume と Media Volume のコントロール

悩みました。

本体の音量コントロールキーでは、

android.media.MediaPlayerで再生している場合は、
MediaVolume が操作されるのですが、

止まっている間や、android.media.MediaPlayerで再生していない場合は、Ringer Volumeが操作されます。

で、これをすべて「Media Volume」に寄せたかったのですが、寄せ方がわかりませんでした。

どうやら、Activityの onCreate 内で以下のおまじないを実行すると、
画面表示中に、ずっと「Media Volume」が操作対象となります。

setVolumeControlStream(AudioManager.STREAM_MUSIC);

やっと探しました。

2010年10月26日火曜日

Android アプリで設定音量を取得&再生音量に設定する。

アプリでシステムの着信音量設定を取得して、効果音をその設定音量に合わせるには、以下のとおり。

使用パッケージはandroid.media.AudioManagerとandroid.media.SoundPool。
soundpool.playと同時に音量を設定することもできるし、事前にsoundpool.setVolumeすることもできる。

package jp.mediba.android.testapp;

import java.util.List;
import android.app.Activity;
import android.content.Context;
import android.media.AudioManager;
import android.media.SoundPool;

public class Out extends Activity {

 SoundPool soundPool;
 int[] sounds = new int[1];
 
 public void onCreate(Bundle savedInstanceState) {
     
  super.onCreate(savedInstanceState);
  setContentView(R.layout.out);

  int[] sounds = new int[1];
  SoundPool soundPool;
  soundPool = new SoundPool(1, AudioManager.STREAM_RING, 0);
  sounds[0] = soundPool.load(this, R.raw.omikuji_bar, 1);

  AudioManager audio = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
  int ringVol = audio.getStreamVolume(AudioManager.STREAM_RING);
  soundPool.play(sounds[0], (float)ringVol, (float)ringVol, 0, 0, 1.0F);
 }
}

2010年10月19日火曜日

XCodeとInterfaceBuilderで10分でモックアプリを作る方法(2)

全部で5回の予定です。
おそらく5回書き終わってからでないとあまり意味のない記事になっちゃいました…。
でも頑張ります。


プロジェクトの作成

View-Basedの一番シンプルなアプリをテンプレートから作成します。


これでiPhoneアプリをまず一つ作成します。





プロジェクトを保存し、シミュレーターを使って起動してみると、
Viewがひとつだけあるグレーの画面がひとつだけあるアプリが出来ます。

とりあえずの動作確認は、緑色の<ビルドと実行>をおすと、シミュレーターで確認できます。



NavigationTestViewController.xib が、このアプリの最初のViewです。
これを上の画面遷移の、「タイトル」とします。

部品を埋めていきます。
画面遷移にはボタンを使うことにします。

NavigationTestViewController.xibをダブルクリックするとInterfaceBuilderが立ち上がります。
これに遷移用のボタンを3つ作成してください。
ボタンをDrag & Drop 出来上がったボタンに分かりやすいように名前をつけました。



これを他の画面についても繰り返していきます。

画面の部品配置用のファイルを追加して、からボタンを配置していきます。

タイトル画面:NavigationTestViewController.xib
メイン画面:MainViewController.xib
結果画面:ResultViewController.xib
ヘルプ画面:HelpViewController.xib
設定画面:HelpViewController.xib


こんな感じ。
nibファイルの作成
View-Controllerのsubclass



メイン画面:MainViewController.xib
をダブルクリックして、IBを立ち上げボタンを配置します。

メイン画面なので、「結果に進む」だけのボタンだけ配置します。
(戻れないけどまあいいかと。)



というかんじで、以下の3つについても、命名規則をあわせて、同じ手順で作ります。

結果画面:ResultViewController.xib
ヘルプ画面:HelpViewController.xib
設定画面:SettingViewController.xib

全部作り終わると、画面ファイルはこんな感じです。




この状態で、GUIの準備はできました。
あとは、それぞれのボタンと、動作Actionを関連付けていきます。









ここからは、しばらくInterfaceBuilderの作業になります。

一度、すべてのxibファイルを閉じて、タイトル画面である、NavigationTestViewController.xibをダブルクリックして、表示します。

このファイルに「ボタンを押すと別画面に遷移する」という動作を関連付けましょう。
ここから出てくるファイルは、以下の3つです。

NavigationTestViewController.xib これは InterfaceBuilderでゴニョゴニョするだけ。

NavigationTestViewController.m これには、methodを追加します。
NavigationTestViewController.h これには、methodの定義だけを追加します。

じゃっそういうことでこれは明日やりますか。

2010年10月18日月曜日

XCodeとInterfaceBuilderで10分でモックアプリを作る方法(1)

iPhone App 開発者としては、初心者のmarcovです。

AppleのiOS関連の開発ツールには、プログラムを書くIDEであるXcodeとGUIを作成するInterfaceBuilder(以下IB)が、付属しています。

また、簡単にアプリを作るためのフォーマットも用意されており、XCodeでiPhoneアプリのテンプレートを選ぶと、取りあえず動くものが出来るのですが、これでは全く物足りません。

普通のゲームのようなアプリを作るときは、メニュー画面とゲーム画面、加えて設定画面、ヘルプ画面の最低でも4つ程の画面は必要だと思われますが、これらを作るときに、Xcodeの新規作成のプロジェクトでは、帯に短しタスキに長しです。

というわけで、画面が4つだけあるような、アプリを作って画面遷移だけしてみます。
これだけできていると、デザイナーから上がってきた、アプリをモックとして、動かすことができるので非常に便利です。また、WEBのライトウェイトプログラマーである自分としても、とりあえずの画面遷移が出来ていると作るターゲットがだいたい決まっているように見えて気分で気にも楽ですね。


全部で5回の予定です。
おそらく5回書き終わってからでないとあまり意味のない記事になっちゃいました…。
でも頑張ります。


今回はこんなアプリを作成します。



タイトル画面から、遷移してそれぞれもどってくるタイプです。
メインと、結果のページに付いては、一方向に流れてタイトルに戻ります。

続きはまた明日。

2010年10月6日水曜日

Android開発 - SQLiteでの日付型データ保存

現在開発中のアプリにおいて、SQLiteデータベースにデータの更新日時を保存したいのですが、
下記のように標準のSQLiteDatabaseライブラリのインサートメソッドを使うと、

ContentValuesvalues = new ContentValues();
values.put("datetime", "datetime('now', 'localtime')");
db.insert("tablename", null, values);

 datetime('now', 'localtime')がそのまま文字列として保存されてしまいます。
(SQLiteには日付のデータ型は存在しないためdatetimeカラムはTEXT型

おそらくライブラリがString型の変数に対して " " で囲ってSQLを発行するためと思われます。

 回避策として、

String sql  =  "INSERT INTO tablename" +
                       "(datetime) " +
                       "VALUES(datetime('now', 'localtime'));";
db.execSQL(sql);

といった形で直接SQLを記述して実行するとうまくいきました。

なんだか納得行かないのでメモ書き。

2010年10月4日月曜日

Android開発 - フェードインアニメーション

アプリケーション起動時にタイトルロゴをフェードインさせるページの実装方法です。


黒一色の画面から白バックのロゴ画像がフェードインします。

アニメーションの定義方法はこちらを参考にしました。
http://android.roof-balcony.com/resource/animation/

■res/anim/にアニメーション定義のXMLファイルを作成。



    android:duration="3000" 
    android:fromAlpha="0.0"
    android:toAlpha="1.0"
    android:interpolator="@android:anim/decelerate_interpolator"
    />


■レイアウトのXMLファイルは以下のとおりです。


android:id="@+id/MainLayout"
android:orientation="vertical"
android:layout_width="320dp"
android:layout_height="455dp"
android:background="#FFFFFF"
>


android:id="@+id/ImageLogo" 
android:src="@drawable/******" 
android:layout_gravity="center" 
android:layout_marginTop="180dp"
android:layout_width="272dp" 
android:layout_height="76dp" 
>

今回はLinearLayoutごとフェードインさせます。

■Javaファイルの記述

package ***********************************;

import android.app.Activity;
import android.os.Bundle;
import android.content.Intent;
import android.view.animation.Animation;
import android.view.animation.Animation.AnimationListener;
import android.view.animation.AnimationUtils;
import android.widget.LinearLayout;

public class **** extends Activity implements AnimationListener{

private Animation fadeIn;
private LinearLayout mainLayout;
private Intent intent;

@Override
public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);
setContentView(R.layout.****);

mainLayout = (LinearLayout)findViewById(R.id.MainLayout);

fadeIn = AnimationUtils.loadAnimation(this, R.anim.****);
fadeIn.setAnimationListener(this);
mainLayout.setAnimation(fadeIn);
}

@Override
public void onAnimationEnd(Animation animation) {

// フェードイン完了後次の画面へ
if (animation == fadeIn) {
intent = new Intent(this, ****.class);
startActivity(intent);
finish();
}

}

@Override
public void onAnimationRepeat(Animation animation) {

}

@Override
public void onAnimationStart(Animation animation) {

}
}

onCreate()時にレイアウトとアニメーションのXMLを指定し、setAnimationListenerにセットします。
対象のアニメーションが終了したときにonAnimationEnd()がコールされ、次のアクティビティに移動します。

このアクティビティをManifestファイルで起動アクティビティに指定すると、

起動 -> 画像のフェードイン表示 -> メイン画面(メニュー画面など)

といったよく見る流れの実装が可能です。

一旦アプリを中断し、再開した場合に再度ロゴアニメーションが表示されるのは鬱陶しいので
次のアクティビティを呼び出した後finish()をかけています。

画面遷移のアニメーションについてはXMLのみで実装する方法もあるらしいので、いずれ調査したいと思います。