还在担心Android功能不会用吗?Intro Showcase View助你快速实现功能引导
1. 引言
在现代应用开发中,如何有效引导用户快速上手并掌握应用的核心功能,是提升用户体验的重要一环。功能引导不仅帮助用户理解复杂功能,还能提高用户留存率,减少因操作复杂度带来的用户流失。随着 Android 平台的不断发展,功能引导的实现方式也在不断演进。本文将深入探讨如何在 Android 开发中实现高效的功能引导,特别是在传统 View 系统与 Jetpack Compose 环境中的实现。
2. 功能引导的设计原则
功能引导的设计需要遵循一定的规范和原则,以确保用户能够快速理解并掌握应用的关键功能。根据 Material Design 的设计规范,引导设计应注重以下几个方面:
• 可见性:引导内容应足够突出,避免被用户忽略。通过颜色、动画等方式增强其视觉吸引力。
• 简洁性:信息传达要简洁明了,避免过多的文字说明,用户能够一眼理解。
• 互动性:引导应与用户的操作相关联,提供即时的反馈,增强用户的参与感。
了解用户心理,特别是在首次使用应用时的心理状态,有助于设计更具吸引力和有效的引导流程。
3. 功能引导的技术实现
使用传统 View 系统实现功能引导
在传统 Android 开发中,MaterialTapTargetPrompt
是一个广泛使用的库,旨在帮助开发者实现基于 Material Design 的功能引导。以下是如何使用该库的详细步骤:
• 依赖导入:首先,在
build.gradle
文件中添加库的依赖:dependencies {
implementation 'uk.co.samuelwall:material-tap-target-prompt:3.3.2'
}• 基本实现:通过以下代码,快速实现一个功能引导,该引导会在 FloatingActionButton 上显示一个提示:
MaterialTapTargetPrompt.Builder(this)
.setTarget(R.id.fab)
.setPrimaryText("发送你的第一封邮件")
.setSecondaryText("点击信封图标开始撰写你的第一封邮件")
.setPromptStateChangeListener { prompt, state ->
if (state == MaterialTapTargetPrompt.STATE_FOCAL_PRESSED) {
// 用户已点击引导目标
}
}
.show()• 状态变化处理:通过
setPromptStateChangeListener
,你可以监控引导的状态变化,并根据用户操作执行相应的逻辑。
Example
以下示例展示了在不同场景下如何使用该库进行功能引导:
1. 简单的视图目标引导
最基本的功能引导只需要指定目标视图(通过 ID 或 View 对象)和引导文本。
通过 ID 设置目标视图:
MaterialTapTargetPrompt.Builder(this)
.setTarget(R.id.fab)
.setPrimaryText("发送你的第一封邮件")
.show()
通过 View 对象设置目标视图:
MaterialTapTargetPrompt.Builder(this)
.setTarget(findViewById(R.id.fab))
.setPrimaryText("发送你的第一封邮件")
.show()
2. 引导 App Bar 中的菜单项
对于在 App Bar 中显示的菜单项,可以通过其 ID 进行目标设置。
菜单 XML 示例:
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_search"
android:icon="@drawable/ic_search"
android:title="搜索"
app:showAsAction="always" />
</menu>
引导菜单项:
MaterialTapTargetPrompt.Builder(this)
.setTarget(R.id.action_search)
.setPrimaryText("点击这里开始搜索")
.setIcon(R.drawable.ic_search)
.show()
3. RecyclerView 中的卡片引导
在 RecyclerView 中,你可以针对卡片或列表项的特定子视图设置引导目标。
获取卡片视图:
val card = mLinearLayoutManager.findViewByPosition(0) as LinearLayout
获取 ViewHolder 并设置引导:
if (card != null) {
val viewHolder = mRecyclerView.getChildViewHolder(card) as CardAdapter.ViewHolder
MaterialTapTargetPrompt.Builder(this)
.setTarget(viewHolder.mImageView)
.setClipToView(card.getChildAt(0))
.setPrimaryText("这是一张卡片")
.setSecondaryText("点击图片查看更多内容")
.show()
}
4. 定位在屏幕中央的引导
当目标视图距离屏幕边缘超过 88dp 时,引导提示会自动调整位置,以不覆盖目标视图。
MaterialTapTargetPrompt.Builder(this)
.setTarget(targetView)
.setPrimaryText("中心位置的引导")
.show()
5. 自定义文本样式
你可以使用 SpannableStringBuilder
自定义引导文本的样式,如改变文本颜色、背景等。
自定义主文本样式:
val primaryText = SpannableStringBuilder("发送你的第一封邮件")
val backgroundColour = BackgroundColorSpan(ContextCompat.getColor(this, R.color.colorAccent))
primaryText.setSpan(backgroundColour, 0, 4, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
自定义次文本样式:
val secondaryText = SpannableStringBuilder("点击信封图标开始撰写你的第一封邮件")
val foregroundColour = ForegroundColorSpan(ContextCompat.getColor(this, R.color.colorAccent))
secondaryText.setSpan(foregroundColour, 8, 15, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
应用自定义文本样式:
MaterialTapTargetPrompt.Builder(this)
.setTarget(targetView)
.setPrimaryText(primaryText)
.setSecondaryText(secondaryText)
.show()
通过上述示例,你可以灵活地在 Android 应用中实现不同场景下的功能引导,从简单的视图引导到复杂的 RecyclerView 卡片引导,再到自定义文本样式,这些都能极大地提升用户体验。
在 Jetpack Compose 中实现功能引导
随着 Android 开发逐渐向 Jetpack Compose 过渡,功能引导的实现也需要适应新的 UI 框架。intro-showcase-view
是一个专门为 Compose 设计的引导库,支持流畅的 Compose 风格的引导实现。
• 选择 Jetpack Compose 的原因:Jetpack Compose 提供了更灵活、更强大的 UI 构建方式,使引导的设计和实现更加直观和高效。
• 基于
intro-showcase-view
的实现:通过intro-showcase-view
,你可以快速创建一个引导,并与 Compose 组件无缝集成。
在 Jetpack Compose 中,IntroShowcaseView
是一个非常方便的库,它可以帮助你快速创建功能引导。以下是如何在 Compose 中使用该库的示例。
引入依赖
首先,在 build.gradle
文件中添加以下依赖:
implementation 'com.canopas.intro-showcase-view:introshowcaseview:<latest-version>'
示例代码
以下是一个使用 IntroShowcaseView
的完整示例,演示了如何在 Jetpack Compose 中创建一个功能引导:
@Composable
fun ShowcaseSample() {
var showAppIntro by remember {
mutableStateOf(true)
}
IntroShowcase(
showIntroShowCase = showAppIntro,
dismissOnClickOutside = false,
onShowCaseCompleted = {
// App Intro 完成后执行的操作
showAppIntro = false
},
) {
FloatingActionButton(
onClick = {},
modifier = Modifier.introShowCaseTarget(
index = 0,
style = ShowcaseStyle.Default.copy(
backgroundColor = Color(0xFF1C0A00), // 背景颜色
backgroundAlpha = 0.98f, // 背景透明度
targetCircleColor = Color.White // 目标圆圈的颜色
),
content = {
Column {
Text(
text = "检查邮件",
color = Color.White,
fontSize = 24.sp,
fontWeight = FontWeight.Bold
)
Text(
text = "点击这里检查/发送邮件",
color = Color.White,
fontSize = 16.sp
)
Spacer(modifier = Modifier.height(10.dp))
Icon(
painterResource(id = R.drawable.right_arrow),
contentDescription = null,
modifier = Modifier
.size(80.dp)
.align(Alignment.End),
tint = Color.White
)
}
}
),
backgroundColor = ThemeColor,
contentColor = Color.White,
elevation = FloatingActionButtonDefaults.elevation(6.dp)
) {
Icon(
Icons.Filled.Email,
contentDescription = "Email"
)
}
}
}
功能说明
•
IntroShowcase
是库中提供的主要组件,用于包裹需要引导的内容。•
introShowCaseTarget
是扩展函数,用于设置引导的目标视图、样式和内容。•
ShowcaseStyle
可以用于自定义引导的样式,例如背景颜色、透明度和目标圈颜色。
通过 IntroShowcaseView
,在 Jetpack Compose 中实现功能引导变得非常简单。这种方式不仅简化了开发流程,还为用户提供了直观的操作提示,提升了用户体验。示例代码:https://github.com/canopas/compose-intro-showcase/tree/master/app
4. 自定义与高级应用
在许多情况下,默认的引导形式可能无法满足需求。此时,可以通过自定义来实现更复杂的引导场景。
• 自定义形状与动画:
MaterialTapTargetPrompt
提供了扩展接口,可以自定义引导的形状(如矩形、椭圆)和动画效果,使引导更符合应用的整体风格。• 根据应用场景定制引导:例如,在引导多个步骤的复杂流程时,确保每一步都与用户操作密切相关,并通过视觉提示引导用户完成整个流程。
• 高级应用:在实现多步骤引导或处理复杂的用户场景时,可以结合条件判断、用户数据等实现更具针对性的引导体验。
在使用 MaterialTapTargetPrompt
库时,除了默认的功能外,你还可以通过自定义 Builder
、Shapes
、Text
等组件,进一步增强功能引导的灵活性和可扩展性。以下内容将介绍如何进行这些自定义操作。
1. 自定义 Builder
MaterialTapTargetPrompt
中的默认 Builder
实现继承自 PromptOptions
,而 PromptOptions
提供了创建点击目标的通用功能。你可以扩展 PromptOptions
来实现自定义功能。
例如,以下自定义 Builder
在引导显示时,会将指定的键值存储为 true
到 SharedPreferences
,并且只有在该键值为 false
时才会显示引导提示。
class CustomTapTargetPromptBuilder(context: Context) :
MaterialTapTargetPrompt.Builder(context) {
private val sharedPreferences = context.getSharedPreferences("MyPrefs", Context.MODE_PRIVATE)
fun setPreferenceKey(key: String): CustomTapTargetPromptBuilder {
val shouldShow = sharedPreferences.getBoolean(key, false)
if (!shouldShow) {
this.setOnHidePromptListener {
sharedPreferences.edit().putBoolean(key, true).apply()
}
} else {
this.setPrimaryText("Prompt already shown")
}
return this
}
}
使用自定义 Builder
:
CustomTapTargetPromptBuilder(this)
.setTarget(R.id.fab)
.setPrimaryText("点击这里开始")
.setPreferenceKey("show_prompt_fab")
.show()
2. 自定义形状(Shapes)
默认情况下,MaterialTapTargetPrompt
使用圆形作为引导形状。不过,你可以通过扩展 PromptBackground
和 PromptFocal
类,渲染任何其他形状。例如,使用矩形作为引导背景。
new MaterialTapTargetPrompt.Builder(this)
.setTarget(view)
.setPrimaryText("Different shapes")
.setSecondaryText("Extend PromptFocal or PromptBackground to change the shapes")
.setPromptBackground(new RectanglePromptBackground())
.setPromptFocal(new RectanglePromptFocal())
.show();
3. 自定义背景效果
可以通过设置 PromptBackground
来使背景变暗,从而突出引导目标。
builder.setPromptBackground(DimmedPromptBackground())
.setPrimaryText("这是一个暗背景效果的引导")
.show()
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.support.annotation.NonNull;
import android.util.DisplayMetrics;
import uk.co.samuelwall.materialtaptargetprompt.extras.PromptOptions;
import uk.co.samuelwall.materialtaptargetprompt.extras.backgrounds.CirclePromptBackground;
/**
* Prompt background implementation that darkens behind the circle background.
*/
public class DimmedPromptBackground extends CirclePromptBackground
{
@NonNull private RectF dimBounds = new RectF();
@NonNull private Paint dimPaint;
public DimmedPromptBackground()
{
dimPaint = new Paint();
dimPaint.setColor(Color.BLACK);
}
@Override
public void prepare(@NonNull final PromptOptions options, final boolean clipToBounds, @NonNull Rect clipBounds)
{
super.prepare(options, clipToBounds, clipBounds);
DisplayMetrics metrics = Resources.getSystem().getDisplayMetrics();
// Set the bounds to display as dimmed to the screen bounds
dimBounds.set(0, 0, metrics.widthPixels, metrics.heightPixels);
}
@Override
public void update(@NonNull final PromptOptions options, float revealModifier, float alphaModifier)
{
super.update(options, revealModifier, alphaModifier);
// Allow for the dimmed background to fade in and out
this.dimPaint.setAlpha((int) (200 * alphaModifier));
}
@Override
public void draw(@NonNull Canvas canvas)
{
// Draw the dimmed background
canvas.drawRect(this.dimBounds, this.dimPaint);
// Draw the background
super.draw(canvas);
}
}
4. 自定义文本(Text)
MaterialTapTargetPrompt
中的文本由 PromptText
类渲染。大多数情况下,可以通过使用 SpannableStringBuilder
来实现文本内容和样式的更改。如果需要进一步自定义文本的布局和渲染方式,可以扩展 PromptText
类。
val primaryText = SpannableStringBuilder("发送你的第一封邮件")
primaryText.setSpan(
BackgroundColorSpan(ContextCompat.getColor(this, R.color.colorAccent)),
0, 4, Spanned.SPAN_INCLUSIVE_INCLUSIVE
)
val secondaryText = SpannableStringBuilder("点击信封图标开始撰写邮件")
secondaryText.setSpan(
ForegroundColorSpan(ContextCompat.getColor(this, R.color.colorAccent)),
8, 15, Spanned.SPAN_INCLUSIVE_INCLUSIVE
)
MaterialTapTargetPrompt.Builder(this)
.setTarget(R.id.fab)
.setPrimaryText(primaryText)
.setSecondaryText(secondaryText)
.show()
如果你需要更复杂的文本布局,可以扩展 PromptText
类,并通过 builder.setPromptText()
方法将自定义的文本处理类应用到引导中。
class CustomPromptText : PromptText() {
override fun draw(canvas: Canvas, paint: Paint, clipBounds: Rect, focalBounds: Rect) {
// 自定义文本绘制逻辑
}
}
MaterialTapTargetPrompt.Builder(this)
.setPromptText(CustomPromptText())
.setPrimaryText("自定义文本布局")
.show()
通过自定义 Builder
、Shapes
和 Text
,你可以为 Android 应用的功能引导添加更丰富、更个性化的功能。这种灵活性使得 MaterialTapTargetPrompt
成为一个强大而易用的工具,能够满足各种应用场景的需求。
5. 性能优化与兼容性考虑
功能引导的性能优化和兼容性问题也是开发者需要关注的重点:
• 性能影响分析:在引导显示过程中,动画、资源加载等可能会对性能产生影响,特别是在低端设备上。通过优化资源、减少不必要的动画,可以提升引导的流畅性。
• 兼容性适配:由于 Android 设备和版本的多样性,在实现功能引导时需要考虑不同版本之间的兼容性问题,确保在所有目标设备上都能正常运行。
6. 结束语
功能引导是提升用户体验的重要手段,随着 Android 开发环境的不断变化,引导的实现方式也在不断演进。本文介绍了在传统 View 系统与 Jetpack Compose 中实现功能引导的方法,并探讨了自定义引导、性能优化与兼容性问题。希望通过这些实践,开发者能够在未来的项目中更好地实现功能引导,提升应用的用户体验。
在实现功能引导的过程中,开发者应始终关注用户的实际需求,通过不断优化和改进,使引导成为用户与应用之间更加顺畅的桥梁。
这篇博客旨在为 Android 开发者提供全面的功能引导实现指南,从设计原则到技术实现,从自定义应用到性能优化,帮助开发者更好地掌握并应用功能引导技术。
参考
https://github.com/sjwall/MaterialTapTargetPrompt
https://sjwall.github.io/MaterialTapTargetPrompt
https://github.com/canopas/Intro-showcase-view
https://canopas.github.io/compose-intro-showcase/