Android 实现闪屏页+功能引导页

版权声明:本文为 like_program 原创文章,未经博主允许不得转载。 https://blog.csdn.net/like_program/article/details/52961753

转载请注明出处: http://blog.csdn.net/like_program/article/details/52961753

闪屏页和功能引导页想必大家都很熟悉了,现在很多 App 启动都有闪屏页和功能引导页,那么今天我来教下大家怎么实现闪屏页和功能引导页。

打开 Android Studio, 新建 GuideTest 项目。

1.实现闪屏页

闪屏页的实现很简单,就是将一张背景图显示 2 秒钟。

修改 activity_main.xml 为 activity_splash.xml,activity_splash.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:id="@+id/rl_splash"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/ic_splash"
    tools:context="com.example.guidetest.SplashActivity">

</RelativeLayout>

闪屏页的布局很简单,只有一张背景图片。

然后修改 MainActivity.java 为 SplashActivity.java ,SplashActivity.java 代码如下所示:

package com.example.guidetest;

import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.widget.RelativeLayout;

public class SplashActivity extends AppCompatActivity {

    /**
     * 闪屏页
     */
    private RelativeLayout rlSplash;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_splash);

        rlSplash = (RelativeLayout) findViewById(R.id.rl_splash);

        startAnim();
    }

    /**
     * 启动动画
     */
    private void startAnim() {
        // 渐变动画,从完全透明到完全不透明
        AlphaAnimation alpha = new AlphaAnimation(0, 1);
        // 持续时间 2 秒
        alpha.setDuration(2000);
        // 动画结束后,保持动画状态
        alpha.setFillAfter(true);

        // 设置动画监听器
        alpha.setAnimationListener(new Animation.AnimationListener() {

            @Override
            public void onAnimationStart(Animation animation) {

            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }

            // 动画结束时回调此方法
            @Override
            public void onAnimationEnd(Animation animation) {
                // 跳转到下一个页面
                jumpNextPage();
            }
        });

        // 启动动画
        rlSplash.startAnimation(alpha);
    }

    /**
     * 跳转到下一个页面
     */
    private void jumpNextPage() {
        startActivity(new Intent(SplashActivity.this, GuideActivity.class));
        finish();
    }
}

为了让闪屏页的启动过渡的自然一些,我们使用了渐变动画,当程序一启动,就会开始启动动画,背景图片会从完全透明逐渐变为完全不透明,动画结束后,就会跳转到功能引导页,然后结束当前 Activity。

闪屏页最好能占满全屏,说到全屏,有些同学可能第一反应就是 requestWindowFeature(Window.FEATURE_NO_TITLE) ,但是很不幸,加上这行代码,闪屏页也无法全屏。所以我们通过修改下 styles.xml 中 AppTheme 的一些属性,来达到全屏的效果,styles.xml 代码如下:

<resources>

    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
        <item name="windowNoTitle">true</item>
        <item name="windowActionBar">false</item>
        <item name="android:windowFullscreen">true</item>
        <item name="android:windowContentOverlay">@null</item>
    </style>

</resources>

为了能让闪屏页跳转到功能引导页,我们先来简单创建一个 Activity,以便让闪屏页跳转到这个 Activity。

新建 GuideActivity.java 和 activity_guide.xml ,activity_guide.xml 代码如下:

扫描二维码关注公众号,回复: 3800168 查看本文章
<?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"
    tools:context="com.example.guidetest.GuideActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="功能引导页"
        android:textColor="@android:color/black"
        android:textSize="25sp"/>

</RelativeLayout>

好了,闪屏页已经实现了,我们来运行一下,看下效果:

实现闪屏页

2.实现功能引导页

1.初步实现功能引导页

功能引导页,相信大家也见过不少了,功能引导页是可以左右滑动的,说到左右滑动,我们自然就想起了 ViewPager 这个类。接下来,我们就要用 ViewPager 来实现功能引导页。

修改 activity_guide.xml ,代码如下:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <android.support.v4.view.ViewPager
        android:id="@+id/vp_guide"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</RelativeLayout>

我们在布局文件中添加了 ViewPager 控件。

然后修改 GuideActivity.java 代码如下所示:

package com.example.guidetest;

import java.util.ArrayList;
import java.util.List;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.ImageView;

public class GuideActivity extends AppCompatActivity {

