告别繁琐引导页开发:Welcome Coordinator 打造丝滑Android应用引导体验

告别繁琐引导页开发:Welcome Coordinator 打造丝滑Android应用引导体验

【免费下载链接】welcome-coordinator Welcome Coordinator for Android 【免费下载链接】welcome-coordinator 项目地址: https://gitcode.com/gh_mirrors/we/welcome-coordinator

你还在为这些问题头疼吗?

开发Android应用引导页时,你是否遇到过这些痛点:

  • 实现流畅的页面切换动画需要编写大量自定义代码
  • 不同设备上的适配问题难以解决
  • 无法灵活控制页面间的过渡效果
  • 自定义行为与交互逻辑复杂,维护成本高

本文将带你全面掌握Welcome Coordinator库,一个专为Android打造的引导页/向导页解决方案,让你在30分钟内构建出媲美主流应用的专业引导界面。

读完本文后,你将能够:

  • 快速集成Welcome Coordinator到现有项目
  • 创建具有平滑过渡效果的多页面引导流程
  • 自定义页面元素的动画与交互行为
  • 解决不同屏幕尺寸的适配问题
  • 实现复杂的视差滚动和页面切换效果

什么是Welcome Coordinator?

Welcome Coordinator是一个功能强大的Android库,专为创建令人印象深刻的应用引导页和表单向导而设计。它基于Android的 CoordinatorLayout,提供了丰富的页面管理和动画效果,让开发者能够轻松实现复杂的页面交互和过渡效果。

mermaid

该库的核心优势在于:

  • 简单易用:通过XML布局和少量Java代码即可实现复杂效果
  • 高度可定制:支持自定义页面行为、动画和交互效果
  • 轻量级:不会显著增加应用体积
  • 兼容性好:支持Android 4.0 (API 14)及以上版本
  • 丰富的示例:提供完整的演示项目,展示各种使用场景

快速开始:5分钟集成指南

1. 配置项目依赖

在你的app模块下的build.gradle文件中添加以下依赖:

dependencies {
    ...
    implementation 'com.redbooth:WelcomeCoordinator:1.0.1'
}

2. 添加布局文件

在需要显示引导页的Activity布局文件中添加WelcomeCoordinatorLayout:

<com.redbooth.WelcomeCoordinatorLayout
    android:id="@+id/coordinator"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:indicatorSelected="@color/colorPrimary"
    app:indicatorUnselected="@color/colorAccent"
    app:showIndicators="true"
    app:scrollingEnabled="true"/>

可配置的主要属性:

  • indicatorSelected:选中状态的指示器颜色
  • indicatorUnselected:未选中状态的指示器颜色
  • showIndicators:是否显示页面指示器
  • scrollingEnabled:是否允许滚动

3. 创建引导页内容

为每个引导页面创建单独的布局文件,例如welcome_page_1.xml

<com.redbooth.WelcomePageLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:layout_width="150dp"
        android:layout_height="150dp"
        android:layout_centerInParent="true"
        android:src="@drawable/ic_welcome_image_1"
        app:view_behavior=".behaviors.ParallaxImageBehavior"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/image"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="24dp"
        android:text="欢迎使用我们的应用"
        android:textSize="20sp"
        app:view_behavior=".behaviors.FadeInBehavior"/>

</com.redbooth.WelcomePageLayout>

注意根布局必须是com.redbooth.WelcomePageLayout,这样才能正确应用页面行为。

4. 在Activity中初始化

在你的Activity中初始化WelcomeCoordinatorLayout并添加页面:

public class WelcomeActivity extends AppCompatActivity {
    private WelcomeCoordinatorLayout coordinatorLayout;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_welcome);
        
        coordinatorLayout = findViewById(R.id.coordinator);
        
        // 添加引导页面
        coordinatorLayout.addPage(
            R.layout.welcome_page_1,
            R.layout.welcome_page_2,
            R.layout.welcome_page_3,
            R.layout.welcome_page_4
        );
        
        // 设置页面滚动监听器
        coordinatorLayout.setOnPageScrollListener(new WelcomeCoordinatorLayout.OnPageScrollListener() {
            @Override
            public void onScrollPage(View v, float progress, float maximum) {
                // 页面滚动时的回调
                Log.d("Welcome", "滚动进度: " + progress);
            }
            
            @Override
            public void onPageSelected(View v, int pageSelected) {
                // 页面选中时的回调
                Log.d("Welcome", "选中页面: " + pageSelected);
                
                // 最后一页隐藏指示器
                if (pageSelected == coordinatorLayout.getNumOfPages() - 1) {
                    coordinatorLayout.showIndicators(false);
                } else {
                    coordinatorLayout.showIndicators(true);
                }
            }
        });
    }
    
    // 按钮点击事件:跳转到下一页
    public void goToNextPage(View view) {
        int nextPage = coordinatorLayout.getPageSelected() + 1;
        if (nextPage < coordinatorLayout.getNumOfPages()) {
            coordinatorLayout.setCurrentPage(nextPage, true); // true表示启用动画
        } else {
            // 所有引导页完成,进入主应用
            startMainActivity();
        }
    }
    
    private void startMainActivity() {
        Intent intent = new Intent(this, MainActivity.class);
        startActivity(intent);
        finish();
    }
}

