Android实现用户引导界面

jopen 4年前

首先看效果图,盗了超级课程表几张图
这里写图片描述
在众多应用中,几乎每一款应用都有自己的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();                  }

源码下载

来自:http://blog.csdn.net/sbsujjbcy/article/details/46531351