您的位置:首页 > 其它

自定义View,包括事件分发,滑动冲突,测量以及布局

2016-04-12 16:28 429 查看
/**
* Created by Venn on 2016/4/11.
* 水平滑动的ScrollView,内部可嵌套竖直滑动的布局(ListView,ScrollView等)
*/
public class HorizontalScrollView extends ViewGroup {

private Context mContext;

private Scroller mScroller;
private VelocityTracker mTracker;
private int mLastX;
private int mLastY;
private int mInterceptedX;
private int mInterceptedY;

private int currentChildIndex;

public HorizontalScrollView(Context context) {
this(context, null);
}

public HorizontalScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
this.mContext = context;
init();
}

public void init() {
if (mScroller == null) {
mScroller = new Scroller(mContext);
}
mTracker = VelocityTracker.obtain();
}

//1 测量过程
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
measureChildren(widthMeasureSpec, heightMeasureSpec);

final int childCount = getChildCount();
//child 的getLayoutParams()得到的为parent里面的
ViewGroup.LayoutParams params = getLayoutParams();
int defaultWidth = params.width;
int childTotalWidth = 0;
int defaultHeight = params.height;

int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);

for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
if (child != null) {
LayoutParams marginParams = (LayoutParams) child.getLayoutParams();
int childHeight = child.getMeasuredHeight() + child.getPaddingTop() + child
.getPaddingBottom() + marginParams.leftMargin + marginParams.rightMargin
+ getPaddingTop() + getPaddingBottom();
defaultHeight = Math.max(defaultHeight, childHeight);

int childWidth = child.getMeasuredWidth() + child.getPaddingLeft() + marginParams
.leftMargin + marginParams.rightMargin + child
.getPaddingRight() + getPaddingLeft() + getPaddingRight();
childTotalWidth += childWidth;
}
}
defaultWidth = Math.max(defaultWidth, childTotalWidth);
if (widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST) {
setMeasuredDimension(defaultWidth, defaultHeight);
} else if (widthMode == MeasureSpec.AT_MOST) {
setMeasuredDimension(defaultWidth, heightSize);
} else if (heightMode == MeasureSpec.AT_MOST) {
setMeasuredDimension(widthSize, defaultHeight);
}
}

//2 布局过程
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
final int childCount = getChildCount();
int childLeft = getPaddingLeft();
int childTop = getPaddingTop();
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
if (child != null && child.getVisibility() != View.GONE) {
int childMeasuredWidth = child.getMeasuredWidth();
LayoutParams marginParams = (LayoutParams) child.getLayoutParams();
childLeft += marginParams.leftMargin;
child.layout(childLeft, childTop + marginParams.topMargin, childLeft +
childMeasuredWidth,
childTop + marginParams.topMargin + child.getMeasuredHeight());
childLeft += childMeasuredWidth + marginParams
.rightMargin;
}
}
}

//3 绘制过程
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
}

//4 事件分发过程
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
return super.dispatchTouchEvent(ev);
}

//5 事件拦截过程
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
boolean intercepted = false;
int x = (int) ev.getX();
int y = (int) ev.getY();
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
intercepted = false;
if (!mScroller.isFinished()) {
mScroller.abortAnimation();
intercepted = true;
}
break;
case MotionEvent.ACTION_MOVE:
if (Math.abs(x - mInterceptedX) > Math.abs(y - mInterceptedY)) {
intercepted = true;
} else {
intercepted = false;
}
break;
case MotionEvent.ACTION_UP:
intercepted = false;
break;
default:
break;
}

mLastX = x;
mLastY = y;
mInterceptedX = x;
mInterceptedY = y;

return intercepted;
}

//6 事件处理过程
@Override
public boolean onTouchEvent(MotionEvent event) {

int x = (int) event.getX();
int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (!mScroller.isFinished()) {
mScroller.abortAnimation();
}
break;
case MotionEvent.ACTION_MOVE:
int dX = x - mLastX;
int dY = y - mLastY;
scrollBy(-dX, 0);
currentChildIndex = dX / 30;
break;
case MotionEvent.ACTION_UP:
//使用弹性滑动,使得手指抬起后自动滑动到当前child
int scrollX = getScrollX();
mTracker.computeCurrentVelocity(1000);
int xVelocity = (int) mTracker.getXVelocity();
if (Math.abs(xVelocity) > 50) {
currentChildIndex = xVelocity > 0 ? currentChildIndex-- : currentChildIndex++;
}

smoothScrollBy(-(30 * currentChildIndex - scrollX), 0, 3000);
mTracker.clear();
break;
}

mLastX = x;
mLastY = y;
mTracker.clear();

return true;
}

//7 对View移除的处理
@Override
protected void onDetachedFromWindow() {
mTracker.recycle();
super.onDetachedFromWindow();
}

@Override
public void computeScroll() {
if (mScroller.computeScrollOffset()) {
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
postInvalidate();
}
}

//水平弹性滑动
private void smoothScrollBy(int dX, int dY, int duration) {
mScroller.startScroll(mScroller.getStartX(), 0, dX, 0, duration);
invalidate();
}

//重载使得margin有效
@Override
public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
return new HorizontalScrollView.LayoutParams(mContext, attrs);
}

//需要重写layoutParams类使得支持当前ViewGroup的LayoutParams
public static class LayoutParams extends MarginLayoutParams {
public LayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: