2010年12月24日金曜日

座標系について

前回スクリーン座標とローカル座標について話しましたがもう一つ座標系があったみたいです

赤がスクリーン座標で、緑がレイアウトの持つローカル座標、青がボタンの持つローカル座標

前回で言っていたスクリーン座標はレイアウトのローカル座標でした。申し訳ない・・・

それでボタン高さ分ずれる原因も判明しました
public void setLayoutParams (ViewGroup.LayoutParams params)
これでボタン座標を設定していますが、これの設定はレイアウトのローカル座標系に対しての設定です。なのでレイアウトのローカル座標からスクリーン座標に変換する必要があります。

前回ボタン高さ分ずらして位置があっているように見えてたのは偶然で本当は下の画像のオレンジ分の差分を考慮しなければいけません
オレンジ矢印の長さ求める方法は摸索中・・・
ステータスバー?呼び方分かればリファレンス見ればなんとか出てきそうかな

2010年12月22日水曜日

スクリーン座標とボタンのローカル座標

前回の投稿で軽く触れてましたがもうちょい詳しく書いておきます

赤い軸がスクリーン座標軸で青い軸がボタンの持つローカル座標軸
間違っていました(詳しくは次の投稿で)
それぞれの座標を取得する方法は

スクリーン座標
float android.view.MotionEvent.getRawX()

public final float getRawX ()

Since: API Level 1
Returns the original raw X coordinate of this event. For touch events on the screen, this is the original location of the event on the screen, before it had been adjusted for the containing window and views.


ローカル座標
 こっちは配列の先頭インデックスの座標を取得する
 タッチイベント中に座標を何個か保持してるみたいで下のメソッドでは最新の座標を取得する
 保持している座標の数などはgetHistorical***などが用意されている
float android.view.MotionEvent.getY()

public final float getX ()

Since: API Level 1
getX(int) for the first pointer index (may be an arbitrary pointer identifier).

詳しくはリファレンスのview.MotionEventのページ見るのが早い
http://developer.android.com/reference/android/view/MotionEvent.html


それで前回のプログラム(ドラッグ中の座標を取得してボタンに設定する)の補足です
主に座標変換について。
取得したスクリーン座標をそのままボタンに設定すると下の画像のようにボタンの左上になっちゃいます。これはボタンを設定する際は矩形の左上の座標を設定する仕様になっているためです。


そこで、ボタンをタップした時点でのローカル座標を取得してその分ずらす必要があります。



具体的には緑をタップしたときのローカル座標(getXなど)を保持しておいてドラッグ中のスクリーン座標(getRawXなど)に対して引いてあげると押した時まま一緒に移動してくれます。
詳しいことは前回の投稿の最後にあるソースを参照してください。


ここまで説明したのですがX方向に対しては上記の説明が正しいのですが
Y方向に関してはgetRawY()-getY()だけだとボタンのheight分ずれてしまいました。
なので前回のプログラムではgetRawY()-(geyY()+button.height)としています。
なんでだろう・・・

-----------------------------------------------------------------------------------------------------------------------------
//追記
分かった・・・
簡単にいうと座標系がもう一つあったということ
長くなりそうなんで新しい投稿で説明します

2010年12月21日火曜日

android.widget.Buttonの座標を設定する方法

ボタンの配置を自由にカスタマイズできるような仕様のアプリを作ろうとしていたがここではまった
やりたいことはドラッグ中の座標にButtonを再配置するということ
はまった点は
 ドラッグ中の座標の取得(主にローカルとスクリーン座標について)
 ボタンの座標の設定方法

はじめに言っておくと直接座標指定するって方法は推奨されていないみたいです
画面配置が機種依存になってしまうからかなって推測



まずボタンの座標を設定する方法ですが直接設定できるようなメソッドは調べた限りでは無かった
ボタンの座標の取得はgetLeft() getRight() getTop() getBottom()など取得できるが設定のためのsetLeft()などは無いみたい

Buttonを追加する土台となるレイアウトの種類によってはButtonを追加する際に呼ぶメソッドに座標を指定するパラメータが無いものがある
じゃあ座標指定できるレイアウトはなんぞやってことだがAbsoluteLayout で座標が指定できるようになる

 //レイアウト作成
  AbsoluteLayout absoluteLayout = new AbsoluteLayout(this);
  setContentView(absoluteLayout);
  //ボタン作成
  Button button1 = new Button(this);
 //レイアウトにボタンを追加 ボタンのパラメータの設定
  absoluteLayout.addView(button1, new AbsoluteLayout.LayoutParams(150,50, 0, 0));

