前 言

在一个Android App启动的流程中,一般先是显示启动页,然后接着是引导页(初次安装或者清除应用缓存信息或者应用更新后第一次启动会经历该流程),在接着进入登录页面或者主页(如果应用之前在安装后或者清除缓存信息后有启动过,那么应用启动时就没有经历引导页的流程,直接由启动页进入登录页或者主页的流程)。那么,下面就来看看Android App是如何实现应用启动时引导页的功能的。

新建一个启动页LauncherActivity.java的Activity类

/*
 *  描述: 启动页
 */
public class LauncherActivity extends AppCompatActivity {

    /**
     * 1.延时2000ms
     * 2.判断程序是否第一次运行
     * 3.Activity全屏主题
     */

    //闪屏业延时
    private static final int HANDLER_SPLASH = 1001;
    //判断程序是否是第一次运行
    private static final String SHARE_IS_FIRST = "isFirst";


    private Handler handler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            switch (msg.what) {
                case HANDLER_SPLASH:
                    //判断程序是否是第一次运行
                    if (isFirst()) {
                        startActivity(new Intent(LauncherActivity.this, GuideActivity.class));
                    } else {
                        startActivity(new Intent(LauncherActivity.this, HomeActivity.class));
                    }
                    finish();
                    break;
                default:
                    break;
            }
            return false;
        }

    });


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

    }

    //初始化View
    private void initView() {
        //延时2000ms
        handler.sendEmptyMessageDelayed(HANDLER_SPLASH, 2000);
    }

    //判断程序是否第一次运行
    private boolean isFirst() {
        boolean isFirst = ShareUtils.getBoolean(this, SHARE_IS_FIRST, true);
        if (isFirst) {
            ShareUtils.putBoolean(this, SHARE_IS_FIRST, false);
            //是第一次运行
            return true;
        } else {
            return false;
        }

    }

    @Override
    public void onResume() {
        super.onResume();
    }

    @Override
    public void onPause() {
        super.onPause();
    }

}

该Activity类主要实现了应用启动页的逻辑功能,它是应用程序的入口,需要在Android的清单文件(AndroidManifest.xml)里设置为主程序的入口。该类主要实现了应用启动时模拟延时的操作,延时的操作开发者可以根据业务场景来实现相应的业务需求,比如延时5秒来显示开屏广告或者应用启动时初始化资源等操作。然后接着判断该应用是否是初次启动,如果是初次启动的话就接着由启动页跳转至引导页,否则就直接跳转至登录页或者主页。

判断应用是否是第一次启动的原理是这样的,开发者可以设置一个布尔值isFirst来进行判断,如果是第一次启动时可以根据isFirst=true来实现跳转至引导页,然后把isFirst设为false并使用SharedPreferences存储起来,当下次再次启动时可以根据isFirst=false来实现直接由启动页跳转至登录页或者主页的逻辑功能。

最后要把启动页设为全屏主题的样式,设置全屏主题可以在清单文件里在该Activity注册的地方单独设置,也可以直接该Activity的逻辑代码里设置,不过本人建议在清单文件里设置比较好,因为在逻辑代码里设置的话如果在清单文件引入的style的应用全局主题与全屏主题发生冲突时可能导致当应用执行到启动页的逻辑代码中那段全屏代码会出现应用闪退的现象。

新建一个引导页GuideActivity.java的Activity类

/*
 *  描述: 引导页
 */
public class GuideActivity extends AppCompatActivity implements View.OnClickListener {

    private ViewPager mViewPager;
    //容器
    private List<View> mList = new ArrayList<>();
    private View view1, view2, view3, view4;
    //小圆点
    private ImageView point1, point2, point3, point4;
    //跳过
    private Button btn_back;

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