5. 运行效果

完成上述步骤后,你将看到一个包含4个页面的引导流程,带有平滑的滑动过渡效果和底部指示器。

mermaid

核心组件深度解析

1. WelcomeCoordinatorLayout

这是库的核心容器组件,继承自HorizontalScrollView,负责管理多个引导页面和处理滚动事件。

主要功能和方法:

方法描述
addPage(@LayoutRes int... layoutResourceIds)添加一个或多个引导页面
setCurrentPage(int newCurrentPage, boolean animated)切换到指定页面,可选择是否显示动画
getNumOfPages()获取页面总数
getPageSelected()获取当前选中的页面索引
showIndicators(boolean show)显示或隐藏页面指示器
setScrollingEnabled(boolean enabled)启用或禁用滚动
setOnPageScrollListener(OnPageScrollListener listener)设置页面滚动监听器

2. WelcomePageLayout

每个引导页面的根布局,继承自RelativeLayout,负责管理页面内的视图和它们的行为。

其LayoutParams支持特殊属性:

  • app:view_behavior:为子视图指定行为类
  • app:destiny:指定目标视图ID,用于某些行为

3. WelcomePageBehavior

定义页面元素行为的基类,是实现自定义动画和交互效果的关键。

主要方法:

public class WelcomePageBehavior {
    protected WelcomeCoordinatorLayout coordinator;
    protected View target;
    protected WelcomePageLayout page;
    
    // 设置相关引用
    public void setCoordinator(WelcomeCoordinatorLayout coordinator) { ... }
    public void setTarget(View target) { ... }
    public void setPage(WelcomePageLayout page) { ... }
    
    // 创建时调用
    protected void onCreate(WelcomeCoordinatorLayout coordinator) { ... }
    
    // 页面滚动时调用,progress为滚动进度(0-1)
    protected void onPlaytimeChange(WelcomeCoordinatorLayout coordinator, 
                                   float newPlaytime, float newScrollPosition) { ... }
}

自定义行为:打造独特的视觉体验

Welcome Coordinator的强大之处在于可以通过自定义WelcomePageBehavior实现各种复杂的动画和交互效果。下面我们将创建几种常见的自定义行为。

1. 视差滚动效果

创建一个视差滚动行为,使不同元素以不同速度移动,产生深度感:

public class ParallaxBehavior extends WelcomePageBehavior {
    private float parallaxFactor = 0.5f; // 视差因子,值越大移动距离越大
    private int originalX; // 初始X坐标
    
    @Override
    protected void onCreate(WelcomeCoordinatorLayout coordinator) {
        super.onCreate(coordinator);
        originalX = target.getLeft(); // 保存初始位置
    }
    
    @Override
    protected void onPlaytimeChange(WelcomeCoordinatorLayout coordinator, 
                                   float newPlaytime, float newScrollPosition) {
        super.onPlaytimeChange(coordinator, newPlaytime, newScrollPosition);
        
        // 计算视差偏移量
        int pageWidth = coordinator.getWidth();
        float offset = (newScrollPosition % pageWidth) / pageWidth;
        int translationX = originalX + (int)(offset * pageWidth * parallaxFactor);
        
        // 应用偏移
        target.setTranslationX(translationX);
    }
    
    // 设置视差因子
    public void setParallaxFactor(float factor) {
        this.parallaxFactor = factor;
    }
}

在布局文件中应用此行为:

<ImageView
    android:layout_width="150dp"
    android:layout_height="150dp"
    android:src="@drawable/ic_rocket"
    app:view_behavior=".behaviors.ParallaxBehavior"/>

2. 淡入淡出效果

创建一个根据页面滚动进度控制视图透明度的行为:

public class FadeBehavior extends WelcomePageBehavior {
    private float startAlpha = 0f; // 开始时的透明度
    private float endAlpha = 1f;   // 结束时的透明度
    private float startScroll = 0.2f; // 开始变化的滚动位置(0-1)
    private float endScroll = 0.8f;   // 结束变化的滚动位置(0-1)
    
