Android实现用户引导界面

在众多应用中,几乎每一款应用都有自己的Splash用户引导界面,该界面在用户首次启动展示,之后不会显示,主要向用户展示新功能.

分析

  • 主要使用ViewPager+Indicator实现
  • 主要是实现一个圆形指示器,这个圆形指示器继承LinearLayout,需要有一些属性可以自定义,比如指示器的颜色,大小,边距等
  • 这个指示器也可以自动滚动,比如应用在幻灯片展示的地方
  • 指示器是圆形的,需要我们自己绘制
  • 这个圆形指示器实现了ViewPager.OnPageChangeListener接口

实现

  • 定义自定义属性

    属性的意思见名字就可以知道了

<resources>

    <declare-styleable name="CircleIndicator">
        <attr name="circle_spacing" format="dimension"/>
        <attr name="circle_fill_color" format="color|reference"/>
        <attr name="circle_stroke_color" format="color|reference"/>
        <attr name="circle_radius" format="dimension"/>
        <attr name="circle_auto_scroll" format="boolean"/>
        <attr name="circle_scroll_delay_time" format="integer"/>
        <attr name="circle_scroll_animation" format="boolean"/>
    </declare-styleable>

</resources>
  • 定义自定义变量,从布局文件中解析进来,此外,如果布局文件没有使用,应该有一个默认的常量.

    定义默认常量

 private static final int DEFAULT_CIRCLE_SPACING = 5;
    private static final int DEFAULT_CIRCLE_COLOR=Color.WHITE;
    private static final int DEFAULT_CIRCLE_SIZE=3;
    private static final boolean DEFAULT_CIRCLE_AUTO_SCROLL=false;
    private static final int DEFAULT_CIRCLE_SCROLL_DELAY_TIME=3000;
    private static final boolean DEFAULT_CIRCLE_SCROLL_ANIMATION=true;

定义用于存储自定义属性的变量

    private int mSpacing;
    private int mSize;
    private int mFillColor;
    private int mStrokeColor;
    private boolean mAutoScroll;
    private int mDelayTime;
    private boolean mIsAnimation;

定义其他辅助变量,比如Canvas,Bitmap,Paint等,用于绘制圆形指示器

    private static final int CIRCLE_STROKE_WIDTH =1;
    private static final int BITMAP_PADDING =2;
 private ViewPager mViewPager;
    private int mCount;
    private int mLastIndex = 0;
    private Canvas mCanvas;
    private Paint mPaint;
    private Bitmap mSelectBitmap;
    private Bitmap mUnselectBitmap;

将自定义属性进行解析赋值给对应变量

private void initCustomParams(Context context, AttributeSet attrs) {
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CircleIndicator);
        try {
            mSpacing = typedArray.getDimensionPixelSize(R.styleable.CircleIndicator_circle_spacing, DEFAULT_CIRCLE_SPACING);
            mFillColor=typedArray.getColor(R.styleable.CircleIndicator_circle_fill_color,DEFAULT_CIRCLE_COLOR);
            mStrokeColor=typedArray.getColor(R.styleable.CircleIndicator_circle_stroke_color,DEFAULT_CIRCLE_COLOR);
            mSize= typedArray.getDimensionPixelSize(R.styleable.CircleIndicator_circle_radius, DEFAULT_CIRCLE_SIZE);
            mAutoScroll= typedArray.getBoolean(R.styleable.CircleIndicator_circle_auto_scroll, DEFAULT_CIRCLE_AUTO_SCROLL);
            mDelayTime=typedArray.getInt(R.styleable.CircleIndicator_circle_scroll_delay_time,DEFAULT_CIRCLE_SCROLL_DELAY_TIME);
            mIsAnimation=typedArray.getBoolean(R.styleable.CircleIndicator_circle_scroll_animation,DEFAULT_CIRCLE_SCROLL_ANIMATION);

        } finally {
            typedArray.recycle();
        }
    }

我们的指示器是自己绘制出来的,接下来绘制圆形指示器

    private void init() {
        setOrientation(HORIZONTAL);

        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setDither(true);
        mPaint.setStyle(Paint.Style.FILL_AND_STROKE);

        mPaint.setStrokeWidth(dip2px(CIRCLE_STROKE_WIDTH));
        mPaint.setColor(mFillColor);

        int size=dip2px(mSize+ BITMAP_PADDING + BITMAP_PADDING);
        int radius=dip2px(mSize / 2);
        int centerPoint=radius+ BITMAP_PADDING;

        mSelectBitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
        mUnselectBitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);

        mCanvas = new Canvas();
        mCanvas.setBitmap(mSelectBitmap);

        mCanvas.drawCircle(centerPoint, centerPoint, radius, mPaint);

        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setColor(mStrokeColor);
        mCanvas.setBitmap(mUnselectBitmap);
        mCanvas.drawCircle(centerPoint, centerPoint, radius, mPaint);

    }

