そこにいろんな形の画像でボタンを作るには、背景を透過にした絵のイメージ画像を使う
この方法で実装した際、Viewの絵の部分をクリックしたときに動作させたいが、画像の余白(透過)部分を押しても反応してしまう問題がある
三角形の画像を想定したとき、
Viewの余白を押したときは反応せず、画像部分を押したときに反応する処理を考える
理論
ベクトルを使って押下判定を組む三角形の頂点座標をO、A、B、タッチしたときの座標Tとする
-
\vec{OT} = \vec{t}, \vec{OA} = \vec{a},\vec{OB} = \vec{b} とおくと\vec{t}は以下のように表せられる
証明は省略するが こんな感じ
条件 | 座標Tの位置 | ||
---|---|---|---|
k_1=0 | かつ | k_2=0 | 点O |
k_1=1 | かつ | k_2=0 | 点A |
k_1=0 | かつ | k_2=1 | 点B |
0<k_1<1 | かつ | k_2=0 | 線分OA上 |
k_1=0 | かつ | 0<k_2<1 | 線分OB上 |
0<k_1 かつ 0<k_2 かつ k_1+k_2=1 | 線分AB上 | ||
0<k_1 かつ 0<k_2 かつ k_1+k_2<1 | \Delta OAB 内 | ||
上記以外 | \Delta OAB 外 |
次にk_1、k_2の求め方
-
\vec{t} =
\begin{pmatrix}
t_x \\
t_y
\end{pmatrix}
,
\vec{a} =
\begin{pmatrix}
a_x \\
a_y
\end{pmatrix}
,
\vec{b} =
\begin{pmatrix}
b_x \\
b_y
\end{pmatrix}
とする
k_1 = \frac{t_x b_y - t_y b_x}{a_x b_y - a_y b_x} \\ k_2 = \frac{a_x t_y - a_y t_x}{a_x b_y - a_y b_x}
Androidで実装
まずは専用のImageViewを作るAndroidはClick時の処理はView.OnClickListenerに実装する
このClickLisnerの実行条件はボタンが押された状態から、リリースされた時
なので、この三角形押下判定をボタン押下判定時に仕込むと、条件の絞り込みが楽になる
あとは、普通に表示処理、押されたときの処理を組んでいく
- /**
- * 三角形の中でのみ押下判定ができるImageViewクラス
- */
- public class MyImageView extends ImageView {
- // 三角形の座標情報
- protected final PointF O; // 頂点O
- protected final PointF A; // 頂点A
- protected final PointF B; // 頂点B
- { // イニシャライザ。画像サイズを1×1として、座標位置を指定しておく
- O = new PointF(0.5f, 0.0f);
- A = new PointF(0f, 1f);
- B = new PointF(1f, 1f);
- }
- // コントラクタ群
- public MyImageView(Context context) { super(context);}
- public MyImageView(Context context, AttributeSet attrs) {super(context, attrs);}
- public MyImageView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);}
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- if(event.getAction()== MotionEvent.ACTION_DOWN) {
- float x = event.getX(), y = event.getY();
- // 押下イベント通知のとき、条件判定を行い、三角形外であれば押下を処理しない
- // これにより、リリース時に、むやみにClick処理が動作しない
- if(!isTouch(new PointF(x, y))) {
- return false;
- }
- }
- return super.onTouchEvent(event);
- }
- /**
- * 押下判定
- * タッチ座標が三角形の内側か判定する
- * @param T タッチ座標
- * @return <li>true:内側<li>false:外側
- */
- private boolean isTouch(final PointF T) {
- final PointF OA, OB, OO, OT;
- // ベクトル化
- int w = getWidth(), h = getHeight();
- OO = new PointF(w * O.x, h * O.y);
- OA = new PointF(w * A.x - OO.x, h * A.y - OO.y);
- OB = new PointF(w * B.x - OO.x, h * B.y - OO.y);
- OT = new PointF(T.x - OO.x, T.y - OO.y);
- // 連立方程式に持ち込んで、kを取得
- float a = OA.x * OB.y - OB.x * OA.y;
- if (a == 0) throw new IllegalStateException("座標計算ができません.");
- float k1, k2;
- k1 = (OT.x * OB.y - OB.x * OT.y) / a;
- k2 = (OA.x * OT.y - OT.x * OA.y) / a;
- return 0 <= k1 && 0 <= k2 && k1 + k2 <= 1;
- }
- }
クリック処理はこんなもん
layout設定やら
- public class MainActivity extends AppCompatActivity {
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- // クリックでトースト表示
- findViewById(R.id.my_button).setOnClickListener(v -> {Toast.makeText(v.getContext(), "テスト", Toast.LENGTH_SHORT).show();});
- }
- }
一応セレクタで押下画像を設定して、押している状態がわかるようにしとく
- # layout/activity_main.xml
- <?xml version="1.0" encoding="utf-8"?>
- <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
- android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
- android:paddingRight="@dimen/activity_horizontal_margin"
- android:paddingTop="@dimen/activity_vertical_margin"
- android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity">
- <TextView android:text="Hello World!" android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
- <com.example.kazuki.myapplication.MyImageView
- android:id="@+id/my_button"
- android:src="@drawable/mybtn_serect"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
- </RelativeLayout>
できた!!
- # drawable/mybtn_serect.xml
- <?xml version="1.0" encoding="UTF-8"?>
- <selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_pressed="true" android:drawable="@drawable/image2" />
- <item android:drawable="@drawable/image1" />
- </selector>
0 件のコメント:
コメントを投稿