在众多应用中,几乎每一款应用都有自己的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