    @Override
    protected void onPlaytimeChange(WelcomeCoordinatorLayout coordinator, 
                                   float newPlaytime, float newScrollPosition) {
        super.onPlaytimeChange(coordinator, newPlaytime, newScrollPosition);
        
        // 计算当前页面的滚动进度(0-1)
        int pageIndex = coordinator.getPageSelected();
        int pageWidth = coordinator.getWidth();
        float pageScroll = (newScrollPosition - pageIndex * pageWidth) / pageWidth;
        
        // 计算透明度
        float alpha;
        if (pageScroll < startScroll) {
            alpha = startAlpha;
        } else if (pageScroll > endScroll) {
            alpha = endAlpha;
        } else {
            // 在startScroll和endScroll之间线性变化
            float progress = (pageScroll - startScroll) / (endScroll - startScroll);
            alpha = startAlpha + (endAlpha - startAlpha) * progress;
        }
        
        // 应用透明度
        target.setAlpha(alpha);
    }
    
    // 设置开始和结束的透明度
    public void setAlphaRange(float start, float end) {
        this.startAlpha = start;
        this.endAlpha = end;
    }
    
    // 设置开始和结束的滚动位置
    public void setScrollRange(float start, float end) {
        this.startScroll = start;
        this.endScroll = end;
    }
}

3. 缩放动画效果

实现视图在页面滚动过程中的缩放效果:

public class ScaleBehavior extends WelcomePageBehavior {
    private float minScale = 0.8f;  // 最小缩放比例
    private float maxScale = 1.2f;  // 最大缩放比例
    private float midPoint = 0.5f;  // 中间点位置(0-1)
    
    @Override
    protected void onPlaytimeChange(WelcomeCoordinatorLayout coordinator, 
                                   float newPlaytime, float newScrollPosition) {
        super.onPlaytimeChange(coordinator, newPlaytime, newScrollPosition);
        
        // 计算当前页面的滚动进度(0-1)
        int pageIndex = coordinator.getPageSelected();
        int pageWidth = coordinator.getWidth();
        float pageScroll = (newScrollPosition - pageIndex * pageWidth) / pageWidth;
        
        // 计算缩放比例 - 在中间点时最大,在页面边缘时最小
        float distanceFromMid = Math.abs(pageScroll - midPoint);
        float scale = maxScale - (maxScale - minScale) * (distanceFromMid / midPoint);
        
        // 应用缩放
        target.setScaleX(scale);
        target.setScaleY(scale);
    }
    
    // 设置缩放范围
    public void setScaleRange(float min, float max) {
        this.minScale = min;
        this.maxScale = max;
    }
}

4. 在XML中应用自定义行为

创建自定义行为后,可以在布局文件中直接应用:

<com.redbooth.WelcomePageLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:id="@+id/main_image"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:layout_centerInParent="true"
        android:src="@drawable/ic_feature_image"
        app:view_behavior=".behaviors.ScaleBehavior"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/main_image"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="32dp"
        android:text="探索更多功能"
        android:textSize="22sp"
        app:view_behavior=".behaviors.ParallaxBehavior"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/title_text"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="16dp"
        android:maxWidth="280dp"
        android:text="发现我们应用的强大功能,提升您的工作效率。"
        android:textSize="16sp"
        app:view_behavior=".behaviors.FadeBehavior"/>

</com.redbooth.WelcomePageLayout>

高级应用场景

1. 实现复杂的表单向导

Welcome Coordinator不仅可以用于应用引导页,还可以创建功能完善的表单向导:

