最近一周工作上比较闲,想来没有事做,,于是就想写个自定义控件巩固一下android的自定义控件开发,一开始的想法就是写一个简单的上下翻页的viewGroup,我们都知道viewpager是左右滑动的控件。
还是老规矩,先上效果图:
以上就是效果图,补充说一下实现的效果:实现子控件根据手势上下滑动翻页,慢速滑动过半时会翻页,快速滑动速度达到1000px/s时也会翻页,当然也要首页和尾页处理,首页和尾页部分不论向下或者向上翻动多少都会回弹;在事件分发上也有处理,当触摸或者滑动距离不会大于系统touchslop时就会当做点击事件来处理
到了上代码的时候了:
package com.xuganwen.testviewgroup2;
import android.content.Context;
import android.support.v4.view.ViewConfigurationCompat;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.widget.Scroller;
/**
* 文件描述:上下滑动viewgroup
* 作者:xuganwen
* 创建时间:2019/5/8
* 更改时间:2019/5/8
* 版本号:1.0
*/
public class VerticalViewPager extends ViewGroup {
private Scroller mScroller;
private int childCount;
private float downY;
private int touchSlop;
private int scrollY;
private int position;
private VelocityTracker velocityTracker;
public VerticalViewPager(Context context) {
super(context);
}
public VerticalViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
mScroller = new Scroller(context);
touchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(ViewConfiguration.get(context));
}
public VerticalViewPager(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
measureChild(getChildAt(i), widthMeasureSpec, heightMeasureSpec);
}
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
child.layout(l, child.getMeasuredHeight() * i, r, child.getMeasuredHeight() * (i + 1));
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if(null==velocityTracker){
velocityTracker=VelocityTracker.obtain();
}
velocityTracker.addMovement(event);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
downY = event.getY();
break;
case MotionEvent.ACTION_MOVE:
float moveY = event.getY();
int deltaY = (int) (moveY - downY);
scrollY = getScrollY();
position = getScrollY() % getMeasuredHeight() > getMeasuredHeight() / 2
? getScrollY() / getMeasuredHeight() + 1
: getScrollY() / getMeasuredHeight();
if (position >= getChildCount()-1) {
position = getChildCount()-1;
}else if(position<=0){
position=0;
}
scrollBy(0, -deltaY);
downY = moveY;
break;
case MotionEvent.ACTION_UP:
if(null!=velocityTracker){
velocityTracker.computeCurrentVelocity(1000);
}
if(Math.abs(velocityTracker.getYVelocity())>1000){
if(velocityTracker.getYVelocity()>1000){
mScroller.startScroll(0, getScrollY(), 0, (position==0?0:position-1) * getMeasuredHeight() - getScrollY());
}else if(velocityTracker.getYVelocity()<-1000){
mScroller.startScroll(0, getScrollY(), 0, (position==getChildCount()-1?getChildCount()-1:(position+1)) * getMeasuredHeight() - getScrollY());
}
}else {
mScroller.startScroll(0, getScrollY(), 0, position * getMeasuredHeight() - getScrollY());
}
invalidate();
release();
break;
case MotionEvent.ACTION_CANCEL:
release();
break;
}
return true;
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
downY = ev.getY();
break;
case MotionEvent.ACTION_MOVE:
if(Math.abs(ev.getY()-downY)>touchSlop){
return true;
}
break;
case MotionEvent.ACTION_UP:
break;
}
return false;
}
@Override
public void computeScroll() {
if (mScroller.computeScrollOffset()) {
//这里调用View的scrollTo() 完成实际的滚动
scrollTo(0, mScroller.getCurrY());
//必须调用该方法,否则不一定能看到滚动效果
invalidate();
}
super.computeScroll();
}
public void release(){
if(velocityTracker!=null){
velocityTracker.clear();
velocityTracker.recycle();
velocityTracker=null;
}
}
}
代码如上,可以看得出来滑动效果是通过scroller滑动计算的距离来实现的,滑动速度处理方面是 使用的VelocityTracker来处理的。子控件的点击事件是通过onInterceptouchevent来处理的。
说实话我想了一下,我这个控件应该是支持放入布局类控件作为子控件的,然后使用framgment替换布局来实现放入fragment来实现fragment上下翻页界面效果的,不知道各位有什么看法没,可以提出来讨论讨论~