splash引导页是最常见不过的效果了,实现的方式也有很多,也比较容易实现,而对于自己来说通过了解不同的实现方式,比较其不同,同时也拓宽自己的思路,在不同的需求下选在自己觉得ok的实现方式。
Handler消息延迟方式
如果引导页的效果比较简单,就一张图片通过时间的延迟进入主程序,这样子通过Handler消息延迟就可以实现了;
public class SplashActivity extends AppCompatActivity {
//停留的时长
private static final long DELAY_TIME = 3000;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash);
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
//进入主程序页面
startActivity(new Intent(SplashActivity.this, MainActivity.class));
finish();
}
}, DELAY_TIME);
}
}
这样子就实现了,效果简单,就没有必要用复杂的方式实现了,如果想在右上角添加一个圆,然后动态显示时间,可以参考自定义view仿KeepApp Splash广告效果;
ViewPager+fragment的方式
接下来是ViewPager+fragment的方式实现,这也是开发中最常用的实现方式;
这里是将viewpager和底部的指示器封装在了一个自定义的布局容器中,然后通过自定义布局容器暴露的方法进行相对应的设置;
public class SplashLayout extends FrameLayout {
private ViewPager viewPager;
private LinearLayout linearLayout;
private int viewSize = 0;
//指示器距离底部的间距
private int pointBottomMargin;
//指示器大小
private int pointSize;
//指示器右边间距
private int pointRightMargin;
//是否显示底部指示器
private boolean showIndicator;
public SplashLayout(Context context) {
this(context, null);
}
public SplashLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SplashLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initAttrs(context, attrs);
initView();
}
private void initAttrs(Context context, AttributeSet attrs) {
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.SplashLayout);
pointBottomMargin = (int) array.getDimension(R.styleable.SplashLayout_bottomPointBottomMargin, dip2px(100));
pointSize = (int) array.getDimension(R.styleable.SplashLayout_pointSize, dip2px(10));
pointRightMargin = (int) array.getDimension(R.styleable.SplashLayout_pointRightMargin, dip2px(20));
showIndicator = array.getBoolean(R.styleable.SplashLayout_showIndicator, true);
array.recycle();
}
/**
* 初始化viewpager 和底部指示器
*/
@SuppressLint("ResourceType")
private void initView() {
//实例化viewpager
viewPager = new ViewPager(getContext());
//这里需要设置viewpager的id 不设置会报错
viewPager.setId(1);
FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
viewPager.setLayoutParams(lp);
//实例化底部指示器容器
linearLayout = new LinearLayout(getContext());
FrameLayout.LayoutParams indoctorLp = new FrameLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
linearLayout.setOrientation(LinearLayout.HORIZONTAL);
linearLayout.setBackgroundColor(ContextCompat.getColor(getContext(), android.R.color.transparent));
indoctorLp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
indoctorLp.bottomMargin = pointBottomMargin;
linearLayout.setLayoutParams(indoctorLp);
addView(viewPager);
addView(linearLayout);
initEvent();
}
private void initEvent() {
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float v, int i1) {
if (viewSize == 0) {
return;
}
for (int i = 0; i < viewSize; i++) {
View childAt = linearLayout.getChildAt(i);
if(childAt==null){
continue;
}
childAt.setBackgroundResource(i == position ? R.drawable.dot_select : R.drawable.dot_normal);
}
}
@Override
public void onPageSelected(int i) {
}
@Override
public void onPageScrollStateChanged(int i) {
}
});
}
/**
* @param
*/
public void showSplash(T adapter, int fragmentSize) {
this.viewSize = fragmentSize;
//给viewpager添加adapter
viewPager.setAdapter(adapter);
if (showIndicator) {
initIndicator();
}
}
/**
* 初始化指示器
*/
private void initIndicator() {
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(pointSize, pointSize);
for (int i = 0; i < viewSize; i++) {
View view = new View(getContext());
view.setBackgroundResource(i == 0 ? R.drawable.dot_select : R.drawable.dot_normal);
lp.rightMargin = pointRightMargin;
view.setId(i);
view.setLayoutParams(lp);
linearLayout.addView(view);
}
}
private int dip2px(int dip) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip, getResources().getDisplayMetrics());
}
}
这样子就没有必要在activity或者fragment写大量的逻辑,将代码隔离开来,在activity中使用时就只需要调用showSplash方法并传入相应的参数,同时可以通过下面这些自定义属性设置引导页指示器是否显示、指示器的大小等;具体的可以根据项目的需要进行调整和修改;
public class ViewPagerActivity extends AppCompatActivity {
private int[] bgRes = {R.mipmap.viewpager1, R.mipmap.viewpager2, R.mipmap.viewpager3, R.mipmap.viewpager4};
private ViewPagerAdapter adapter;
private List fragments;
private SplashLayout splashLayout;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_viewpager);
splashLayout = findViewById(R.id.splash_layout);
fragments = new ArrayList<>();
for (int i = 0; i < bgRes.length; i++) {
ContentFragment fragment;
if (i == (bgRes.length - 1)) {
fragment = ContentFragment.getInstance(bgRes[i], true);
} else {
fragment = ContentFragment.getInstance(bgRes[i], false);
}
fragments.add(fragment);
}
adapter = new ViewPagerAdapter(getSupportFragmentManager(), fragments);
splashLayout.showSplash(adapter, fragments.size());
}
}
activity里面的代码就比较少,也就比较整洁了,ImageView设置背景这里是加载的本地资源图片,如果加载网络图片,可以将那里的逻辑换成一些第三方图片加载库来实现;这样viewpager+fragment的方式就实现引导页效果了;
ViewFlipper方式
ViewFlipper是系统提供的一个切换控件,效果类似ViewPager,有不少日历效果就是采用ViewFlipper来实现的,这里也用ViewFlipper来实现下splash引导页的效果;
在xml布局文件中可以直接将splash显示的图片通过ImageView放在ViewFlipper中,当然也可以通过代码的方式创建,然后通过addView方法添加到对应的布局中;
不过就这样子的话还不能进行滑动,需要通过GestureDetector这个类来处理手势滑动,在activity中重写onTouchEvent方法,在用户按下或者触摸是,将手势处理交给GestureDetector类中的方法去处理;
@Override
public boolean onTouchEvent(MotionEvent event) {
return gestureDetector.onTouchEvent(event);
}
在用户触摸时就会回调onFling方法;
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
if (e1.getX() > e2.getX()) {
if (index >= childCount - 1) {
return false;
}
viewFlipper.showNext();
index = index + 1;
} else if (e1.getX() < e2.getX()) {
if (index <= 0) {
return false;
}
viewFlipper.showPrevious();
index = index - 1;
} else {
return false;
}
changeInicator();
return false;
}
通过showNext和showPrevious方法来切换下一页和上一页,然后去改变指示器的效果,这样效果就实现了;
完整代码:
public class ViewFillipperActivity extends AppCompatActivity implements GestureDetector.OnGestureListener {
private ViewFlipper viewFlipper;
private Button btn;
private LinearLayout linearLayout;
private GestureDetector gestureDetector;
private int index = 0;//当前是第几屏
private int childCount;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.acvitivty_viewfillipper);
viewFlipper = findViewById(R.id.viewflipper);
btn = findViewById(R.id.id_btn);
linearLayout = findViewById(R.id.id_indicator);
childCount = viewFlipper.getChildCount();
initIndicator();
gestureDetector = new GestureDetector(this);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(ViewFillipperActivity.this, MainActivity.class));
finish();
}
});
}
/**
* 初始化指示器
*/
private void initIndicator() {
int width = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10f, getResources().getDisplayMetrics());
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(width, width);
for (int i = 0; i < childCount; i++) {
View view = new View(this);
view.setBackgroundResource(i == 0 ? R.drawable.dot_select : R.drawable.dot_normal);
lp.rightMargin = 2 * width;
view.setId(i);
view.setLayoutParams(lp);
linearLayout.addView(view);
}
}
@Override
public boolean onDown(MotionEvent e) {
return false;
}
@Override
public void onShowPress(MotionEvent e) {
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
return false;
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
return false;
}
@Override
public void onLongPress(MotionEvent e) {
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
if (e1.getX() > e2.getX()) {
if (index >= childCount - 1) {
return false;
}
viewFlipper.showNext();
index = index + 1;
} else if (e1.getX() < e2.getX()) {
if (index <= 0) {
return false;
}
viewFlipper.showPrevious();
index = index - 1;
} else {
return false;
}
changeInicator();
return false;
}
private void changeInicator() {
for (int i = 0; i < childCount; i++) {
View childAt = linearLayout.getChildAt(i);
childAt.setBackgroundResource(i == index ? R.drawable.dot_select : R.drawable.dot_normal);
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
return gestureDetector.onTouchEvent(event);
}
}
不过ViewFlipper在切换的时候页面ViewPager的那种切换过渡动画效果,还有通过监听HorizontalScrollView的横向滚动也可以实现该效果,方式很多种,具体的可以根据项目的需要选择最适合的方式来实现。
下面就是一个splash视频的效果,splash的切换的还是采用上面写好的SplashLayout类,主要是处理fragment里面的视频播放逻辑;
public class VideoFragment extends Fragment implements MediaPlayer.OnPreparedListener, MediaPlayer.OnCompletionListener {
private static final String VIDEO_RES = "res";
private static final String SHOW_WELCOME = "show_welcome";
private FullScreenVideoView videoView;
private ImageView geNext;
//是否显示按钮
private boolean showWelcome;
public static VideoFragment getInstance(int resId, boolean showWelcome) {
VideoFragment fragment = new VideoFragment();
Bundle bundle = new Bundle();
bundle.putInt(VIDEO_RES, resId);
bundle.putBoolean(SHOW_WELCOME, showWelcome);
fragment.setArguments(bundle);
return fragment;
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_video_guide, container, false);
videoView = view.findViewById(R.id.videoview_guide);
int videoRes = getArguments().getInt(VIDEO_RES);
showWelcome = getArguments().getBoolean(SHOW_WELCOME);
geNext = view.findViewById(R.id.go_next);
geNext.setVisibility(showWelcome ? View.VISIBLE : View.INVISIBLE);
videoView.setVideoPath("android.resource://" + getActivity().getPackageName() + "/" + videoRes);
initEvent();
return view;
}
private void initEvent() {
videoView.setOnPreparedListener(this);
}
@Override
public void onPrepared(MediaPlayer mp) {
//准备播放回调
if (videoView != null) {
videoView.requestFocus();
videoView.seekTo(0);
videoView.start();
videoView.setOnCompletionListener(this);
}
}
@Override
public void onCompletion(MediaPlayer mp) {
//播放完成监听
videoView.requestFocus();
videoView.seekTo(0);
videoView.start();
}
@Override
public void onDestroy() {
super.onDestroy();
if (videoView != null) {
videoView.stopPlayback();
}
}
}
源码地址