public class FormWizardActivity extends AppCompatActivity {
    private WelcomeCoordinatorLayout coordinatorLayout;
    private UserProfile userProfile = new UserProfile();
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_form_wizard);
        
        coordinatorLayout = findViewById(R.id.form_coordinator);
        
        // 添加表单页面
        coordinatorLayout.addPage(
            R.layout.form_page_personal_info,
            R.layout.form_page_contact_details,
            R.layout.form_page_preferences,
            R.layout.form_page_confirmation
        );
        
        // 禁用自由滚动,只能通过按钮导航
        coordinatorLayout.setScrollingEnabled(false);
        
        // 隐藏默认指示器,使用自定义进度指示器
        coordinatorLayout.showIndicators(false);
        
        // 设置页面监听器
        coordinatorLayout.setOnPageScrollListener(new WelcomeCoordinatorLayout.OnPageScrollListener() {
            @Override
            public void onScrollPage(View v, float progress, float maximum) {}
            
            @Override
            public void onPageSelected(View v, int pageSelected) {
                updateNavigationButtons(pageSelected);
                updateProgressIndicator(pageSelected);
            }
        });
        
        // 初始化第一页表单
        initFormPage1();
    }
    
    // 更新导航按钮状态
    private void updateNavigationButtons(int currentPage) {
        Button prevButton = findViewById(R.id.btn_prev);
        Button nextButton = findViewById(R.id.btn_next);
        
        // 第一页隐藏"上一步"按钮
        prevButton.setVisibility(currentPage > 0 ? View.VISIBLE : View.GONE);
        
        // 最后一页将"下一步"按钮文本改为"完成"
        if (currentPage == coordinatorLayout.getNumOfPages() - 1) {
            nextButton.setText("完成");
            nextButton.setOnClickListener(v -> submitForm());
        } else {
            nextButton.setText("下一步");
            nextButton.setOnClickListener(v -> goToNextPage());
        }
    }
    
    // 更新进度指示器
    private void updateProgressIndicator(int currentPage) {
        int totalPages = coordinatorLayout.getNumOfPages();
        ProgressBar progressBar = findViewById(R.id.progress_bar);
        TextView progressText = findViewById(R.id.progress_text);
        
        progressBar.setMax(totalPages - 1);
        progressBar.setProgress(currentPage);
        progressText.setText(String.format("%d/%d", currentPage + 1, totalPages));
    }
    
    // 前往下一页
    public void goToNextPage() {
        int currentPage = coordinatorLayout.getPageSelected();
        
        // 验证当前页表单
        if (validateCurrentPage(currentPage)) {
            // 保存当前页数据
            saveCurrentPageData(currentPage);
            
            // 前往下一页
            coordinatorLayout.setCurrentPage(currentPage + 1, true);
            
            // 初始化下一页表单
            if (currentPage + 1 == 1) initFormPage2();
            else if (currentPage + 1 == 2) initFormPage3();
            else if (currentPage + 1 == 3) initConfirmationPage();
        }
    }
    
    // 返回上一页
    public void goToPrevPage(View view) {
        int currentPage = coordinatorLayout.getPageSelected();
        if (currentPage > 0) {
            coordinatorLayout.setCurrentPage(currentPage - 1, true);
        }
    }
    
    // 验证当前页表单
    private boolean validateCurrentPage(int page) {
        switch (page) {
            case 0: return validatePersonalInfo();
            case 1: return validateContactDetails();
            case 2: return validatePreferences();
            default: return true;
        }
    }
    
    // 保存当前页数据
    private void saveCurrentPageData(int page) {
        switch (page) {
            case 0: savePersonalInfo(); break;
            case 1: saveContactDetails(); break;
            case 2: savePreferences(); break;
        }
    }
    
    // 提交表单
    private void submitForm() {
        // 处理表单提交逻辑
        Toast.makeText(this, "表单提交成功!", Toast.LENGTH_LONG).show();
        finish();
    }
    
    // 各页面表单的初始化、验证和保存方法
    // ... (此处省略具体实现)
}

2. 实现视差滚动引导页

结合多种行为,创建具有深度感的视差滚动引导页:

<!-- welcome_page_parallax.xml -->
<com.redbooth.WelcomePageLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <!-- 背景层 - 慢速移动 -->
    <ImageView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="centerCrop"
        android:src="@drawable/parallax_bg"
        app:view_behavior=".behaviors.ParallaxBehavior"
        app:parallax_factor="0.1"/>

    <!-- 中间层 - 中速移动 -->
    <ImageView
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="150dp"
        android:src="@drawable/parallax_middle"
        app:view_behavior=".behaviors.ParallaxBehavior"
        app:parallax_factor="0.3"/>

    <!-- 前景层 - 快速移动 -->
    <ImageView
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_alignParentRight="true"
        android:layout_margin="30dp"
        android:src="@drawable/parallax_foreground"
        app:view_behavior=".behaviors.ParallaxBehavior"
        app:parallax_factor="0.6"/>

    <!-- 文本内容 - 淡入效果 -->
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="400dp"
        android:text="探索奇妙世界"
        android:textColor="@android:color/white"
        android:textSize="24sp"
        android:textStyle="bold"
        app:view_behavior=".behaviors.FadeBehavior"/>

</com.redbooth.WelcomePageLayout>

3. 实现页面间元素转换动画

通过自定义行为实现页面间元素的平滑过渡效果:

public class ElementTransitionBehavior extends WelcomePageBehavior {
    private int targetPageIndex; // 目标页面索引
    private int destinyViewId = WelcomePageLayout.LayoutParams.NO_DESTINY_VIEW;
    private View destinyView; // 目标页面中的对应视图
    private Rect startRect = new Rect(); // 起始位置
    private Rect endRect = new Rect();   // 目标位置
    
