Android启动引导页及圆点指示器详解

概述

  • 启动屏动画界面。
  • 第一次启动App进入引导页,成功进入主界面后跳过引导,Sp存储。
  • 使用ViewPager实现引导页,通过算法实现圆点指示器的动态移动。
  • 滑动引导页至最后一页时,出现点击按钮,进入主界面。
  • 源码:https://github.com/tyyecec/EczomGuide


代码详解


1. 小黑点的动态移动。

1. 监听视图树改变状态,得到当前屏幕信息
ivPoint.getViewTreeObserver().addOnGlobalLayoutListener(new GuideOnGlobalLayoutListener());

2. 两白点的间距 = 第1个点距左屏边缘 - 第0个点距左屏边缘
distance = llPointGroup.getChildAt(1).getLeft() - llPointGroup.getChildAt(0).getLeft();

3. 监听ViewPager滑动,得到当前页面的位置及屏幕滑动的百分比
viewpager.addOnPageChangeListener(new GuideOnPageChangeListener());

4. 获取针对在父控件中的View参数,黑点
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) ivPoint.getLayoutParams();

5. 黑点滑动距离对应的坐标 = 引导页位置*两白点的间距 + 屏幕滑动百分比*两白点的间距
params.leftMargin = position * distance + (int) (positionOffset * distance);

6. 设置黑点移动距离
ivPoint.setLayoutParams(params);

2. 成功进入主界面后跳过引导。

1. 缓存Sp存储数据
putBoolean(Context context,String key,boolean value)
getBoolean(Context context,String key)

2. 设置动画监听,在动画播放结束时的回调方法中判断是否进过主界面
boolean isStartMain = CacheUtils.getBoolean(WelcomeActivity.this, ISMAIN);

3. 监听btn点击事件,保存'进入过引导页面'数据到Sp
CacheUtils.putBoolean(GuideActivity.this, WelcomeActivity.ISMAIN, true);

3. Button的颜色选择器。

1. xml中设置选择器


完整代码


1. Sp缓存 --- CacheUtils.java

package com.eczom.eczomguide;

import android.content.Context;
import android.content.SharedPreferences;

public class CacheUtils {

    public static void putBoolean(Context context,String key,boolean value){
        SharedPreferences sp = context.getSharedPreferences("eczom",Context.MODE_PRIVATE);
        sp.edit().putBoolean(key,value).commit();
    }

    public static boolean getBoolean(Context context,String key){
        SharedPreferences sp = context.getSharedPreferences("eczom",Context.MODE_PRIVATE);
        return sp.getBoolean(key,false);
    }
}

2. px和dp互换 --- DensityUtil.java

package com.eczom.eczomguide;

import android.content.Context;

public class DensityUtil {

    public static int dip2px(Context context, float dpValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }

    public static int px2dip(Context context, float pxValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (pxValue / scale + 0.5f);
    }
}

3. 启动动画 --- WelcomeActivity.java

package com.eczom.eczomguide;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.ScaleAnimation;
import android.widget.RelativeLayout;

import butterknife.BindView;
import butterknife.ButterKnife;

public class WelcomeActivity extends Activity {

    @BindView(R.id.activity_welcome)
    RelativeLayout activityWelcome;

    public static final String ISMAIN = "isMain";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_welcome);
        ButterKnife.bind(this);

        //渐变
        AlphaAnimation aa = new AlphaAnimation(0, 1);
        aa.setFillAfter(true);

        //缩放
        ScaleAnimation sa = new ScaleAnimation(0, 1, 0, 1, ScaleAnimation.RELATIVE_TO_SELF, 0.5f, ScaleAnimation.RELATIVE_TO_SELF, 0.5f);
        sa.setFillAfter(true);

        AnimationSet set = new AnimationSet(false);

        //添加动画,1500ms
        set.addAnimation(aa);
        set.addAnimation(sa);
        set.setDuration(1500);

        activityWelcome.startAnimation(set);

        set.setAnimationListener(new GuideAnimationListener());
    }

    private class GuideAnimationListener implements Animation.AnimationListener {
        //动画开始播放时回调
        @Override
        public void onAnimationStart(Animation animation) {

        }

        //动画播放结束时回调
        @Override
        public void onAnimationEnd(Animation animation) {
            //判断是否进过主界面
            boolean isStartMain = CacheUtils.getBoolean(WelcomeActivity.this, ISMAIN);
            Intent intent;
            if (isStartMain) {
                //进过---主界面
                intent = new Intent(WelcomeActivity.this, MainActivity.class);
            } else {
                //没进过---引导界面
                intent = new Intent(WelcomeActivity.this, GuideActivity.class);
            }

            startActivity(intent);
            finish();
        }

        //动画重复播放时回调
        @Override
        public void onAnimationRepeat(Animation animation) {

        }
    }
}

4. 引导页面 --- activity_guide.xml




    

    

5. 引导页面 --- GuideActivity.java

package com.eczom.eczomguide;

import android.app.Activity;
import android.content.Intent;
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.ViewTreeObserver;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;

import java.util.ArrayList;

import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;