    /**
     * 功能引导页
     */
    private ViewPager mVpGuide;

    /**
     * 功能引导页展示的 ImageView 集合
     */
    private List<ImageView> mImageList;

    /**
     * 功能引导页展示的图片集合
     */
    private static int[] mImageIds = new int[] { R.drawable.guide_1,
            R.drawable.guide_2, R.drawable.guide_3 };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_guide);

        initView();
        mVpGuide.setAdapter(new GuideAdapter());
    }

    /**
     * 初始化页面
     */
    private void initView() {
        mVpGuide = (ViewPager) findViewById(R.id.vp_guide);

        mImageList = new ArrayList<ImageView>();
        // 将要展示的 3 张图片存入 ImageView 集合中
        for (int i = 0; i < mImageIds.length; i++) {
            ImageView image = new ImageView(this);
            // 将图片设置给对应的 ImageView
            image.setBackgroundResource(mImageIds[i]);

            mImageList.add(image);
        }
    }

    /**
     * 适配器
     */
    class GuideAdapter extends PagerAdapter {

        @Override
        public int getCount() {
            return mImageList.size();
        }

        @Override
        public boolean isViewFromObject(View arg0, Object arg1) {
            return arg0 == arg1;
        }

        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            container.addView(mImageList.get(position));
            return mImageList.get(position);
        }

        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            container.removeView(mImageList.get(position));
        }
    }
}

在 initView() 方法中,我们实例化了 ViewPager ,然后创建了一个用来存储 ImageView 控件的集合 mImageList,接着把事先准备好的图片依次设置给对应的 ImageView,并把这些 ImageView 都添加到集合 mImageList 中。

注意:这里将图片设置给 ImageView,最好使用 image.setBackgroundResource() ,如果使用 image.setImageResource() ,图片可能不会占满屏幕。

接着我们创建了适配器,将适配器和 mImageList 关联了起来。

好了,运行一下程序,看下效果:

初步实现功能引导页

2.添加功能引导页小圆点

单纯的滑动页面显然是不够的,我们还需要给页面加上小圆点,用小圆点来指示当前是在哪个页面。

我们准备用 3 个小灰点和 1 个小红点,进入页面时,小红点遮挡住第一个小灰点,等到滑动到第二个页面,小红点也移动到刚好能遮挡住第二个小灰点的位置,随着页面的滑动,小红点也不停移动。

我们来写小圆点的布局。我们可以用 shape 来定义小圆点的形状。

在 drawable 文件夹下新建 shape_dot_gray.xml ( 小灰点 ) 和 shape_dot_red.xml ( 小红点 )。

shape_dot_gray.xml 代码如下:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
       android:shape="oval">

    <solid android:color="@android:color/darker_gray"/>

</shape>

shape_dot_red.xml 代码如下:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
       android:shape="oval">

    <solid android:color="@android:color/holo_red_light"/>

</shape>

虽然定义的形状是 oval(椭圆),但是等会在布局文件中,我们把小圆点的宽高设置成一样,显示出来就是圆。

接着在布局文件中添加小圆点。修改 activity_guide.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"
    tools:context="com.example.guidetest.GuideActivity">

    <android.support.v4.view.ViewPager
        android:id="@+id/vp_guide"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

    <RelativeLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="20dp">

        <LinearLayout
            android:id="@+id/ll_dot_group"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="horizontal">

            <View
                android:layout_width="10dp"
                android:layout_height="10dp"
                android:background="@drawable/shape_dot_gray"/>

            <View
                android:layout_width="10dp"
                android:layout_height="10dp"
                android:layout_marginLeft="10dp"
                android:background="@drawable/shape_dot_gray"/>

            <View
                android:layout_width="10dp"
                android:layout_height="10dp"
                android:layout_marginLeft="10dp"
                android:background="@drawable/shape_dot_gray"/>
        </LinearLayout>

        <View
            android:id="@+id/view_red_dot"
            android:layout_width="10dp"
            android:layout_height="10dp"
            android:background="@drawable/shape_dot_red"/>
    </RelativeLayout>

</RelativeLayout>

我们在屏幕的下半部分添加了一个横向的 LinearLayout ,在 LinearLayout 中水平放置了 3 个小灰点,并且在第一个小灰点的上方覆盖了一个小红点。

