2013-11-03

[Android開発] タップした位置にビューを移動する方法

Android開発をする際に必ず必要となるのがレイアウトの知識です。
ですが、Androidのレイアウト表示は多少クセのある部分があります。
ということで、今回は「タップした位置にビューを移動する方法(注意点)」をお届けしたいと思います。


[最終結論] getLocationOnScreen() にはステータスバーの高さも含まれているので、setY() を使うときは注意が必要。


まず、「タップした位置にビューを移動する」というのはどういうことかというと、


このように、タッチしたビューを基準にして他のビューを移動させることです。
「今ココを選択してますよ〜」、みたいなやつですね。


では、これを実現するための順序を。

1. タッチイベントをつくる。
2. タッチされたビューの位置を取得。
3. その位置を基準にして移動させたいビューの位置を設定。

という流れです。
※ちなみにレイアウトのxml には<RelativeLayout> を使った例になります。

では、まず1番の「タッチイベントをつくる」から。
と言っても、これは簡単ですね。
該当する xml に <ImageView>を書き込んで、onCreate() の中で


ImageView imageView = (ImageView) findViewById(R.id.image_view);
imageView.setOnClickListener(new View.OnClickListener() {
           
    @Override
    public void onClick(View v) {

        // ここに2番、3番の処理を書きます。      

         
    }
           
});



とやればオッケーです。

では、次に2番「タッチされたビューの位置を取得」にうつりましょう。
まずタッチされたビュー(ここではR.id.image_view)の位置を取得します。
つまり、以下のようなことですね。


この部分を追加したコードは以下。
ImageView imageView = (ImageView) findViewById(R.id.image_view);
imageView.setOnClickListener(new View.OnClickListener() {
           
    @Override
    public void onClick(View v) {

        ImageView touchImageView = (ImageView) findViewById(R.id.image_view);
        int[] locations = new int[2];
        touchImageView.getLocationOnScreen(
locations); //ここで位置が格納される。

         
    }
           
});


※この例ではタッチイベントを使わない場合も考慮して findViewById を使っていますが、単純に引数のView v を使ってもOKです。ちょっと複雑な場合は無視してオッケーです。^-^


はい。
これで、タッチされたビューの「画面の左上からの」位置がlocations の中に取得できました。
ちなみに、、、

locations[0] がX軸(Left位置で使用)
locations[1] がY軸(Top位置で使用)

になります。


では、最後の3番「その位置を基準にして移動させたいビューの位置を設定」です。

まず移動させたいビューを findViewById で取得して、setX()とsetY() で位置を設定します。
この部分はのコードは、、、


ImageView imageView = (ImageView) findViewById(R.id.image_view);
imageView.setOnClickListener(new View.OnClickListener() {
           
    @Override
    public void onClick(View v) {

        ImageView touchImageView = (ImageView) findViewById(R.id.image_view);
        int[] locations = new int[2];
        touchImageView.getLocationOnScreen(
locations); //ここで位置が格納される。


        int touchedImageX = locations[0];
        int touchedImageY = locations[1];


        // ↓↓↓ここから移動させたいビュー の設定

        ImageView moveImageView = (ImageView) findViewById(R.id.move_image_view);
        moveImageView.setX(touchedImageX); //取得した位置をセット
        moveImageView.setY(touchedImageY); //取得した位置をセット
         
    }
           
});


これでOK。
…ではないのです!!

なぜかというと、 どうやらsetX() と setY() は最初に位置を取得した getLocationOnScreen() とは開始点が違うようなので、これだと少しずれた場所に移動させられてしまうのです。

つまり、わかりやすく言うと

画面上部にあるステータスバーが含まれていない位置を指定しなければいけないのです。





なので、ステータスバーの影響をうけないLeftの位置は問題ありませんが、

設定するべきTopの位置 = getLocationOnScreen() で取得したY座標 - ステータスバーの高さ

となります。
ステータスバーの高さは

Rect rect = new Rect();
Window window = getWindow();
window.getDecorView().getWindowVisibleDisplayFrame(rect);
int statusBarHeight = rect.top;
// ステータスバーの高さ



で取得できるので、 最終的なコードは


ImageView imageView = (ImageView) findViewById(R.id.image_view);
imageView.setOnClickListener(new View.OnClickListener() {
           
    @Override
    public void onClick(View v) {

        ImageView touchImageView = (ImageView) findViewById(R.id.image_view);
        int[] locations = new int[2];
        touchImageView.getLocationOnScreen(
locations); //ここで位置が格納される。


        int touchedImageX = locations[0];
        int touchedImageY = locations[1];


        // ↓↓↓ここから移動させたいビュー の設定

        Rect rect = new Rect();
        Window window = getWindow();
        window.getDecorView().getWindowVisibleDisplayFrame(rect);
        int statusBarHeight = rect.top; // ステータスバーの高さ


        ImageView moveImageView = (ImageView) findViewById(R.id.move_image_view);
        moveImageView.setX(touchedImageX);
        moveImageView.setY(touchedImageY-statusBarHeight); // ←ココ
         
    }
           
});



でOKです。
ただし、これはタッチされた画像の左上の点にビューを移動させることになりますので、もしビューの真ん中だとかちょっとずらしたい場合は画像の幅をとって1/2にするなどして位置を調整する必要があります。

また、ビューをタッチした時ではなく、アプリ(アクティビティ)を起動した瞬間にこのテクニックを使う場合には以下のように onWindowFocusChanged() 内で行なってください。onCreate() では早すぎてビューの位置やサイズを取得できないようです。

@Override
public void onWindowFocusChanged(boolean hasFocus) { 


    // ここに追加。


}



以上になります。
お疲れ様でした。^-^


ちなみに記事中で使ってる画像は今作っているゲームアプリのサンプルです。
まだまだ時間がかかりそうです。^-^;

0 件のコメント:

コメントを投稿