public class GuideActivity extends Activity {

    @BindView(R.id.viewpager)
    ViewPager viewpager;
    @BindView(R.id.btn_guide)
    Button btnGuide;
    @BindView(R.id.ll_point_group)
    LinearLayout llPointGroup;
    @BindView(R.id.iv_point)
    ImageView ivPoint;

    private ArrayList imageViews;

    //两白点的间距
    private int distance;
    //白点的直径
    private int diameter;

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

        //准备数据源
        int[] ids = new int[]{
                R.drawable.welcom3,
                R.drawable.welcom2,
                R.drawable.welcom1
        };

        //使用封装工具类适配,把单位dip换成px
        diameter = DensityUtil.dip2px(this,15);

        imageViews = new ArrayList<>();
        for (int i = 0; i < ids.length; i++) {
            //设置背景
            ImageView imageView = new ImageView(this);
            imageView.setBackgroundResource(ids[i]);
            imageViews.add(imageView);

            //动态创建指示点,白点
            ImageView point = new ImageView(this);
            point.setBackgroundResource(R.drawable.point_white);
            //单位为像素,需适配
            LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(diameter, diameter);
            //除第一个点外,其他点均离左边的点diameter远
            if (i != 0) {
                params.leftMargin = diameter;
            }
            point.setLayoutParams(params);
            //把白点添加到线性布局
            llPointGroup.addView(point);
        }

        //设置viewpager适配器
        viewpager.setAdapter(new GuidePagerAdapter());

        //监听视图树改变状态,得到当前屏幕信息
        ivPoint.getViewTreeObserver().addOnGlobalLayoutListener(new GuideOnGlobalLayoutListener());

        //监听ViewPager滑动,得到当前页面的位置及屏幕滑动的百分比
        viewpager.addOnPageChangeListener(new GuideOnPageChangeListener());

    }

    @OnClick(R.id.btn_guide)
    public void onClick() {
        //保存'进入过引导页面'数据到Sp
        CacheUtils.putBoolean(GuideActivity.this, WelcomeActivity.ISMAIN, true);
        //跳转到MainActivity
        Intent intent = new Intent(GuideActivity.this, MainActivity.class);
        startActivity(intent);
        finish();
    }

    private class GuideOnGlobalLayoutListener implements ViewTreeObserver.OnGlobalLayoutListener {

        @Override
        public void onGlobalLayout() {
            //两白点的间距 = 第1个点距左屏边缘 - 第0个点距左屏边缘
            distance = llPointGroup.getChildAt(1).getLeft() - llPointGroup.getChildAt(0).getLeft();
            //会执行多次,得到数据后移除监听优化性能
            ivPoint.getViewTreeObserver().removeGlobalOnLayoutListener(this);
        }
    }

    private class GuideOnPageChangeListener implements ViewPager.OnPageChangeListener {

        /**
         * 当页面在滑动时回调
         *
         * @param position             当前页面的位置
         * @param positionOffset       当前页面滑动的百分比
         * @param positionOffsetPixels 当前页面滑动的像素
         */
        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
            //获取针对在父控件中的View参数,黑点
            RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) ivPoint.getLayoutParams();
            //黑点滑动距离对应的坐标 = 引导页位置*两白点的间距 + 屏幕滑动百分比*两白点的间距
            params.leftMargin = position * distance + (int) (positionOffset * distance);
            //设置黑点移动距离
            ivPoint.setLayoutParams(params);
        }

        /**
         * 当页面滑动结束后回调
         *
         * @param position 当前页面的位置
         */
        @Override
        public void onPageSelected(int position) {
            if (position == imageViews.size() - 1) {
                //滑到最后一个引导页时显示btn
                btnGuide.setVisibility(View.VISIBLE);
            } else {
                btnGuide.setVisibility(View.GONE);
            }
        }

        /**
         * 当页面状态发生改变时回调
         *
         * @param state 三种状态(0,1,2)==(什么都没做,正在滑动,滑动完毕)
         */
        @Override
        public void onPageScrollStateChanged(int state) {

        }
    }

    private class GuidePagerAdapter extends PagerAdapter {

        /**
         * @return 返回视图的总个数
         */
        @Override
        public int getCount() {
            return imageViews.size();
        }

        /**
         * 将指定position的视图添加到container中并返回
         *
         * @param container 容器ViewPager
         * @param position  即将实例化的视图位置
         * @return 返回该视图
         */
        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            ImageView imageView = imageViews.get(position);
            container.addView(imageView);
            return imageView;
        }

        /**
         * 判断view和object是否为同一个View
         *
         * @param view   当前视图
         * @param object instantiateItem()返回的结果值,即imageView
         * @return 判断结果
         */
        @Override
        public boolean isViewFromObject(View view, Object object) {
            return view == object;
        }

        /**
         * 将指定position的视图从container中移除
         *
         * @param container 容器ViewPager
         * @param position  即将销毁的视图位置
         * @param object    即将销毁的视图
         */
        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            container.removeView((View) object);
        }
    }
}


最终效果

你可能感兴趣的:(Android启动引导页及圆点指示器详解)