    @Override
    protected void onCreate(WelcomeCoordinatorLayout coordinator) {
        super.onCreate(coordinator);
        
        // 获取目标视图ID
        if (target.getLayoutParams() instanceof WelcomePageLayout.LayoutParams) {
            WelcomePageLayout.LayoutParams params = 
                (WelcomePageLayout.LayoutParams) target.getLayoutParams();
            destinyViewId = params.getDestinyViewId();
        }
        
        // 获取目标页面索引(假设behavior类名格式为"TransitionToPageXBehavior")
        String behaviorClassName = getClass().getSimpleName();
        if (behaviorClassName.startsWith("TransitionToPage") && behaviorClassName.endsWith("Behavior")) {
            try {
                String pageIndexStr = behaviorClassName.substring(14, behaviorClassName.length() - 8);
                targetPageIndex = Integer.parseInt(pageIndexStr);
            } catch (Exception e) {
                Log.e("TransitionBehavior", "Invalid behavior class name: " + behaviorClassName);
            }
        }
    }
    
    @Override
    protected void onPlaytimeChange(WelcomeCoordinatorLayout coordinator, 
                                   float newPlaytime, float newScrollPosition) {
        super.onPlaytimeChange(coordinator, newPlaytime, newScrollPosition);
        
        int currentPage = coordinator.getPageSelected();
        int pageWidth = coordinator.getWidth();
        
        // 只有在当前页面或目标页面时才执行动画
        if (currentPage != targetPageIndex - 1 && currentPage != targetPageIndex) {
            return;
        }
        
        // 延迟获取目标视图(确保所有页面都已加载)
        if (destinyView == null && destinyViewId != WelcomePageLayout.LayoutParams.NO_DESTINY_VIEW) {
            ViewGroup targetPage = (ViewGroup) coordinator.getChildAt(0).getChildAt(targetPageIndex);
            if (targetPage != null) {
                destinyView = targetPage.findViewById(destinyViewId);
                if (destinyView != null) {
                    // 获取起始和结束位置
                    target.getGlobalVisibleRect(startRect);
                    destinyView.getGlobalVisibleRect(endRect);
                    
                    // 目标视图初始状态设为不可见
                    destinyView.setVisibility(View.INVISIBLE);
                }
            }
        }
        
        // 计算过渡进度
        float progress;
        if (currentPage == targetPageIndex - 1) {
            // 从当前页滚动到目标页
            progress = newScrollPosition / pageWidth - currentPage;
        } else {
            // 从目标页滚动回当前页
            progress = 1 - (newScrollPosition / pageWidth - currentPage);
        }
        
        // 应用过渡动画
        applyTransition(progress);
    }
    
    // 应用过渡动画
    private void applyTransition(float progress) {
        if (destinyView == null) return;
        
        // 当进度为0时,显示原视图,隐藏目标视图
        // 当进度为1时,隐藏原视图,显示目标视图
        target.setVisibility(progress < 1 ? View.VISIBLE : View.INVISIBLE);
        destinyView.setVisibility(progress > 0 ? View.VISIBLE : View.INVISIBLE);
        
        // 计算中间位置和大小
        int left = (int) (startRect.left + (endRect.left - startRect.left) * progress);
        int top = (int) (startRect.top + (endRect.top - startRect.top) * progress);
        int right = (int) (startRect.right + (endRect.right - startRect.right) * progress);
        int bottom = (int) (startRect.bottom + (endRect.bottom - startRect.bottom) * progress);
        
        // 应用到目标视图
        ViewGroup.MarginLayoutParams params = 
            (ViewGroup.MarginLayoutParams) destinyView.getLayoutParams();
        
        int newLeftMargin = left - destinyView.getParent().getLeft();
        int newTopMargin = top - destinyView.getParent().getTop();
        int newWidth = right - left;
        int newHeight = bottom - top;
        
        params.leftMargin = newLeftMargin;
        params.topMargin = newTopMargin;
        params.width = newWidth;
        params.height = newHeight;
        
        destinyView.setLayoutParams(params);
    }
}

在布局文件中应用此行为:

<!-- page1.xml -->
<com.redbooth.WelcomePageLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:id="@+id/feature_image"
        android:layout_width="120dp"
        android:layout_height="120dp"
        android:layout_centerInParent="true"
        android:src="@drawable/feature_login"
        app:destiny="@+id/header_image"
        app:view_behavior=".behaviors.TransitionToPage1Behavior"/>

    <!-- 其他视图... -->

</com.redbooth.WelcomePageLayout>

<!-- page2.xml -->
<com.redbooth.WelcomePageLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:id="@+id/header_image"
        android:layout_width="match_parent"
        android:layout_height="180dp"
        android:scaleType="centerCrop"
        android:src="@drawable/feature_login"/>

    <!-- 其他视图... -->