        initView();
    }

    //初始化View
    private void initView() {

        point1 = (ImageView) findViewById(R.id.point1);
        point2 = (ImageView) findViewById(R.id.point2);
        point3 = (ImageView) findViewById(R.id.point3);
        point4 = (ImageView) findViewById(R.id.point4);

        btn_back = (Button) findViewById(R.id.btn_back);
        btn_back.setOnClickListener(this);
        //设置默认图片
        setPointImg(true, false, false, false);
        mViewPager = (ViewPager) findViewById(R.id.mViewPager);
        view1 = View.inflate(this, R.layout.pager_item_one, null);
        view2 = View.inflate(this, R.layout.pager_item_two, null);
        view3 = View.inflate(this, R.layout.pager_item_three, null);
        view4 = View.inflate(this, R.layout.pager_item_four, null);
        view4.findViewById(R.id.btn_start).setOnClickListener(this);

        mList.add(view1);
        mList.add(view2);
        mList.add(view3);
        mList.add(view4);

        //设置适配器
        mViewPager.setAdapter(new GuideAdapter());

        //监听ViewPager滑动
        mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

            }

            //pager切换
            @Override
            public void onPageSelected(int position) {
                switch (position) {
                    case 0:
                        setPointImg(true, false, false, false);
                        btn_back.setVisibility(View.VISIBLE);
                        break;
                    case 1:
                        setPointImg(false, true, false, false);
                        btn_back.setVisibility(View.VISIBLE);
                        break;
                    case 2:
                        setPointImg(false, false, true, false);
                        btn_back.setVisibility(View.VISIBLE);
                        break;
                    case 3:
                        setPointImg(false, false, false, true);
                        btn_back.setVisibility(View.GONE);
                        break;
                }
            }

            @Override
            public void onPageScrollStateChanged(int state) {

            }
        });

    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_start:
            case R.id.btn_back:
                startActivity(new Intent(this, HomeActivity.class));
                finish();
                break;
        }
    }

    class GuideAdapter extends PagerAdapter {

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

        @Override
        public boolean isViewFromObject(View view, Object object) {
            return view == object;
        }

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

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

    //设置小圆点的选中效果
    private void setPointImg(boolean isCheck1, boolean isCheck2, boolean isCheck3, boolean isCheck4) {
        if (isCheck1) {
            point1.setBackgroundResource(R.drawable.point_on);
        } else {
            point1.setBackgroundResource(R.drawable.point_off);
        }

        if (isCheck2) {
            point2.setBackgroundResource(R.drawable.point_on);
        } else {
            point2.setBackgroundResource(R.drawable.point_off);
        }

        if (isCheck3) {
            point3.setBackgroundResource(R.drawable.point_on);
        } else {
            point3.setBackgroundResource(R.drawable.point_off);
        }

        if (isCheck4) {
            point4.setBackgroundResource(R.drawable.point_on);
        } else {
            point4.setBackgroundResource(R.drawable.point_off);
        }
    }
}

该Activity类主要实现了应用引导页的逻辑功能,引导页功能的实现主要由Android中的ViewPager控件来实现的,ViewPager控件实现引导页的优势是ViewPager可以自动加载一下页和保留下一页的特性,使在滑动的过程中比较顺畅。可能这样解析ViewPager的特性大家不是很理解,举个例子,目前抖音App比较火,大家在玩Android版的抖音的过程中有没有发现,当你在刷抖音视频时手指上滑拉取下一视频,大家有没有觉得拉取视频很顺畅,而且视频能立即播放,当手指下滑拉取上一视频,效果也一样。其实这背后都是ViewPager控件的功劳,当用户启动到抖音视频页面时,ViewPager除了加载当前的视频界面外还自动加载下一个视频界面的操作,所以大家有没有感觉首次进入到视频页面加载视频时等待的时间很长,这其实是ViewPager同时加载当前视频和下一个视频的原因。当用户上滑拉取下一视频界面时,ViewPager不会立即销毁掉上一个视频界面,而是销毁上上个视频界面,下滑也是如此,ViewPager这样做的原因就是让用户感觉在滑动的过程中比较顺畅。

实现引导页的功能还需要自定义一个View容器,View容器主要的功能就是ViewPager加载时引用的视图界面内容,这些视图是以GuideActivity作为父视图层。父视图层中的主要实现ViewPager控件相应的滑动的逻辑功能,除此之外,还实现了ViewPager滑动界面时底部小红点指示器显示以及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/mViewPager"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_marginBottom="15dp"
        android:gravity="center"
        android:orientation="horizontal">

        <ImageView
            android:id="@+id/point1"
            android:layout_width="10dp"
            android:layout_height="10dp" />

        <ImageView
            android:id="@+id/point2"
            android:layout_width="10dp"
            android:layout_height="10dp"
            android:layout_marginLeft="10dp"
            android:layout_marginRight="10dp" />

        <ImageView
            android:id="@+id/point3"
            android:layout_width="10dp"
            android:layout_height="10dp"
            android:layout_marginEnd="10dp" />

        <ImageView
            android:id="@+id/point4"
            android:layout_width="10dp"
            android:layout_height="10dp" />
    </LinearLayout>

    <Button
        android:id="@+id/btn_back"
        android:layout_width="50dp"
        android:layout_height="30dp"
        android:layout_alignParentEnd="true"
        android:layout_marginTop="25dp"
        android:layout_marginEnd="25dp"
        android:background="@drawable/shape_jump"
        android:text="跳过"
        android:textColor="@android:color/white"
        android:textSize="14sp" />

</RelativeLayout>

apk安装包下载体验地址:

可以扫描以下二维码进行下载安装,或者点击以下链接 https://fir.im/guideviewtest 进行下载安装体验。
Android开发-Android应用中启动时引导页的实现-LMLPHP


Demo程序源码下载地址一(GitHub)
Demo程序源码下载地址二(码云)

05-04 16:15