そこにいろんな形の画像でボタンを作るには、背景を透過にした絵のイメージ画像を使う
この方法で実装した際、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; } }あとは、普通に表示処理、押されたときの処理を組んでいく
クリック処理はこんなもん
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設定やら
# 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 件のコメント:
コメントを投稿