接下来,我们就要实现让小红点跟随屏幕的滑动而移动了。我们知道,ViewPager 可以设置滑动监听 (OnPageChangeListener) ,当屏幕滑动时,会不停回调 onPageScrolled() 方法。

onPageScrolled() 这个方法里有两个参数:

position:这个参数的意思是 当前页面的索引,也就是当前在 ViewPager 的哪一页,默认从 0 开始,表示第一页

positionOffset:这个参数的意思是 屏幕滑动的距离占屏幕宽度的百分比。

有了这两个参数,想让小红点跟着屏幕移动也就不难了。接下来我们就在这个方法中实现小红点移动的逻辑。

修改 GuideActivity.java 代码如下:

package com.example.guidetest;

import java.util.ArrayList;
import java.util.List;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v4.view.ViewPager.OnPageChangeListener;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout.LayoutParams;

public class GuideActivity extends AppCompatActivity {

    private static final String TAG = "GuideActivity";

    /**
     * 功能引导页
     */
    private ViewPager mVpGuide;

    /**
     * 功能引导页展示的 ImageView 集合
     */
    private List<ImageView> mImageList;

    /**
     * 功能引导页展示的图片集合
     */
    private static int[] mImageIds = new int[] { R.drawable.guide_1,
            R.drawable.guide_2, R.drawable.guide_3 };

    private LinearLayout mDotGroup;

    private View mRedDot;

    private int mDotDistance;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_guide);

        initView();
    }

    /**
     * 初始化页面
     */
    private void initView() {
        mVpGuide = (ViewPager) findViewById(R.id.vp_guide);
        mDotGroup = (LinearLayout) findViewById(R.id.ll_dot_group);
        mRedDot = findViewById(R.id.view_red_dot);

        mImageList = new ArrayList<ImageView>();
        // 将要展示的 3 张图片存入 ImageView 集合中
        for (int i = 0; i < mImageIds.length; i++) {
            ImageView image = new ImageView(this);
            // 将图片设置给对应的 ImageView
            image.setBackgroundResource(mImageIds[i]);

            mImageList.add(image);
        }

        // 计算相邻小灰点之间的距离
        mDotDistance = mDotGroup.getChildAt(1).getLeft() - mDotGroup.getChildAt(0).getLeft();

        mVpGuide.setAdapter(new GuideAdapter());
        mVpGuide.setOnPageChangeListener(new GuidePageChangeListener());
    }

    /**
     * 适配器
     */
    class GuideAdapter extends PagerAdapter {

        @Override
        public int getCount() {
            return mImageList.size();
        }

        @Override
        public boolean isViewFromObject(View arg0, Object arg1) {
            return arg0 == arg1;
        }

        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            container.addView(mImageList.get(position));
            return mImageList.get(position);
        }

        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            container.removeView(mImageList.get(position));
        }
    }

    /**
     * 滑动监听
     */
    class GuidePageChangeListener implements OnPageChangeListener {

        @Override
        public void onPageScrollStateChanged(int arg0) {

        }

        // 页面滑动时回调此方法
        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
            // 滑动过程中,小红点移动的距离
            int distance = (int) (mDotDistance * (positionOffset + position));
            Log.d(TAG, "小红点移动的距离:" + distance);
            // 获取当前小红点的布局参数
            LayoutParams params = (LayoutParams) mRedDot.getLayoutParams();
            // 修改小红点的左边缘和父控件(RelativeLayout)左边缘的距离
            params.leftMargin = distance;
            mRedDot.setLayoutParams(params);
        }

        @Override
        public void onPageSelected(int arg0) {

        }
    }
}

我们先计算了相邻小灰点之间的距离,然后用 小灰点之间的距离 乘以 ( 屏幕滑动的距离占屏幕宽度的百分比 + 当前页面的索引),得到的就是小红点应该滑动的距离。

然后修改小红点的 LayoutParams,就实现了小红点移动的逻辑。

有的同学可能对这行语句不怎么理解:

params.leftMargin = distance;

distance 不是小红点移动的距离吗,为什么可以赋值给 params.leftMargin (小红点的左边缘和父控件( RelativeLayout )左边缘的距离)。

其实,小红点初始化的时候,它的左边缘和父控件( RelativeLayout )左边缘的距离就是 0( 可以看布局文件 ),所以小红点移动的距离也等于它的左边缘和父控件( RelativeLayout )左边缘的距离。

