この記事は、リンクバルアドベントカレンダー2022の14日目の記事です。
はじめに
よくスマホを使えば、多分新しいアプリケーションをインストールして初めて起動しようとするたびに、紹介や使用方法が表示されることがよくあります。 みなさんはそれらを何と呼びますか? チュートリアル、ウォークスルー、ガイド等?
これらは、Onboardingという一般名で知られています。 この記事では、Jetpack Composeを使用して簡単に作成する方法を紹介します。
Onboarding画面とは
Onboarding画面は、ユーザーがアプリケーションを起動したときに最初に目にするものです。First User Experience (FUX)として知られることもあります。通常、アプリケーションの使用方法、機能、アプリケーションがユーザーにどのように役立つかなど、アプリケーションに関するいくつかのものを示すために使用されます。これは、複雑な機能や紛らわしいUIを持つアプリケーションで一般的です。
この記事のOnboarding画面
「Onboarding」の画像をGoogleで検索すると、ほとんどの結果が上の画像のようになります。 しかし実際には、OnboardingのUI/UXデザインには他にも多くの形式があります。それらの一つはガイド/ウォークスルー(Guides/Walkthrough)です。
また、現在、Jetpack Composeを使用してガイド/ウォークスルー オンOnboardingを作成する方法に関する記事を見つけるのは困難です 。 現在の記事のほとんどは、HorizontalPagerを使用したスライドショー形式です。
この記事では、ガイド/ウォークスルーOnboarding画面を作成する簡単な方法を紹介します。
結果を見ましょう!!
実装
準備するもの
まず、次の準備/知識が必要です。
- プロジェクト。
- Preferences (SharedPreferences/DataStore)。
- Jetpack Composeアーキテクチャ。
- アプリの編集したスクリーンショット。
プロジェクト: もちろん、アプリの紹介画面を実装するので、最も重要なことはそのアプリのコードですね。サンプル用のため、この記事でJetpack Composeの組み込みテンプレートのJetchatを使用します。
この指示に従ってください 。
Preferences: Googleは、開発者がDataStore
を使用することを推奨しています。 ただし、この記事が長くなりすぎないように、代わりにSharedPreferences
を使用します。
後でDataStore
に変換することもお勧めします。
アプリの編集したスクリーンショット: この記事のマイナスポイントは、ガイド/ウォークスルーOnboarding画面のようなCompound Shape
を作成する方法がまだ見つかなかったことです。 現在の回避策はスクリーンショットを使用することですが、これでは相互運用性がなくなります。
はじめましょう
ステップ1: 現在のチュートリアルステップを保存する
まず、今のチュートリアルステップを格納するkey-value
のペアを保存する必要があります。
SharedPreferences
を使用すると、次のようになります。
object MyPreferences {
const val KEY_CHAT_STEP = "key_chat_step"
// ここにさらに画面を追加できる
lateinit var prefs: SharedPreferences
fun get(key: String): Int {
return prefs.getInt(key, 0)
}
fun set(key: String, value: Int) {
with(prefs.edit()) {
putInt(key, value)
apply()
}
}
}
class NavActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// ...
+ MyPreferences.prefs = getPreferences(Context.MODE_PRIVATE)
// ...
ステップ2: ViewModelを作成してFragment/Screenに追加する
Jetchat
を使用する場合は、package conversation
に移動してViewModel
を作成しましょう。
class ConversationViewModel : ViewModel() {
// 初期ステップを0で初期化する
private val _tutorialStep = MutableStateFlow(0)
val tutorialStep: StateFlow<Int> = _tutorialStep
init {
getTutorialStep()
}
// 次のステップに移動すると、新しい値を更新する
fun setTutorialStep(currentStep: Int = 0) {
_tutorialStep.value = currentStep
MyPreferences.set(MyPreferences.KEY_CHAT_STEP, currentStep)
}
// 実機に格納されているステップ値を取得する
private fun getTutorialStep() {
_tutorialStep.value = MyPreferences.get(MyPreferences.KEY_CHAT_STEP)
}
}
プロジェクトのコードスタイルによっては、違うかもしれません。 Jetchatの場合はConversationFragment.kt
にコードを追加します 。
class ConversationFragment : Fragment() {
+ private val viewModel: ConversationViewModel by viewModels()
private val activityViewModel: MainViewModel by activityViewModels()
override fun onCreateView(
// ...
ステップ3: チュートリアル画面を作る
チュートリアル画面の共通コンポーネントを作成します。
@Composable
fun TutorialScreen(
backgroundResId: Int, // Screen background Resource Id
contentXOffset: Float, // 説明文のxOffset
contentYOffset: Float, // 説明文のyOffset
onGoNext: () -> Unit, // [Next]ボタンをクリックすると実行されるアクション
goNextText: String = "Next",
content: @Composable () -> Unit
) {
Box(modifier = Modifier
.fillMaxSize()
.systemBarsPadding()
) {
// スクリーンショットを背景として
Image(
painter = painterResource(backgroundResId),
contentScale = ContentScale.FillBounds,
contentDescription = null,
)
// 説明文
Surface(
color = Color.Transparent,
contentColor = Color.White,
modifier = Modifier.offset(x = contentXOffset.dp, y = contentYOffset.dp)
) {
content()
}
// 画面左上のアクションボタン
Button(
onClick = onGoNext,
modifier = Modifier
.align(Alignment.TopEnd)
.padding(16.dp)
) {
Text(goNextText)
}
}
}
ステップ4: Conversationのチュートリアル画面を作成しする
conversation
packageに戻り、ConversationTutorial.kt
という名前の新しいファイルを作成します。
最大のステップ値を3
に設定します。つまり、チュートリアルが3つのステップがあります。
const val CONVERSATION_MAX_STEP = 3
@Composable
fun ConversationTutorial(step: Int, onClick: () -> Unit) {
when (step) {
0 -> ConversationTutorial1(onClick = onClick)
1 -> ConversationTutorial2(onClick = onClick)
2 -> ConversationTutorial3(onClick = onClick)
}
}
それぞれのチュートリアル画面を作成します。下記は例です 。
@Composable
private fun ConversationTutorial1(onClick: () -> Unit) {
TutorialScreen(
backgroundResId = R.drawable.chat_step_1,
contentXOffset = 20f,
contentYOffset = 600f,
onGoNext = onClick
) {
Column(
modifier = Modifier.wrapContentWidth(),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text("Write your message here")
Icon(Icons.Default.ArrowDownward, null)
}
}
}
ステップ5:ConversationFragmentにチュートリアル画面を追加する
class ConversationFragment : Fragment() {
// ...
override fun onCreateView(
// ...
JetchatTheme {
+ if (tutorialStep < CONVERSATION_MAX_STEP) {
+ ConversationTutorial(step = tutorialStep, onClick = toNextStep)
+ } else {
ConversationContent(
// ..
ステップ6: アニメーションを追加してインターフェイスをより魅力的にする
この記事では、Infinite Animation
を使用してNext/Finish
ボタンをドキドキさせて注意を引きます。
その他のJetpack Composeアニメーションについては、Jetpack Composeアニメーションで見てください。
@Composable
fun TutorialScreen(
backgroundResId: Int,
contentXOffset: Float,
contentYOffset: Float,
onGoNext: () -> Unit,
goNextText: String = "Next",
content: @Composable () -> Unit
) {
+ val infiniteTransition = rememberInfiniteTransition()
+ val scale by infiniteTransition.animateFloat(
+ initialValue = 1f,
+ targetValue = 1.1f,
+ animationSpec = infiniteRepeatable(
+ animation = tween(500, easing = LinearEasing),
+ repeatMode = RepeatMode.Reverse
+ )
+ )
Box(
// ..
Button(
onClick = onGoNext,
modifier = Modifier
.align(Alignment.TopEnd)
.padding(16.dp)
+ .graphicsLayer(
+ scaleX = scale,
+ scaleY = scale
+ )
) {
Text(goNextText)
}
}
おわりに
多くのアプリケーションがOnboarding画面を無視してきましたが、機能が複雑だったりインターフェースがわかりにくいアプリケーションの場合、Onboarding画面が必要になると思います。 Onboarding画面はユーザーに利益をもたらすだけでなく、ユーザーを感動させることでビジネスにも利益をもたらします。
shadow
背景の上にtransparent
背景があるものを作成するための回避策は見つかりませんでした(Combound Shape
の作成)。 回避策の提案があれば、遠慮なくコメントしてください 。
ここまで読んでいただきありがとうございました 。