Androidプログラミング・メモ3
by K.I
2016/04/05〜
Index
- Androidのアプリケーションプログラムに関する備忘録としてのメモ
- ジェスチャーのイベント、それにPopUpウィンドウについて
[top]
- GestureDetectorをインプリメントする
public class Customview extends View
implements GestureDetector.OnGestureListener,
GestureDetector.OnDoubleTapListener,
ScaleGestureDetector.OnScaleGestureListener {
- Eclipseの場合、これでメソッド・スタブが自動生成されるので、これに必要な操作を記述すれば良い
- メソッドの戻り値がbooleanの場合、最初のイベントでtrueを返さないと次のイベントが発生しない様だ
onTouchEvent⇒ACTION_DOWN
onDown
onTouchEvent⇒ACTION_UP
onSingleTapUp
onSingleTapConfirmed
onTouchEvent⇒ACTION_DOWN
onDown
onShowPress
onTouchEvent⇒ACTION_UP
onSingleTapUp
onSingleTapConfirmed
onTouchEvent⇒ACTION_DOWN
onDown
onShowPress
onLongPress
onTouchEvent⇒ACTION_UP
onTouchEvent⇒ACTION_DOWN
onDown
onTouchEvent⇒ACTION_UP
onSingleTapUp
onTouchEvent⇒ACTION_DOWN
onDoubleTap
onDoubleTapEvent
onDown
onTouchEvent⇒ACTION_UP
onDoubleTapEvent
onTouchEvent⇒ACTION_DOWN
onDown
onTouchEvent⇒ACTION_MOVE
onTouchEvent⇒ACTION_MOVE
onScroll
onTouchEvent⇒ACTION_MOVE
onScroll
onTouchEvent⇒ACTION_MOVE
onScroll
:
onTouchEvent⇒ACTION_MOVE
onScroll
onTouchEvent⇒ACTION_MOVE
onScroll
onScaleBegin ⇒ピンチ操作の開始(指2本でタッチした時)
onScale ⇒ピンチ操作中に繰り返し呼ばれる
onScale
onScale
:
:
:
onScale
onScaleEnd ⇒ピンチ操作の終了(どちらかの指が離れて、1本になった時)
onTouchEvent⇒ACTION_UP
- ScaleGestureDetectorのメソッド
- getScaleFactorで、1つ前の間隔との比
- getCurrentSpanX,getCurrentSpanYで、現在の間隔
- getFocusX,getFocusYで、2本の指の中心位置を取得できる
- onScaleEndの時は片方の指だけなので、中心位置にならないことに注意!
onTouchEvent⇒ACTION_DOWN
onDown
onTouchEvent⇒ACTION_MOVE
onTouchEvent⇒ACTION_MOVE
onTouchEvent⇒ACTION_MOVE
onScroll
onTouchEvent⇒ACTION_MOVE
onScroll
onTouchEvent⇒ACTION_MOVE
onScroll
:
:
:
onTouchEvent⇒ACTION_UP
- スケーリングと違って、スクロールは終了イベントが無さそうなので、
- onTouchEventのACTION_UPで終了を判定するしかないと思う
onTouchEvent⇒ACTION_DOWN
onDown
onTouchEvent⇒ACTION_UP
onSingleTapUp
onTouchEvent⇒ACTION_DOWN
onDoubleTap
onDoubleTapEvent
onDown
onShowPress
onTouchEvent⇒ACTION_MOVE
onDoubleTapEvent
onTouchEvent⇒ACTION_MOVE
onDoubleTapEvent
onTouchEvent⇒ACTION_MOVE
onDoubleTapEvent
:
onTouchEvent⇒ACTION_MOVE
onDoubleTapEvent
onScaleBegin
onScale
onScale
onScale
:
:
:
onScale
onScaleEnd
- ダブルタップからスクロール操作をすると、すぐにスケーリング動作に切り替わる様だ1
- ダブルタップした位置を中心として、下方向で拡大、上方向で縮小となる
onTouchEvent⇒ACTION_DOWN
onDown
onTouchEvent⇒ACTION_MOVE
onTouchEvent⇒ACTION_MOVE
onScroll
onTouchEvent⇒ACTION_MOVE
onScroll
onTouchEvent⇒ACTION_MOVE
onScroll
:
onTouchEvent⇒ACTION_MOVE
onScroll
onTouchEvent⇒ACTION_UP
onFling!
- 最初は、どうしても少しスクロールしてしまうので、
- スクロールとフリックを共存させるのは難しいかもしれない
- 最初の状態を憶えておいて、普通にスクロールしておいて、
- もしフリックの場合は元に戻してから、フリック動作に移るとかすれば良いかも。
1実際、やってみたら切り替わったが、何か条件があるのかもしれない。
[top]
- アイコンとTEXTを、吹き出し形状のPopupWindowで表示するようにする。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/baloon" ⇒吹き出しの9-patchイメージ
android:orientation="horizontal" >
<ImageView
android:id="@+id/popupicon"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_gravity="center_vertical"
android:src="@drawable/popupicon" /> ⇒アイコンイメージ
<TextView
android:id="@+id/popuptext"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:text="notitle"
android:textColor="#ffffff"
android:textSize="20sp" />
</LinearLayout>
- Viewの任意の位置に、PopupWindowを表示する
@Override
public void onLongPress(MotionEvent e) {
LinearLayout layout = (LinearLayout)activity.getLayoutInflater().inflate(R.layout.popupwindow, null);
// Textを設定・TextViewの取得は、layoutから指定する必要がある
TextView poptext = (TextView)layout.findViewById(R.id.popuptext);
poptext.setText(String.format("%d, %d",(int)e.getX(),(int)e.getY()));
// PopupWindowはActivityから呼出されるので、予め取得しておいたメインのActivityを指定
PopupWindow popupWindow = new PopupWindow(activity);
popupWindow.setContentView(layout);
// PopupWindowの背景に、透明のイメージを指定
popupWindow.setBackgroundDrawable(getResources().getDrawable(R.drawable.transparent));
// PopupWindowの外側をタッチすると、消えるようにする
popupWindow.setOutsideTouchable(true);
popupWindow.setFocusable(true);
// showAsDropDownで表示する時は、setWindowLayoutModeで大きさを指定する必要がある
popupWindow.setWindowLayoutMode(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
popupWindow.showAsDropDown(this,(int)(e.getX()),(int)(e.getY()-view_h));
}
- Popupの表示位置2は、左下が座標原点(0,0)になる
- 通常の左上基準の座標と異なるので、予め取得しておいたviewの高さを引いている
- showAsDropDownは、ナビゲーションバーも含めた画面全体の左下が基準で、Y方向は何故かマイナス値で指定するようだ。
- ナビゲーションバーの高さも考慮する必要があるかもしれない
- でも、画面を回転すると、PopupWindowの位置がズレる。
- popupWindowはちゃんと破棄する必要があるが、isShowing()がtrueの時しか破棄できないので注意!(重要)
- setOutsideTouchableと、setFocusableにより、画面外のタッチでPopupWindowを消せるようになるが、
- Windowを消すために一度タッチする必要がある。
- タッチする度にpopupWindowを表示するためには、表示前にdismissで消すのが良いかもしれない。
- popupWindow.getWidth()とか、popupWindow.getHeight()では大きさを求められない。
- ContentViewから、大きさを求める必要があるようだ。
View v = popupWindow.getContentView();
w = v.getWidth();
h = v.getHeight();
- でも、この方法だと、一度Viewを表示させないと大きさがわからない。
- 表示させないで、大きさを求めることって、出来ないのかな。。
- いろいろ調べていたら、 ビューが表示される前のビューの大きさを測る方法を見つけたので、
- やっと、PopupWindowの大きさを求めることが出来た。
View v = popupWindow.getContentView();
v.measure(
MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
int w = v.getMeasuredWidth();
int h = v.getMeasuredHeight();
- これを見つけるまで、ずいぶんと悩んでしまった。
- popupWindowの大きさを求めるには、getMeasuredWidth,getMeasuredHeightを上記のように使う必要がある。(重要)
- Viewの大きさが中身によって決まる場合、Viewの内容を設定してからmeasureを実行しないと、正しい大きさが求められないので注意
- popupWindowに、×印の画像を用意して、Closeボタンをつけてみる
- 画像をボタンとして扱うImageButtonを使ってみた。
LinearLayout layout = (LinearLayout)activity.getLayoutInflater().inflate(R.layout.popupwindow, null);
:
// Closeボタン
ImageButton popupClose = (ImageButton)layout.findViewById(R.id.closebtn);
popupClose.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
popupWindow.dismiss();
}
});
- popupWindow上のリソースは、直接findViewByIdするとnullを返すので、layoutから指定する必要3がある(重要)
- 画像は src で指定しても良いが、backgroundの方が大きさの調整がやり易かった。
- ImageViewをボタンにしても良い。
- ImageButtonをImageViewに置き換えるだけ。ImageViewの方がレイアウトがやりやすいかも。
- showAtLocationでは、Gravityで基準位置を設定できる
// showAtLocationは、setWidth,setHeightで、大きさを指定しないとダメらしい
popupWindow.setWidth(LayoutParams.WRAP_CONTENT);
popupWindow.setHeight(LayoutParams.WRAP_CONTENT);
popupWindow.showAtLocation(this,Gravity.TOP|Gravity.LEFT,(int)e.getX(),(int)e.getY());
- 基準位置をTopLeftにすれば、クリック位置をそのまま渡せると思ったんだけど、
- Popup表示の位置指定は、指定したViewの座標じゃなくて、画面全体の座標のようだ
- Centerを指定しても、Viewの中央じゃなくて、画面全体の中央みたいだし。
- そうだとすると、Viewを指定している意味が無い気がするんだけど。。
- どちらかというと、PopupWindow自体の基準位置を指定できるようにして欲しかったなぁ。
- レイアウトの位置を設定するGravity定数値
- 意味が分からないものもあるが、とりあえず値を調べたのでメモ
| Parameter | Decimal | Hex | 意味 |
| TOP | 48 | 0x30 | コンテナ上部に配置 |
| BOTTOM | 80 | 0x50 | コンテナ下部に配置 |
| LEFT | 3 | 0x03 | コンテナ左側に配置 |
| RIGHT | 5 | 0x05 | コンテナ右側に配置 |
| CENTER | 17 | 0x11 | コンテナ中央に配置 |
| CENTER_HORIZONTAL | 1 | 0x01 | コンテナ左右の中央に配置 |
| CENTER_VERTICAL | 16 | 0x10 | コンテナ上下の中央に配置 |
| NO_GRAVITY | 0 | 0x00 | 指定なし |
| FILL | 119 | 0x77 | |
| FILL_HORIZONTAL | 7 | 0x07 | |
| FILL_VERTICAL | 112 | 0x70 | |
| CLIP_HORIZONTAL | 8 | 0x08 | |
| CLIP_VERTICAL | 128 | 0x80 | |
| START | 8388611 | 0x800003 | |
| END | 8388613 | 0x800005 | |
| AXIS_CLIP | 8 | 0x08 | |
| AXIS_PULL_AFTER | 4 | 0x04 | |
| AXIS_PULL_BEFORE | 2 | 0x02 | |
| AXIS_SPECIFIED | 1 | 0x01 | |
| AXIS_X_SHIFT | 0 | 0x00 | |
| AXIS_Y_SHIFT | 4 | 0x04 | |
| DISPLAY_CLIP_HORIZONTAL | 16777216 | 0x1000000 | |
| DISPLAY_CLIP_VERTICAL | 268435456 | 0x10000000 | |
| HORIZONTAL_GRAVITY_MASK | 7 | 0x07 | |
| VERTICAL_GRAVITY_MASK | 112 | 0x70 | |
| RELATIVE_HORIZONTAL_GRAVITY_MASK | 8388615 | 0x800007 | |
| RELATIVE_LAYOUT_DIRECTION | 8388608 | 0x800000 | |
- PopupWindow関連、調べてるといろいろ変な癖がありそうなので、メモしておく
- Androidで、引き伸ばし可能な画像フォーマットとして、9-Patch Imageというのがある
- 自分の環境では、Android SDK の C:\Android\android-sdk\tools\ draw9patch.bat を起動
- 予め、作成しておいた png 形式のファイルを開く
- png画像の周辺に1pxの領域を追加して、その辺上に伸縮する箇所を定義する
- Popupメニューというと、標準ではContextMenuというのがあるので、
- まずContextMenuを表示するViewをActivityのonCreate時に登録
cv = (customView)findViewById(R.id.myView1);
registerForContextMenu(cv);
@Override
public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, view, menuInfo);
int viewId = view.getId();
if (viewId == R.id.myView1) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.context_menu, menu);
}
}
@Override
public boolean onContextItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.context_menu1:
showMsg("item0 selected");
return true;
case R.id.context_menu2:
showMsg("item1 selected");
return true;
default:
return super.onContextItemSelected(item);
}
}
これだけなら、とても簡単だ。
- でもcostomViewで、GestureDetectorをインプリメントしていると、Contextメニューは表示されない
- ContextMenuとGestureDetectorは併用出来ないのかな?
- でもContextMenuって、アイコン表示されないんだなぁ。文字幅にフィットしてくれないし。
- それにタップした位置に表示されるわけじゃないので、Popupメニューとは言えないなぁ。
- Viewの中央に表示されるだけ4なので、メニューというより使い勝手の悪いDialogのような感じ。。
- うーん。これなら、面倒だけど自分でPopupWindowを作った方がいいかも。。。
2PopupWindowの左上を何処に表示するかを指定する。
3これは、popupWindowの構成に因るものなのかもしれない。
4Viewが小さければ良いのかもしれないけど。。
[top]
[プログラムの部屋に戻る]
⇒ Disqusの広告がうるさすぎるので基本は非表示にしました