实现构造方法,最终调用三个参数的构造方法,并调用相关函数进行初始化

 public CircleIndicator(Context context) {
        this(context, null);
    }

    public CircleIndicator(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public CircleIndicator(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initCustomParams(context, attrs);
        init();
    }

实现指示器相关逻辑

  • 首先需要初始化指示器的位置,应该是ViewPager的第一页,即初始化位置为0,调用initIndicator,并设置指示器的背景图为选中状态.记录上次指示器的位置即当前位置.
  • removeIndicator移出指示器只要移出当前类的所有子View即可
  • updateIndicator需要将上次的位置背景图设置为未选中,当前位置设置未选中,并记录上次位置为当前位置
  • addIndicator需要将圆形指示器的数目传入,其值为ViewPager的页数,并新建ImageView设置背景图为未选中的时候的图,并设置外边距,将其添加到当前类的子View中,如果设置了自动滚动,还需要进行自动滚动
  • setViewPager函数进行一些初始化操作
public void setViewPager(ViewPager viewPager) {
        mViewPager = viewPager;
        mViewPager.addOnPageChangeListener(this);
        if (mViewPager != null) {
            mCount = mViewPager.getAdapter().getCount();
            addIndicator(mCount);
        }

    }

    private void addIndicator(int count) {
        removeIndicator();
        if (count <= 0)
            return;
        for (int i = 0; i < count; i++) {
            ImageView imageView = new ImageView(getContext());
            LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
            params.leftMargin = mSpacing/2;
            params.rightMargin = mSpacing/2;

            imageView.setImageBitmap(mUnselectBitmap);
            addView(imageView, params);
        }
        initIndicator();
        if(mAutoScroll){
            sendScrollMessage(mDelayTime);
        }
    }

    private void initIndicator() {
        ((ImageView) getChildAt(0)).setImageBitmap(mSelectBitmap);
        mLastIndex=0;
    }

    private void removeIndicator() {
        removeAllViews();
    }

    private void updateIndicator(int position) {

        if (position != mLastIndex) {
            ((ImageView) getChildAt(mLastIndex)).setImageBitmap(mUnselectBitmap);
            ((ImageView) getChildAt(position)).setImageBitmap(mSelectBitmap);
        }
        mLastIndex = position;

    }

实现自动滚动,主要通过Handler进行延时实现

 private Handler mHandler=new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case SCROLL_WHAT:
                    scrollOnce();
                    sendScrollMessage(mDelayTime);
                    break;
            }
        }
    };
    public void scrollOnce() {
        PagerAdapter adapter = mViewPager.getAdapter();
        if (adapter == null) {
            return;
        }
        int nextIndex=mViewPager.getCurrentItem();
        ++nextIndex;
        if(nextIndex >=mCount){
            nextIndex =0;
        }
        updateIndicator(nextIndex);
        mViewPager.setCurrentItem(nextIndex, mIsAnimation);

    }

    private void sendScrollMessage(long delayTimeInMills) {
        mHandler.removeMessages(SCROLL_WHAT);
        mHandler.sendEmptyMessageDelayed(SCROLL_WHAT, delayTimeInMills);
    }

实现相关getter和setter函数

 private void setAutoScroll(boolean autoScroll){
        if (autoScroll){
            sendScrollMessage(mDelayTime);
        }else{
            mHandler.removeMessages(SCROLL_WHAT);
        }
        mAutoScroll=autoScroll;

    }
    public boolean isAutoScroll() {
        return mAutoScroll;
    }

    public int getDelayTime() {
        return mDelayTime;
    }

    public void setDelayTime(int delayTime) {
        mDelayTime = delayTime;
    }

    public boolean isAnimation() {
        return mIsAnimation;
    }

    public void setIsAnimation(boolean isAnimation) {
        mIsAnimation = isAnimation;
    }

实现接口相关函数

 @Override
    public void onPageScrolled(int i, float v, int i1) {

    }

    @Override
    public void onPageSelected(int position) {
        updateIndicator(position);
    }

    @Override
    public void onPageScrollStateChanged(int i) {

    }

以及一个单位转换的工具函数

private int dip2px(int dip) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip, getResources().getDisplayMetrics());
    }