</com.redbooth.WelcomePageLayout>

实战案例:构建专业引导页

下面我们将综合运用前面所学的知识,创建一个功能完善、视觉精美的应用引导页。

1. 设计引导页流程

我们将创建一个包含4个页面的引导流程:

  • 欢迎页:介绍应用的核心价值
  • 功能页1:展示主要功能之一
  • 功能页2:展示主要功能之二
  • 操作引导页:引导用户完成首次操作
  • 完成页:欢迎用户并提供开始使用按钮

2. 实现页面布局与行为

欢迎页布局 (welcome_page_1.xml)

<com.redbooth.WelcomePageLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/primary_color">

    <ImageView
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:layout_centerInParent="true"
        android:src="@drawable/ic_app_logo"
        app:view_behavior=".behaviors.ScaleBehavior"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/app_logo"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="40dp"
        android:text="欢迎使用 TaskMaster"
        android:textColor="@android:color/white"
        android:textSize="28sp"
        android:textStyle="bold"
        app:view_behavior=".behaviors.FadeBehavior"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/welcome_title"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="16dp"
        android:maxWidth="280dp"
        android:text="智能任务管理助手,让你的工作效率提升三倍"
        android:textColor="@android:color/white"
        android:textSize="18sp"
        android:textAlignment="center"
        app:view_behavior=".behaviors.FadeBehavior"/>

</com.redbooth.WelcomePageLayout>

功能页1布局 (welcome_page_2.xml)

<com.redbooth.WelcomePageLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/secondary_color">

    <ImageView
        android:layout_width="220dp"
        android:layout_height="220dp"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="120dp"
        android:src="@drawable/ic_feature_tasks"
        app:view_behavior=".behaviors.ParallaxBehavior"
        app:parallax_factor="0.4"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/feature_image"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="32dp"
        android:text="智能任务管理"
        android:textColor="@android:color/white"
        android:textSize="24sp"
        android:textStyle="bold"
        app:view_behavior=".behaviors.FadeBehavior"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/feature_title"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="16dp"
        android:maxWidth="300dp"
        android:text="轻松创建、组织和跟踪你的任务,设置优先级和截止日期,再也不会忘记重要事项。"
        android:textColor="@android:color/white"
        android:textSize="16sp"
        android:textAlignment="center"
        app:view_behavior=".behaviors.FadeBehavior"/>

</com.redbooth.WelcomePageLayout>

完成页布局 (welcome_page_5.xml)

<com.redbooth.WelcomePageLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/accent_color">

    <ImageView
        android:layout_width="240dp"
        android:layout_height="240dp"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="100dp"
        android:src="@drawable/ic_welcome_complete"
        app:view_behavior=".behaviors.ScaleBehavior"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/complete_image"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="32dp"
        android:text="准备就绪!"
        android:textColor="@android:color/white"
        android:textSize="28sp"
        android:textStyle="bold"
        app:view_behavior=".behaviors.FadeBehavior"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/complete_title"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="16dp"
        android:maxWidth="300dp"
        android:text="你的智能任务管理助手已准备就绪,开始提升你的工作效率吧!"
        android:textColor="@android:color/white"
        android:textSize="16sp"
        android:textAlignment="center"
        app:view_behavior=".behaviors.FadeBehavior"/>

    <Button
        android:id="@+id/btn_get_started"
        android:layout_width="240dp"
        android:layout_height="56dp"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="60dp"
        android:background="@drawable/btn_rounded_white"
        android:text="开始使用"
        android:textColor="@color/accent_color"
        android:textSize="18sp"
        android:textStyle="bold"
        app:view_behavior=".behaviors.FadeBehavior"/>

</com.redbooth.WelcomePageLayout>

3. 主Activity实现

public class WelcomeActivity extends AppCompatActivity {
    private WelcomeCoordinatorLayout coordinatorLayout;
    private SharedPreferences preferences;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        // 检查是否已经显示过引导页
        preferences = getSharedPreferences("app_prefs", MODE_PRIVATE);
        if (preferences.getBoolean("guide_completed", false)) {
            // 已经显示过,直接进入主界面
            startMainActivity();
            return;
        }
        
        setContentView(R.layout.activity_welcome);
        
        coordinatorLayout = findViewById(R.id.welcome_coordinator);
        
        // 添加所有引导页面
        coordinatorLayout.addPage(
            R.layout.welcome_page_1,
            R.layout.welcome_page_2,
            R.layout.welcome_page_3,
            R.layout.welcome_page_4,
            R.layout.welcome_page_5
        );
        