こんな感じで指定した座標にボタンを追加することができる
指定しているコードはAbsoluteLayout.LayoutParams(width,height, x, y)で行っている
xyはButtonの矩形の左上の座標になる

ボタンのパラメータを動的に指定したい場合は
button1.setLayoutParams(new AbsoluteLayout.LayoutParams(width,height, x, y));
とすると指定したパラメータにボタンが更新される




次にドラッグ中の座標取得ですが
ドラッグの実装についてはどっかのサイトを見たんだけどどこか忘れた・・・
それで、はまったのはどの座標系の座標を取ってきているかを勘違いしていたってことで

android.view.MotionEvent.getX()

ボタンのローカル座標
ボタンの持っている座標系の座標を取得するもんだと思う
詳しくリファレンス読んでないので間違ってるかもしれない

android.view.MotionEvent.getRawX()
スクリーン座標
こちらはスクリーンの座標系の座標を取得するものだと思う


きっちり調べたわけではないので間違ってること言っていたらご指摘お願いします。





上で述べた二つを組み合わせて作ったのがボタンをドラッグ中の座標に設定するサンプルプログラム
import android.app.Activity;
import android.os.Bundle;
import android.widget.AbsoluteLayout;
import android.widget.AbsoluteLayout.LayoutParams;
import android.widget.Button;
import android.widget.TextView;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;

public class AndroidFormTest extends Activity implements OnTouchListener {
 private Button button1;
 private TextView textView1, textView2,textView3;
 private int mState = 0;
 private int offsetX = 0;// ボタンクリックしたスクリーンのX座標とボタンのX座標の差分
 private int offsetY = 0;// ボタンクリックしたスクリーンのY座標とボタンのY座標の差分
 private final int STATE_NONE = 0;
 private final int STATE_DRAG = 1;
 /** Called when the activity is first created. */
 @Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  AbsoluteLayout absoluteLayout = new AbsoluteLayout(this);
  setContentView(absoluteLayout);
  //ボタン作成
  button1 = new Button(this);
  // イベントリスナの登録
  button1.setOnTouchListener(this);

  absoluteLayout.addView(button1, new AbsoluteLayout.LayoutParams(150,50, 0, 0));
  button1.setText(button1.getLeft() + "," + button1.getTop());
  textView1 = new TextView(this);
  absoluteLayout.addView(textView1, new AbsoluteLayout.LayoutParams(300,50, 20, 100));
  textView2 = new TextView(this);
  absoluteLayout.addView(textView2, new AbsoluteLayout.LayoutParams(300,50, 20, 150));
  textView3 = new TextView(this);
  absoluteLayout.addView(textView3, new AbsoluteLayout.LayoutParams(300,50, 20, 200));
 }
  @Override
 public boolean onTouch(View v, MotionEvent event) {
  // TODO 自動生成されたメソッド・スタブ
  // ドラッグ
  switch (event.getAction() & MotionEvent.ACTION_MASK) {
  case MotionEvent.ACTION_DOWN:
   offsetX = (int) event.getX();
   offsetY = (int) event.getY();
   textView1.setText("クリックした時点のローカル:" + offsetX + "," + offsetY);
   mState = STATE_DRAG;
   break;
  case MotionEvent.ACTION_UP:
  case MotionEvent.ACTION_POINTER_UP:
   mState = STATE_NONE;
   break;
  case MotionEvent.ACTION_MOVE:
   if (mState == STATE_DRAG) {
    int pointX, pointY;
    pointX = (int) event.getRawX() - offsetX;
    pointY = (int) event.getRawY() - (offsetY + button1.getHeight());
    button1.setLayoutParams(new AbsoluteLayout.LayoutParams(150,50, pointX, pointY));
    textView2.setText("ボタン座標(左上):" + button1.getLeft() + "," + button1.getTop());
    textView3.setText("スクリーン座標:" + (int) event.getRawX() + "," + (int) event.getRawY());
   }
   break;
  }
  return false;
 }
}
------------------------------------------------------------------------------------------------------------
追記
58行目のpointY = (int) event.getRawY() - (offsetY + button1.getHeight());
をpointY = (int) event.getRawY() - (offsetY + ステータスバーの高さ);と変更してください。
今回はステータスバーの高さとボタンの高さがたまたま一緒だったのでうまくいっています。
ステータスバーの高さを取得できる方法が分かり次第また追記します。