使用

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:indicator="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"

    tools:context=".MainActivity">

    <android.support.v4.view.ViewPager
        android:id="@+id/viewpager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_alignParentTop="true"
        android:layout_alignParentBottom="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentRight="true"
        ></android.support.v4.view.ViewPager>

    <cn.edu.zafu.view.CircleIndicator
        android:id="@+id/circle_indicator"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center_horizontal"
        android:layout_alignParentBottom="true"
        android:layout_marginBottom="10dp"
        indicator:circle_spacing="5dp"
        indicator:circle_radius="3dp"
        indicator:circle_fill_color="#728bff"
        indicator:circle_stroke_color="#aaa"
        indicator:circle_auto_scroll="true"
        >

    </cn.edu.zafu.view.CircleIndicator>
</RelativeLayout>
 private void initView() {
        mViewPager= (ViewPager) findViewById(R.id.viewpager);
        mCircleIndicator= (CircleIndicator) findViewById(R.id.circle_indicator);
        mViewPager.setAdapter(new FragmentPagerAdapter(getSupportFragmentManager()) {

            private int[] resId={R.mipmap.ic_help_view_1,R.mipmap.ic_help_view_2,R.mipmap.ic_help_view_3,R.mipmap.ic_help_view_4};
            private Map<Integer,Fragment> mFragments=new HashMap<Integer,Fragment>();
            @Override
            public Fragment getItem(int i) {
                Fragment fragment=mFragments.get(i);
                if(fragment==null){
                    fragment=BlankFragment.newInstance(resId[i],i,resId.length);
                    mFragments.put(i,fragment);
                }
                return fragment;
            }

            @Override
            public int getCount() {
                return resId.length;
            }
        });
        mCircleIndicator.setViewPager(mViewPager);
    }
package cn.edu.zafu.splash;

import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.RelativeLayout;

public class BlankFragment extends Fragment {

    private static final String IMAGE_ID = "imageId";
    private static final String CUCRNT = "curcent";
    private static final String TOTAL = "total";
    private int mImageId;
    private int mCurcent;
    private int mTotal;

    public static BlankFragment newInstance(int imageId,int current,int total) {
        BlankFragment fragment = new BlankFragment();
        Bundle args = new Bundle();
        args.putInt(IMAGE_ID, imageId);
        args.putInt(CUCRNT, current);
        args.putInt(TOTAL, total);
        fragment.setArguments(args);
        return fragment;
    }

    public BlankFragment() {

    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (getArguments() != null) {
            mImageId = getArguments().getInt(IMAGE_ID);
            mCurcent = getArguments().getInt(CUCRNT);
            mTotal = getArguments().getInt(TOTAL);

        }
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {

        View view= inflater.inflate(R.layout.fragment_blank, container, false);
        return view;
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        getActivity().getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                WindowManager.LayoutParams.FLAG_FULLSCREEN);
        super.onViewCreated(view, savedInstanceState);
        ImageView imageView= (ImageView) view.findViewById(R.id.image);
        imageView.setImageResource(mImageId);
        if(mCurcent==mTotal-1){
            RelativeLayout relativeLayout= (RelativeLayout) view.findViewById(R.id.relativelayout);
            ImageButton button=new ImageButton(getActivity().getApplicationContext());
            button.setBackgroundResource(R.drawable.last_button_selector);

            RelativeLayout.LayoutParams params=new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT,RelativeLayout.LayoutParams.WRAP_CONTENT);

            params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
            params.addRule(RelativeLayout.CENTER_HORIZONTAL);
            params.bottomMargin=dip2px(80);
            relativeLayout.addView(button,params);
            button.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    int versionCode=Util.getAppVersionCode(getActivity());
                    Util.set(getActivity(),Util.FILE_NAME,versionCode+"",true);
                    Intent intent=new Intent(getActivity(),SecondActivity.class);
                    startActivity(intent);
                    getActivity().finish();
                }
            });
        }
    }
    private int dip2px(int dip) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip, getActivity().getResources().getDisplayMetrics());
    }

}

如果要实现是否首次启动,如果是才显示的话需要加一些逻辑判断,如果当前版本号已经持久化了,则直接跳过,这个数据是在Splash页面最后一个按钮点击事件里处理的

private boolean ignoreSplash() {
        if(Util.contatins(this, Util.FILE_NAME, Util.getAppVersionCode(this) + "")){
            Intent intent=new Intent(MainActivity.this,SecondActivity.class);
            startActivity(intent);
            this.finish();
            return true;
        }
        return false;
    }