        // 设置页面滚动监听器
        coordinatorLayout.setOnPageScrollListener(new WelcomeCoordinatorLayout.OnPageScrollListener() {
            @Override
            public void onScrollPage(View v, float progress, float maximum) {}
            
            @Override
            public void onPageSelected(View v, int pageSelected) {
                // 最后一页隐藏指示器
                coordinatorLayout.showIndicators(pageSelected < 4);
                
                // 为最后一页的按钮设置点击事件
                if (pageSelected == 4) {
                    View lastPage = coordinatorLayout.getChildAt(0).getChildAt(4);
                    Button getStartedButton = lastPage.findViewById(R.id.btn_get_started);
                    if (getStartedButton != null) {
                        getStartedButton.setOnClickListener(v1 -> {
                            // 标记引导页已完成
                            preferences.edit().putBoolean("guide_completed", true).apply();
                            startMainActivity();
                        });
                    }
                }
            }
        });
        
        // 初始化操作引导页的交互
        setupInteractiveGuidePage();
    }
    
    // 设置交互式引导页
    private void setupInteractiveGuidePage() {
        // 找到第三页(索引为3)
        View interactivePage = coordinatorLayout.getChildAt(0).getChildAt(3);
        if (interactivePage != null) {
            // 设置模拟任务项点击事件
            View taskItem1 = interactivePage.findViewById(R.id.sample_task_1);
            View taskItem2 = interactivePage.findViewById(R.id.sample_task_2);
            View taskItem3 = interactivePage.findViewById(R.id.sample_task_3);
            View checkBox1 = interactivePage.findViewById(R.id.checkbox_1);
            
            // 任务项点击事件
            View.OnClickListener taskClickListener = v -> {
                // 显示选中状态
                v.setSelected(true);
                
                // 延迟后自动进入下一页
                new Handler().postDelayed(() -> {
                    coordinatorLayout.setCurrentPage(4, true);
                }, 800);
            };
            
            taskItem1.setOnClickListener(taskClickListener);
            taskItem2.setOnClickListener(taskClickListener);
            taskItem3.setOnClickListener(taskClickListener);
            checkBox1.setOnClickListener(taskClickListener);
        }
    }
    
    // 进入主界面
    private void startMainActivity() {
        Intent intent = new Intent(this, MainActivity.class);
        startActivity(intent);
        finish();
    }
    
    // 跳过引导页
    public void skipWelcome(View view) {
        preferences.edit().putBoolean("guide_completed", true).apply();
        startMainActivity();
    }
}

性能优化与最佳实践

1. 内存优化

  • 避免在行为中创建大量对象:特别是在onPlaytimeChange方法中,该方法会频繁调用
  • 及时回收资源:不需要的监听器和引用应及时清除
  • 图片资源优化:为不同分辨率提供合适大小的图片,使用WebP格式减小体积
// 优化前:在onPlaytimeChange中创建新对象
@Override
protected void onPlaytimeChange(WelcomeCoordinatorLayout coordinator, 
                               float newPlaytime, float newScrollPosition) {
    // 每次调用都创建新的Rect对象
    Rect rect = new Rect();
    target.getGlobalVisibleRect(rect);
    // ...
}

// 优化后:使用成员变量复用对象
private Rect tempRect = new Rect();

@Override
protected void onPlaytimeChange(WelcomeCoordinatorLayout coordinator, 
                               float newPlaytime, float newScrollPosition) {
    // 复用已有的Rect对象
    target.getGlobalVisibleRect(tempRect);
    // ...
}

2. 性能优化

  • 减少过度绘制:避免在布局中叠加多个半透明视图
  • 简化布局层次:减少嵌套布局,使用merge标签和ConstraintLayout
  • 避免在滚动时执行复杂计算:必要时使用硬件加速或缓存计算结果
// 优化计算密集型操作
@Override
protected void onPlaytimeChange(WelcomeCoordinatorLayout coordinator, 
                               float newPlaytime, float newScrollPosition) {
    super.onPlaytimeChange(coordinator, newPlaytime, newScrollPosition);
    
    // 只有当滚动进度变化超过一定阈值时才执行计算
    if (Math.abs(newPlaytime - lastPlaytime) < 0.01) {
        return;
    }
    lastPlaytime = newPlaytime;
    
    // ...执行优化后的计算
}

3. 适配不同设备

  • 使用dp单位:确保在不同密度屏幕上的一致性
  • 支持横屏模式:为横屏设计合适的布局
  • 测试不同尺寸设备:确保在手机和平板上都有良好表现