运行一下程序:

小红点不动

咦,小红点没有移动?LogCat 里显示的小红点移动的距离是 0 ?

小红点移动距离

我擦,这是什么情况???

别急,待我去 StackOverflow 一下。。。

3.解决小红点不移动

哦,找到了

How can you tell when a layout has been drawn?

stackoverflow

嗯,总结了一下,原因是这样的…

一个控件要想绘制到屏幕上,需要经历 3 个步骤:

onMeasure() 测量控件的大小
onLayout() 把控件绘制到屏幕上哪个位置
onDraw() 绘制控件,比如背景啊,形状啊,颜色啊,巴拉巴拉。。。

我们要得到相邻小灰点之间的距离,得等到 onLayout() 方法调用完吧,onLayout() 方法调用完,位置确定下来了,getLeft() 方法才能拿到值。

那么问题来了,我们刚才写的代码都是在 onCreate() 方法中执行的啊,onCreate() 方法又是在 onLayout() 方法之前执行的,也就是说,我们在 onCreate() 方法中,想调用 getLeft() 方法拿到值。但是这时候 onLayout() 方法都还没有执行,我们去哪拿值。。。

有的同学可能会说,onCreate() 方法拿不到值,那 onStart() 方法呢,再不行,就 onResume() 方法。

很不幸,别说 onLayout() 方法了,就连 onMeasure() 都比这些方法执行的晚。。。

为了让大家对这些方法的执行顺序了解的更清晰,放一张混合生命周期图:

原图出自:如何在onCreate中测量View的实际宽高

生命周期

说了这么多,我们来上代码:

// 计算相邻小灰点之间的距离
// mDotDistance = mDotGroup.getChildAt(1).getLeft() -
// mDotGroup.getChildAt(0).getLeft();
// Log.d(TAG, "相邻小灰点之间的距离:" + mDotDistance);

// 获取控件树,对 onLayout 结束事件进行监听
mDotGroup.getViewTreeObserver().addOnGlobalLayoutListener(
        new OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                // OnGlobalLayoutListener 可能会被多次触发,因此在得到了高度之后,要将 OnGlobalLayoutListener 注销掉
                mDotGroup.getViewTreeObserver()
                        .removeGlobalOnLayoutListener(this);
                // 计算相邻小灰点之间的距离
                mDotDistance = mDotGroup.getChildAt(1).getLeft()
                        - mDotGroup.getChildAt(0).getLeft();
                Log.d(TAG, "相邻小灰点之间的距离:" + mDotDistance);
            }
        });

可以看到,我们给小灰点的父控件(mDotGroup)的控件树设置了一个全局布局监听器。当控件树中的 View 可见时,也就是小灰点已经绘制完成时,回调 onGlobalLayout() 方法,这个时候,就能拿到 getLeft() 的值了。

还有人不懂?
好,那我用更通俗,更接地气的话来解释一下。

在 onCreate() 方法中,我们和 mDotGroup 的对话如下:

我们:“你的子控件小灰点的 getLeft() 值是多少啊?我要用下。”

mDotGroup : “我也不知道啊,等会执行 onLayout() 方法的时候我才能拿到 getLeft() 值,现在还在 onCreate() 方法里,要不你再等等?”

我们:“额。。。行吧,反正我现在也不急着用 getLeft() 值,这样吧,等会用户滑动屏幕的时候,要回调 onPageScrolled() 方法,在这个方法里,我要用到小灰点之间的距离,小灰点之间的距离需要 getLeft() 值才能算出来,那个时候你的 onLayout() 方法应该执行完了吧?”

mDotGroup : “那时候 onLayout() 方法早执行完了,放心,我就在这盯着小灰点,它一绘制完,我就把 getLeft() 值告诉你,绝对能在 onPageScrolled() 方法回调的时候把 getLeft() 的值交到你手里。”

我们:“那好,你忙,我有事先走了”

mDotGroup:”慢走,不送~~“

呼~~ 打字好累。

先运行一下看下效果吧:

小红点移动

相邻小灰点之间的距离也被打印出来了:

相邻小灰点之间的距离

3.功能引导页只展示一次

功能引导页,总不能让用户每次打开你的 App 都要展示一次吧,把用户搞烦了,很可能会直接卸载了你的 App,当然,你要是不在意用户卸载你的 App,当我没说。