public void onClick(View v) {
                    int versionCode=Util.getAppVersionCode(getActivity());
                    Util.set(getActivity(),Util.FILE_NAME,versionCode+"",true);
                    Intent intent=new Intent(getActivity(),SecondActivity.class);
                    startActivity(intent);
                    getActivity().finish();
                }

源码下载

时间: 2024-11-12 12:55:32

Android实现用户引导界面的相关文章

Android 自定义控件 GuideView 引导界面

最近看了hyman的博客:http://blog.csdn.net/lmj623565791/article/details/23692439 ,由于个人技术还比较薄弱,就在这个自定义控件的基础上做了个拓展,支持水平和垂直的 下面是主要代码; GuideView: import android.annotation.SuppressLint; import android.content.Context; import android.content.res.TypedArray; import

用户引导界面

在AppDelegate里的代码 视图控制器里的

android——利用SharedPreference做引导界面

很久以前就接触过sharedPreference这个android中的存储介质.但是一直没有实际使用过,今天在看之前做的“民用机型大全”的app时,突然想到可以使用sharedPreference类来改进这个app中的一个缺陷. 此前,我先介绍sharedPreference的使用.Android数据总共有四种存储的方式 一.SharePreference 二.SQLite 三.File 四.ContentProvider SharedPreference类是一个轻量级的存储类,特别适合保存软件

【Android UI设计与开发】3.引导界面(三)实现应用程序只启动一次引导界面

大部分的引导界面基本上都是千篇一律的,只要熟练掌握了一个,基本上也就没什么好说的了,要想实现应用程序只启动一次引导界面这样的效果,只要使用SharedPreferences类,就会让程序变的非常简单,下面来详细介绍一下这个类的使用方法 1.SharedPreferences的详细介绍和用法 其实在 20.游戏开发基础(游戏数据存储)中已经有过介绍了,为了文章的完整还是再介绍一遍. 做软件开发应该都知道,很多软件会有配置文件,里面存放这程序运行当中的各个属性值,由于其配置信息并不多,如果采用数据库

【Android UI设计与开发】第05期:引导界面(五)实现应用程序只启动一次引导界面

[Android UI设计与开发]第05期:引导界面(五)实现应用程序只启动一次引导界面 jingqing 发表于 2013-7-11 14:42:02 浏览(229501) 这篇文章算是对整个引导界面开发专题的一个终结了吧,个人觉得大部分的引导界面基本上都是千篇一律的,只要熟练掌握了一个,基本上也就没什么好说的了,要是在今后的开发中遇到了更好玩,更有趣的引导界面,博主也会在这里及时的跟大家分享,今天的内容主要是教大家的应用程序只有在第一次启动的时候显示引导界面,以后在启动程序的时候就不再显示了

Android用户引导页实现,圆点实现动画效果

当前市面上比较流行的应用,用户引导页已经是一个必不可缺的功能点了,简单的介绍下应用的基础功能或者版本升级功能点介绍. 那么常用的用户引导页是如何实现的呢?这篇文章就说说我个人用到的,并且觉得使用不错的效果. 1.首先定义布局文件guide_layout.xml文件,主要使用ViewPager做页面显示,使用一个圆点布局显示圆点,圆点根据图片数目动态添加. guide_layout.xml文件的代码如下: <?xml version="1.0" encoding="utf

十八、Android引导界面

一.所需素材 很有必要整理一下,里面附带友盟的社会化分享组件,我就不去掉了. 二.代码 import com.umeng.update.UmengUpdateAgent; import android.app.Activity; import android.content.Intent; import android.content.SharedPreferences; import android.os.Bundle; import android.os.Handler; import an

Android UI开发第四十三篇——使用Property Animation实现墨迹天气3.0引导界面及动画实现

前面写过<墨迹天气3.0引导界面及动画实现>,里面完美实现了动画效果,那一篇文章使用的View Animation,这一篇文章使用的Property Animation实现.Property Animation是Android3.0以后新增的动画库. 这篇文章的源码以及效果在github. 实现墨迹天气向上滑动的viewpager使用的开源库ViewPager-Android.ViewPager-Android开源库设置app:orientation定义滑动方向. 墨迹天气引导界面共有4个视图

android实现应用程序只有在第一次启动时显示引导界面

概述 SharedPreferences的使用非常简单,能够轻松的存放数据和读取数据.SharedPreferences只能保存简单类型的数据,例如,String.int等.一般会将复杂类型的数据转换成Base64编码,然后将转换后的数据以字符串的形式保存在 XML文件中,再用SharedPreferences保存. 使用SharedPreferences保存key-value对的步骤如下: (1)使用Activity类的getSharedPreferences方法获得SharedPrefere