Androidの通常の画面構成は画面上端からステータスバー、タイトルバーがあり、その下
にビュー画面があります。タッチイベントの「event.getY();」で取得したy座標の原点は画 面の上端を原点にした値ですので、この値を元にビュー画面に表示する場合は補正が必要 になります。この補正をする値はステータスバーとタイトルバーの高さの合計です。これ らの高さを求める方法はいくつかありますが、ステータスバーとタイトルバーの高さを 個々に求める代わりに画面上端からビュー画面のトップ位置までの距離は以下で得られま す。なお、この値はonCreateメソッド内やGviewコンストラクタ内では画面構成が完成 していないため0となってしまいますので、onDrawメソッド内で取得しています。
float top=getWindow().findViewById(Window.ID_ANDROID_CONTENT).getTop();
したがってタッチイベントで得らられる「event.getY();」からこのtopを引いた値をビュ ー画面のy座標として使えばよいことになります。なお、ステータスバー、タイトルバー を消してフルスクリーンモードにすればこのような補正は必要ありません。
「例題6-2」ビュー画面の左上隅からタッチムーブ位置に直線を描きます。(px,py)がタッチ
位置です。
・MainActivity.java
package com.example.touch3;
import android.app.Activity;
import android.content.Context;
import android.graphics.*;
import android.os.Bundle;
import android.view.*;
public class MainActivity extends Activity { private float px=0,py=0;
private GView gv;
@Override
public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);
gv=new GView(this);
setContentView(gv);
}
private class GView extends View { private Paint paint;
public GView(Context context) { super(context);
paint=new Paint();
}
protected void onDraw(Canvas canvas) { float
top=getWindow().findViewById(Window.ID_ANDROID_CONTENT).getTop();
paint.setColor(Color.BLUE);
canvas.drawLine(0,0,px,py-top,paint);
} }
public boolean onTouchEvent(MotionEvent event) { if (event.getAction()==MotionEvent.ACTION_MOVE){
px=event.getX(); // タッチ位置をメンバ変数に格納 py=event.getY();
gv.invalidate(); // 再描画 }
return super.onTouchEvent(event);
} }
「練習問題6-2」タッチムーブ位置にイメージを追従して移動しなさい。
・MainActivity.java
package com.example.touch4;
import android.app.Activity;
import android.content.Context;
import android.graphics.*;
import android.os.Bundle;
import android.view.*;
public class MainActivity extends Activity { private float px=0,py=0;
private GView gv;
@Override
public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);
gv=new GView(this);
setContentView(gv);
}
private class GView extends View { private ①
public GView(Context context) { super(context);
img=BitmapFactory.decodeResource(getResources(),R.drawable.sai);
}
protected void onDraw(Canvas canvas) { float
top=getWindow().findViewById(Window.ID_ANDROID_CONTENT).getTop();
canvas.drawBitmap( ② ,null);
} }
public boolean onTouchEvent(MotionEvent event) { if (event.getAction()==MotionEvent.ACTION_MOVE){
px=event.getX();
py=event.getY();
gv.invalidate();
}
return super.onTouchEvent(event);
} }
6-3 画面サイズ
画面の幅と高さは onDraw メソッドの canvas 引数を使って canvas.getWidth()と canvas.getHeight()で取得できます。得られる高さにはステータスバーとタイトルバーの高 さの合計が含まれていますので、描画できる画面の高さは以下のようにtopを引いた値とな ります。
top=getWindow().findViewById(Window.ID_ANDROID_CONTENT).getTop();
h=canvas.getHeight()-top;
「例題6-3」画面中央に羅針盤を置き、タッチ位置から北東、南東、南西、北西の4方向を
判定します。
・MainActivity.java
package com.example.touch5;
import android.app.Activity;
import android.content.Context;
import android.graphics.*;
import android.os.Bundle;
import android.view.*;
public class MainActivity extends Activity { private float w,h,top;
private GView gv;
@Override
public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);
gv=new GView(this);
setContentView(gv);
}
private class GView extends View { private Bitmap img;
public GView(Context context) { super(context);
img=BitmapFactory.decodeResource(getResources(),R.drawable.compass);
}
protected void onDraw(Canvas canvas) {
top=getWindow().findViewById(Window.ID_ANDROID_CONTENT).getTop();
w=canvas.getWidth();
h=canvas.getHeight()-top;
canvas.drawBitmap(img,w/2-img.getWidth()/2,h/2-img.getHeight()/2,null);
} }
public boolean onTouchEvent(MotionEvent event) { if (event.getAction()==MotionEvent.ACTION_MOVE){
float px=event.getX();
float py=event.getY()-top;
if (px>w/2 && py<h/2) setTitle("北東");
else if (px>w/2 && py>h/2) setTitle("南東");
else if (px<w/2 && py>h/2) setTitle("南西");
else if (px<w/2 && py<h/2) setTitle("北西");
}
return super.onTouchEvent(event);
} }
「注」画面の幅と高さはViewクラスのgetWidthメソッド、getHeightメソッドで取得で きます。これらのメソッドはGviewコンストラクタ内では幅と高さが確定していないので 値を取得できません。onDrawメソッド内で取得します。
int w=getWidth();
int h=getHeight();
Canvas クラスのgetWidthメソッド、getHeightメソッドを使って次のように取得する
こともできます。ただしこの場合、幅はView クラスの getWidthメソッドと同じですが、
高さはステータスバーとタイトルバーの高さも含んでいます。
int w=canvas.getWidth();
int h=canvas.getHeight();
画面サイズを別の方法で求めるには11章-「11-8 ブラウザ(WebView)」の「「注」
DisplayクラスのgetWidth()とgetHeight()」を参照。
「練習問題6-3」1~8のイメージを画面中央を中心とする半径160の円周上に配置します。
タッチ位置を元にタッチしたイメージを判定しなさい。
・MainActivity.java
package com.example.touch6;
import android.app.Activity;
import android.content.Context;
import android.graphics.*;
import android.os.Bundle;
import android.view.*;
public class MainActivity extends Activity { private int[] imageId={
R.drawable.num1,R.drawable.num2,R.drawable.num3,R.drawable.num4, R.drawable.num5,R.drawable.num6,R.drawable.num7,R.drawable.num8 };
private float w,h,top,iw,ih;
@Override
public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);
setContentView(new GView(this));
}
private class GView extends View { public GView(Context context) { super(context);
}
protected void onDraw(Canvas canvas) {
top=getWindow().findViewById(Window.ID_ANDROID_CONTENT).getTop();
w=canvas.getWidth();
h=canvas.getHeight()-top;
for (int i=0;i<8;i++){
Bitmap
img=BitmapFactory.decodeResource(getResources(),imageId[i]);
float x=(float)(160*Math.cos(45*i*Math.PI/180));
float y=(float)(160*Math.sin(45*i*Math.PI/180));
iw=img.getWidth();
ih=img.getHeight();
canvas.drawBitmap(img, ① ,h/2-y-ih/2,null);
} }
}
public boolean onTouchEvent(MotionEvent event) { if (event.getAction()==MotionEvent.ACTION_DOWN){
float px=event.getX();
float py=event.getY()-top;
for (int i=0;i<8;i++){
float x=(float)(160*Math.cos(45*i*Math.PI/180));
float y=(float)(160*Math.sin(45*i*Math.PI/180));
if ( ② && px<w/2-x+iw/2 && h/2-y-ih/2<py && py<h/2-y+ih/2) setTitle("No"+(i+1));
} }
return super.onTouchEvent(event);
} }
「 注 」Bitmap img=BitmapFactory.decodeResource(getResources(),imageId[i]);は onDrawメソッド内に置いているので2-1の「注」Android Lint Checksと2-4の「注」Android Lint Checksで示した理由により警告エラーとなります。onDrawメソッド外でリソースを作成 すればよいのですが、煩雑になるため、ここではonDraw内に置きました。
☆応用サンプル 羅針盤の針を回す
タッチムーブ位置の方向を指すように針を回転させます。羅針盤compassと針needle を画面中央に描画します。画面の中央位置を(w,h)に取得し、「canvas.translate(w,h);」で 画面中央に座標移動します。針の角度angleはタッチ位置(x,y)からアークタンジェントを 使って「Math.atan(y/x)*180/Math.PI」で求め、「canvas.rotate(angle);」で座標回転しま す。
・MainActivity.java
package com.example.compass;
import android.app.Activity;
import android.os.Bundle;
import android.content.Context;
import android.graphics.*;
import android.view.*;
public class MainActivity extends Activity { private GView gv;
private int w,h;
private float angle=0,top=0;
@Override
public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);
gv=new GView(this);
setContentView(gv);
}
private class GView extends View { private Bitmap needle,compass;
public GView(Context context) { super(context);
needle=BitmapFactory.decodeResource(getResources(),R.drawable.needle);
compass=
BitmapFactory.decodeResource(getResources(),R.drawable.compass);
}
protected void onDraw(Canvas canvas) {
top=getWindow().findViewById(Window.ID_ANDROID_CONTENT).getTop();
w=getWidth()/2;
h=getHeight()/2;
canvas.drawColor(Color.WHITE);
canvas.translate(w,h);
canvas.drawBitmap(compass,-compass.getWidth()/2, -compass.getHeight()/2, null);
canvas.rotate(angle);
canvas.drawBitmap(needle,-needle.getWidth()/2, -needle.getHeight()/2, null);
} }
public boolean onTouchEvent(MotionEvent event) { if (event.getAction()==MotionEvent.ACTION_MOVE){
float x=event.getX()-w;
float y=event.getY()-top-h;
angle=(float)(Math.atan(y/x)*180/Math.PI);
if (x<0)
angle+=180;
gv.invalidate();
}
return super.onTouchEvent(event);
} }
7-2 アラート・ダイアログ
AlertDialogはユーザに何らかの処理に対する問い合わせ(OKやCANCELなど)を行
うダイアログクラスです。
1. AlertDialog.Builder
AlertDialgのコンストラクタには直接アクセスできませんので、以下のように
AlertDialog.Builderクラスのオブジェクトbuilderを使ってアラート・ダイアログの内容
を設定します。
AlertDialog.Builder builder=new AlertDialog.Builder(this);
builder.setTitle("確認");
builder.setMessage("処理してよいですか");