マインスイーパーを開発していてタイル(マス)を拡大縮小かつスクロールできるようにしているので、終端に到達していない端にスクロールを予想させる影をつけたくなりました。
どうせ影をつけるならフェードイン・アウトのアニメーションをつけてみたので方法を説明します。
そそたた
このような誰も気が付かない部分をこだわるの結構好きです。
デザインの格言に「神は細部に宿る」というのがありますしね。
動作イメージ
上下左右にスクロールを予想させる影を250ミリ秒かけてフェードイン・アウトするアニメーションで表示、非表示を切り替えます。
分かりにくいですが左端のイメージを載せておきます。
ソースコード
Viewに対してアニメーションさせるのが簡単なので、影の画像を設定したImageViewを上下左右に4つ配置してfadeAnimationメソッド内でAlphaAnimationを使ってフェードイン・アウトさせています。
開発中のマインスイーパーのソースから関連する部分を抜粋して載せておきます。
そそたた
リソースサイズを気にするならAnimatedVectorDrawableを使う方がよさそうです。
試してないので分かりませんが多分できそうです。
開発環境は次の通りです。
PC | MacBook Pro(2016年モデル) |
IDE | Android Studio 4.0.1 |
Android SDK | minSdkVersion 21 targetSdkVersion 30 |
言語 | Kotlin 1.3.72 |
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
xmlns:app="http://schemas.android.com/apk/res-auto">
<com.sosotata.minesweeper.ui.widgets.SquareTileGameView
android:id="@+id/squareView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<ImageView
android:id="@+id/topShadow"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scaleType="fitXY"
android:src="@drawable/top_shadow"
android:visibility="gone"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
/>
<ImageView
android:id="@+id/bottomShadow"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scaleType="fitXY"
android:src="@drawable/bottom_shadow"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
/>
<ImageView
android:id="@+id/leftShadow"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:scaleType="fitXY"
android:src="@drawable/left_shadow"
android:visibility="gone"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
/>
<ImageView
android:id="@+id/rightShadow"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:scaleType="fitXY"
android:src="@drawable/right_shadow"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
/**
* [android.view.View.onDraw]
*/
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
if (::mRenderBitmap.isInitialized) {
canvas.drawBitmap(mRenderBitmap, mRenderMatrix, null)
}
//
// スクロールを予測させる影をフェードイン・アウトのアニメーションで表示
//
val act: Activity? = context as? Activity
val values = FloatArray(9)
mRenderMatrix.getValues(values)
// 左端の影
if (values[Matrix.MTRANS_X] < -0.5f) {
fadeAnimation(act?.leftShadow, VISIBLE)
} else {
fadeAnimation(act?.leftShadow, GONE)
}
// 上端の影
if (values[Matrix.MTRANS_Y] < -0.5f) {
fadeAnimation(act?.topShadow, VISIBLE)
} else {
fadeAnimation(act?.topShadow, GONE)
}
// 右端の影
if (mTranslateLimit.right - values[Matrix.MTRANS_X] < -0.5f) {
fadeAnimation(act?.rightShadow, VISIBLE)
} else {
fadeAnimation(act?.rightShadow, GONE)
}
// 下端の影
if (mTranslateLimit.bottom - values[Matrix.MTRANS_Y] < -0.5f) {
fadeAnimation(act?.bottomShadow, VISIBLE)
} else {
fadeAnimation(act?.bottomShadow, GONE)
}
}
private fun fadeAnimation(view: View?, visibility: Int) {
if (view?.visibility != visibility) {
val fade: AlphaAnimation = if (visibility == VISIBLE) {
AlphaAnimation(0.0f, 1.0f)
} else {
AlphaAnimation(1.0f, 0.0f)
}
fade.duration = 250
fade.fillAfter = true
view?.visibility = visibility
view?.startAnimation(fade)
}
}
コメント