所以呢,我们就要在 ViewPager 的最后一个页面上添加一个按钮,用户点击这个按钮,进入 App 的主页面。这个按钮一旦被点击,我们就要记录下来,下次再进入 App ,在加载闪屏页的时候,我们先判断用户之前有没有点击过那个按钮,如果点击过,就直接进入主页面,没点击过,就展示功能引导页。

那么,怎么知道当前页面是不是 ViewPager 的最后一个页面呢?有的同学可能会说:这还不简单,手指使劲滑,滑到 ViewPager 滑不动为止,就是 ViewPager 最后一个页面。

如果真有这样的同学,我只能说:同学,你这么聪明,你的 App 知道么?

嗯,该怎么判断当前页面是不是 ViewPager 的最后一个页面呢,其实的确很简单,OnPageChangeListener 里有个回调方法 onPageSelected(),当选中某个页面的时候会回调这个方法,这个方法有个参数 position,表示当前页面的索引,position 默认从 0 开始。当用户从第一页滑动到第二页时,会回调这个方法,position 会从 0 变成 1。

我们先在布局文件中把按钮写出来,修改 activity_guide.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"
    tools:context="com.example.guidetest.GuideActivity">

    <android.support.v4.view.ViewPager
        android:id="@+id/vp_guide"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

    <Button
        android:id="@+id/btn_start"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="60dp"
        android:background="@drawable/btn_guide_selector"
        android:padding="5dp"
        android:text="进入主页面"
        android:textColor="@drawable/btn_guide_text_selector"
        android:visibility="invisible"/>

    <RelativeLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="20dp">

        <LinearLayout
            android:id="@+id/ll_dot_group"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="horizontal">

            <View
                android:layout_width="10dp"
                android:layout_height="10dp"
                android:background="@drawable/shape_dot_gray"/>

            <View
                android:layout_width="10dp"
                android:layout_height="10dp"
                android:layout_marginLeft="10dp"
                android:background="@drawable/shape_dot_gray"/>

            <View
                android:layout_width="10dp"
                android:layout_height="10dp"
                android:layout_marginLeft="10dp"
                android:background="@drawable/shape_dot_gray"/>
        </LinearLayout>

        <View
            android:id="@+id/view_red_dot"
            android:layout_width="10dp"
            android:layout_height="10dp"
            android:background="@drawable/shape_dot_red"/>
    </RelativeLayout>

</RelativeLayout>

按钮默认是隐藏的,到最后一页才显示出来。按钮设置了背景和文字颜色的选择器。

选择器的代码就不贴了,要是哪个同学不会写,下载源码吧。。。

然后修改 GuideActivity.java 中 GuidePageChangeListener 的 onPageSelected() 方法,并实例化按钮,代码如下:

package com.example.guidetest;

import android.content.Intent;
import android.os.Bundle;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;

import java.util.ArrayList;
import java.util.List;

public class GuideActivity extends AppCompatActivity {

    private static final String TAG = "GuideActivity";

    /**
     * 功能引导页展示的图片集合
     */
    private static int[] mImageIds = new int[]{R.drawable.ic_guide_1,
            R.drawable.ic_guide_2, R.drawable.ic_guide_3};

    /**
     * 功能引导页
     */
    private ViewPager mVpGuide;

    /**
     * 功能引导页展示的 ImageView 集合
     */
    private List<ImageView> mImageList;

    private Button mBtnStart;

    /**
     * 小灰点的父控件
     */
    private LinearLayout mDotGroup;

    /**
     * 小红点
     */
    private View mRedDot;

