大家肯定都看到过,很多app我们进入的时候首先是一个引导页左右滑动的那种,一般都是ViewPager实现的,但是有时我们想实现竖着的引导页怎么办呢?以前自己也学习过看过这方面的知识,然后记在了笔记上,不过笔记也是写个大概,为了方便自己回顾,同时也方便大家学习,就写到博客上了(毕竟我说过,要将我电脑上所有的笔记都往博客上移~)。具体的实现效果就是如下图所示:
效果就是上面图片那个了,是不是很像竖着的ViewPager,我们先来想一下思路应该怎么实现吧。。。闭眼,想三分钟,如果是你,你会怎么做?
。
。
。
。
。
。
想好了么~对于新手来说,估计都是懵逼的,我当初刚毕业的时候对这个也是懵逼的~其实说到实现思路,对安卓开发有一定经验的人了应该都大概能想到的:
1.既然是多个图片,那么可能需要的是ViewGroup,因为我们有几个子ImageView,View下不能包含View,所以不能自定义VIew,只能自定义ViewGroup。
2.我们需要滑动图片,而且是手指滑动的同时图片也滑动,并且当我们手指抬起时,需要根据滑动的距离来决定我们是否需要切换到下一个图片。
那么具体实现过程我们接下来一起看看吧:
1.首先我们需要几个属性:
private int width,height; //屏幕的宽高,我们需要用于设置图片的大小为一个屏幕的大小
private int start,end; //从手指按下到滑动停止,整个过程的起点和终点
private int last; //手指最后一次的位置
private Scroller scroller; //View滑动的辅助类
2.在构造方法中初始化相关的数据:
scroller = new Scroller(context);
DisplayMetrics dm = new DisplayMetrics();
WindowManager wm = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
wm.getDefaultDisplay().getMetrics(dm);
width = dm.widthPixels;
height = dm.heightPixels;
我们在上述代码中进行了Scroller类的初始化和获取屏幕的宽高并赋值。
3.接下来需要在onMeasure中测量大小,遍历子view然后测量大小:
/*
* 重写测量子View的方法
* */
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int count = getChildCount();
for(int i = 0; i < count; i++){
View child = getChildAt(i);
measureChild(child,widthMeasureSpec,heightMeasureSpec); //测量子View
}
}
4.设置子view在父层中的位置:
/*
* 重写设定子View的位置的方法
* */
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int count = getChildCount();
MarginLayoutParams mpl = (MarginLayoutParams) getLayoutParams();
mpl.height = count * height; //设置ViewGroup的高度,即子View的高度乘以子View的个数,因为每个子View是充满屏幕的
setLayoutParams(mpl);
for(int i = 0; i < count; i++){
View child = getChildAt(i);
child.layout(l,i * height,r,((i + 1) * height)); //调用子View的layout方法将位置信息传递过去
}
}
5.设置好了子View的位置以及大小后,剩下的就是手势了,触摸事件onTouchEvent:
@Override
public boolean onTouchEvent(MotionEvent ev) {
int y = (int) ev.getY(); //手指触碰时在Y方向的位置
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN: //手指按下时
last = y; //手指按下时,最新位置是当前位置y
start = getScrollY();
break;
case MotionEvent.ACTION_MOVE: //手指滑动时
if(!scroller.isFinished()){ //如果停止了滚动停止了就终止动画
scroller.abortAnimation();
}
int yDistance = last - y; //(滑动差,即上次位置到此次位置,是相对于上次位置)滑动的距离是手指按下的位置 - 当前的位置
if(getScrollY() < 0){ //总滑动距离(相对于原点)如果小于0,那么滑动距离差就是0
yDistance = 0;
}
if(getScrollY() > getHeight() - height){ //如果总滑动距离超过能滑动的距离,那么滑动差就是0
yDistance = 0;
}
scrollBy(0,yDistance); //执行滑动(跟着手指滑动)
last = y;
break;
case MotionEvent.ACTION_UP: //手指抬起时
end = getScrollY();
int yScrollDistance = end - start;
if(yScrollDistance > 0){ //大于0说明手指在上滑,比如第一页滑向第二页,向下滚动
if(yScrollDistance < height/3){ //滑动距离小于屏幕1/3,复原
//复原需要滑动的距离就是手指按下到手指抬起这段距离
scroller.startScroll(0,getScrollY(),0, - yScrollDistance);
}else { //大于1/3,滑向下一页,计算思路很简单,滑动的距离相当于先复原再滑动一个屏幕的距离
scroller.startScroll(0,getScrollY(),0,-yScrollDistance + height);
}
}else {
if(-yScrollDistance < height/3){ //同理如上
scroller.startScroll(0,getScrollY(),0,-yScrollDistance);
}else {
scroller.startScroll(0,getScrollY(),0,-yScrollDistance - height);
}
}
break;
}
postInvalidate(); //刷新
return true;
}
6.似乎看着一切都搞定了,但是还差了一个,为了不重复影响scrollto和scrollby别忘了调用computeScroll:
@Override
public void computeScroll() {
super.computeScroll();
if(scroller.computeScrollOffset()){ //判断是否终止计算
scrollTo(0,scroller.getCurrY());
postInvalidate();
}
}
so~finish~