<!-- 为不同屏幕尺寸提供不同布局 -->
// res/layout/welcome_page_1.xml (默认)
// res/layout-sw600dp/welcome_page_1.xml (平板)
// res/layout-land/welcome_page_1.xml (横屏)

4. 无障碍支持

  • 添加内容描述:为图片和交互元素添加contentDescription
  • 支持键盘导航:确保可以通过键盘或D-pad导航
  • 测试屏幕阅读器:确保TalkBack等辅助功能正常工作
<ImageView
    android:layout_width="200dp"
    android:layout_height="200dp"
    android:src="@drawable/ic_app_logo"
    android:contentDescription="应用标志"
    app:view_behavior=".behaviors.ScaleBehavior"/>

常见问题与解决方案

1. 页面内容显示不全或被截断

问题:在某些设备上,引导页内容显示不全或被截断。

解决方案

  • 确保使用相对布局而非绝对布局
  • 使用match_parent和wrap_content适当组合
  • 避免使用固定尺寸,使用百分比或权重
  • 为不同屏幕尺寸提供专门布局
<!-- 不好的做法:使用固定像素值 -->
<TextView
    android:layout_width="300dp"
    android:layout_height="wrap_content"
    android:layout_marginLeft="40dp"/>

<!-- 好的做法:使用相对布局和边距 -->
<TextView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginLeft="16dp"
    android:layout_marginRight="16dp"
    android:maxWidth="320dp"/>

2. 动画卡顿或不流畅

问题:页面滚动或元素动画卡顿,不流畅。

解决方案

  • 避免在动画中修改布局属性(如width, height, margin)
  • 优先使用属性动画(translationX, translationY, alpha, scale)
  • 复杂动画使用硬件加速
  • 减少同时执行的动画数量
// 开启硬件加速
target.setLayerType(View.LAYER_TYPE_HARDWARE, null);

// 动画结束后关闭硬件加速
ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
animator.addUpdateListener(animation -> {
    // 应用动画
});
animator.addListener(new AnimatorListenerAdapter() {
    @Override
    public void onAnimationEnd(Animator animation) {
        // 动画结束后恢复
        target.setLayerType(View.LAYER_TYPE_NONE, null);
    }
});

3. 自定义行为不生效

问题:自定义的WelcomePageBehavior没有被正确应用或不执行。

解决方案

  • 确保自定义行为类继承自WelcomePageBehavior
  • 确保布局文件中正确指定了behavior属性
  • 检查是否提供了默认构造函数
  • 确认behavior类的完整类名是否正确
// 正确的行为类实现
public class MyCustomBehavior extends WelcomePageBehavior {
    // 必须提供默认构造函数
    public MyCustomBehavior() {
        super();
    }
    
    @Override
    protected void onPlaytimeChange(WelcomeCoordinatorLayout coordinator, 
                                   float newPlaytime, float newScrollPosition) {
        super.onPlaytimeChange(coordinator, newPlaytime, newScrollPosition);
        // 实现自定义逻辑
    }
}
<!-- 正确指定behavior -->
<ImageView
    android:layout_width="100dp"
    android:layout_height="100dp"
    app:view_behavior="com.example.myapp.behaviors.MyCustomBehavior"/>
<!-- 或者使用相对路径(如果在同一个包中) -->
<ImageView
    android:layout_width="100dp"
    android:layout_height="100dp"
    app:view_behavior=".behaviors.MyCustomBehavior"/>

总结与展望

Welcome Coordinator库为Android开发者提供了一个强大而灵活的解决方案,用于创建高质量的应用引导页和表单向导。通过本文的介绍,你已经掌握了该库的核心功能和高级用法。

主要优势回顾

  • 简化引导页开发流程,减少代码量
  • 提供丰富的动画和过渡效果
  • 高度可定制,满足各种设计需求
  • 轻量级实现,不增加应用负担
  • 良好的兼容性,支持Android 4.0及以上版本

未来扩展方向

  • 集成ViewPager2支持,提供更好的性能
  • 添加更多预设动画效果
  • 支持垂直滚动引导页
  • 增加页面间数据传递机制
  • 提供Jetpack Compose版本

通过Welcome Coordinator,你可以轻松创建出专业级别的应用引导体验,为用户提供直观、友好的首次使用体验。无论是简单的应用介绍,还是复杂的表单向导,Welcome Coordinator都能满足你的需求,让你的应用在竞争激烈的市场中脱颖而出。

现在就动手集成Welcome Coordinator到你的项目中,打造令人印象深刻的应用引导体验吧!

【免费下载链接】welcome-coordinator Welcome Coordinator for Android 【免费下载链接】welcome-coordinator 项目地址: https://gitcode.com/gh_mirrors/we/welcome-coordinator

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值