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

    xiaoxiao2021-03-25  103

    概述

    启动屏动画界面。 第一次启动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中设置选择器 <Button android:id="@+id/btn_guide" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" android:layout_marginBottom="70dp" android:background="@drawable/btn_guide_selector" android:text="Welcome" android:paddingLeft="15dp" android:paddingRight="15dp" android:textColor="@color/guide_btn_textcolor_selector" android:textSize="17sp" android:visibility="gone" /> 2. @drawable/btn_guide_selector <item android:state_pressed="true" android:drawable="@drawable/guide_btn_pressed" /> <item android:drawable="@drawable/guide_btn_normal" /> 3. @color/guide_btn_textcolor_selector <item android:color="#FAFAFA" android:state_pressed="true" /> <item android:color="#584F60" />

    完整代码


    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

    <?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.eczom.eczomguide.GuideActivity"> <android.support.v4.view.ViewPager android:id="@+id/viewpager" android:layout_width="match_parent" android:layout_height="match_parent" /> <Button android:id="@+id/btn_guide" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" android:layout_marginBottom="70dp" android:background="@drawable/btn_guide_selector" android:text="Welcome" android:paddingLeft="15dp" android:paddingRight="15dp" android:textColor="@color/guide_btn_textcolor_selector" android:textSize="17sp" android:visibility="gone" /> <RelativeLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" android:layout_marginBottom="30dp"> <LinearLayout android:id="@+id/ll_point_group" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" /> <ImageView android:id="@+id/iv_point" android:background="@drawable/point_black" android:layout_width="15dp" android:layout_height="15dp" /> </RelativeLayout> </RelativeLayout>

    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<ImageView> 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); } } }

    最终效果

    转载请注明原文地址: https://ju.6miu.com/read-15819.html

    最新回复(0)