    /**
     * 相邻小灰点之间的距离
     */
    private int mDotDistance;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_guide);

        initView();
    }

    /**
     * 初始化页面
     */
    private void initView() {
        mVpGuide = (ViewPager) findViewById(R.id.vp_guide);
        mBtnStart = (Button) findViewById(R.id.btn_start);
        mDotGroup = (LinearLayout) findViewById(R.id.ll_dot_group);
        mRedDot = findViewById(R.id.view_red_dot);

        mImageList = new ArrayList<>();
        // 将要展示的 3 张图片存入 ImageView 集合中
        for (int i = 0; i < mImageIds.length; i++) {
            ImageView image = new ImageView(this);
            // 将图片设置给对应的 ImageView
            image.setBackgroundResource(mImageIds[i]);

            mImageList.add(image);
        }

        // 计算小灰点之间的距离
//        mDotDistance = mDotGroup.getChildAt(1).getLeft() - mDotGroup.getChildAt(0).getLeft();

        // 获取控件树,对 layout 结束事件进行监听
        mDotGroup.getViewTreeObserver().addOnGlobalLayoutListener(
                new ViewTreeObserver.OnGlobalLayoutListener() {
                    @Override
                    public void onGlobalLayout() {
                        // OnGlobalLayoutListener可能会被多次触发,
                        // 因此在得到了高度之后,要将 OnGlobalLayoutListener 注销掉
                        mDotGroup.getViewTreeObserver()
                                .removeGlobalOnLayoutListener(this);
                        // 计算小灰点之间的距离
                        mDotDistance = mDotGroup.getChildAt(1).getLeft()
                                - mDotGroup.getChildAt(0).getLeft();
                        Log.d(TAG, "小红距离:" + mDotDistance);
                    }
                });

        mVpGuide.setAdapter(new GuideAdapter());
        mVpGuide.setOnPageChangeListener(new GuidePageChangeListener());

        mBtnStart.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 按钮一旦被点击,更新 SharedPreferences
                PrefUtils.setBoolean(GuideActivity.this, PrefUtils.GUIDE_SHOWED, true);
                // 跳转到主页面
                startActivity(new Intent(GuideActivity.this, HomeActivity.class));
                finish();
            }
        });
    }

    /**
     * 适配器
     */
    class GuideAdapter extends PagerAdapter {

        @Override
        public int getCount() {
            return mImageList.size();
        }

        @Override
        public boolean isViewFromObject(View arg0, Object arg1) {
            return arg0 == arg1;
        }

        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            container.addView(mImageList.get(position));
            return mImageList.get(position);
        }

        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            container.removeView(mImageList.get(position));
        }
    }

    /**
     * 滑动监听
     */
    class GuidePageChangeListener implements ViewPager.OnPageChangeListener {

        @Override
        public void onPageScrollStateChanged(int arg0) {

        }

        // 页面滑动时回调此方法
        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
            // 页面滑动过程中,小红点移动的距离
            int distance = (int) (mDotDistance * (positionOffset + position));
            Log.d(TAG, "小红点移动的距离:" + distance);
            // 获取小红点的布局参数
            RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) mRedDot
                    .getLayoutParams();
            // 修改小红点的左边缘和父控件(RelativeLayout)左边缘的距离
            params.leftMargin = distance;
            // 修改小红点的布局参数
            mRedDot.setLayoutParams(params);
        }

        // 某个页面被选中时回调此方法
        @Override
        public void onPageSelected(int position) {
            // 如果是最后一个页面,按钮可见,否则不可见
            if (position == mImageIds.length - 1) {
                mBtnStart.setVisibility(View.VISIBLE);
            } else {
                mBtnStart.setVisibility(View.INVISIBLE);
            }
        }
    }
}

我们还要在闪屏页中判断按钮有没有被点击过,修改 SplashActivity 中的 jumpNextPage() 方法

/**
 * 跳转到下一个页面
 */
private void jumpNextPage() {
    // startActivity(new Intent(SplashActivity.this, GuideActivity.class));
    // finish();

    // 判断之前有没有展示过功能引导
    boolean guideShowed = PrefUtils.getBoolean(SplashActivity.this,
            PrefUtils.GUIDE_SHOWED, false);

    if (!guideShowed) {
        // 跳转到功能引导页
        startActivity(new Intent(SplashActivity.this, GuideActivity.class));
    } else {
        // 跳转到主页面
        startActivity(new Intent(SplashActivity.this, MainActivity.class));
    }

    finish();
}

我们再新建 HomeActivity 和 activity_home 作为主页面,activity_home 代码如下:

<?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:background="@drawable/ic_home"
    tools:context="com.example.guidetest.HomeActivity">

</RelativeLayout>

简单起见,我们在布局里只放了张背景图片。

运行一下:

图 1为 没有展示过功能引导页,图 2为 已经展示过功能引导页

图 1

没有展示过功能引导页

图 2

已经展示过功能引导页

源码下载

猜你喜欢

转载自blog.csdn.net/like_program/article/details/52961753