カスタムView Groupラーニング(LinearLayoutのレイアウトで、スクロールとネストが可能)
61843 ワード
カスタムView Groupラーニング(LinearLayoutのレイアウトで、スクロールとネストが可能)
まず効果図を見て
ViewGroupをカスタマイズするには、onLayout()メソッドを書き換える必要がありますもちろん、onMeasure()の下が必要です.
まずonMeasureを測定するLinearLayoutのようなコンテナをカスタマイズするには、まず水平方向のコンテナのwidth=すべてのサブViewの幅と(padding,marginを考慮しない)コンテナのheight=すべてのViewの中で最も高い高さだけを見ます.
測定が終わったら、onLayoutに並べます
基本的なビューグループが完成しました
次にpaddingとmargin、gravity、orientationプロパティを追加します.
まずattrsにorientationとgravityの2つのカスタム属性を追加します
child.getLayoutParams()を直接強くMarginLayoutParamsに変換するとエラーが報告されます.書き換える必要があります.または、MarginLayoutParamsを継承し、generateLayoutParamsメソッドを書き換える必要があります.
onLayoutで方向を区別しgravity重心を区別しpaddingとmargin値を追加してviewを配置
以上、orientationとgravityプロパティを持ち、padding、サブviewのpaddingとmarginを考慮したコンテナを完了しました.
次にスクロールできるようにするonTouchEnventを書き換えるだけでACTION_MOVE中scrollBy(-offsetX,0);スクロールできます
次は完全なコードです
まず効果図を見て
ViewGroupをカスタマイズするには、onLayout()メソッドを書き換える必要がありますもちろん、onMeasure()の下が必要です.
public class MyViewGroup extends ViewGroup {
public MyViewGroup(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
//
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//
}
まずonMeasureを測定するLinearLayoutのようなコンテナをカスタマイズするには、まず水平方向のコンテナのwidth=すべてのサブViewの幅と(padding,marginを考慮しない)コンテナのheight=すべてのViewの中で最も高い高さだけを見ます.
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
childWidth = 0;//
childHeight = 0;
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
if (child.getVisibility() != View.GONE) {
measureChild(child, widthMeasureSpec,
heightMeasureSpec);
childWidth += child.getMeasuredWidth();
childHeight = Math.max(childHeight, child.getMeasuredHeight());
}
}
setMeasuredDimension(resolveSize(childWidth, widthMeasureSpec), resolveSize(childHeight, heightMeasureSpec));
}
測定が終わったら、onLayoutに並べます
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int childLeft = 0;
int childTop = 0;
final int height = b - t;
final int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
if (child.getVisibility() != View.GONE) {
final int childWidth = child.getMeasuredWidth();
final int childHeight = child.getMeasuredHeight();
child.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);
childLeft += childWidth;
childTop = 0;
}
}
}
基本的なビューグループが完成しました
次にpaddingとmargin、gravity、orientationプロパティを追加します.
まずattrsにorientationとgravityの2つのカスタム属性を追加します
<declare-styleable name="MyViewGroup">
<attr name="orientation">
<enum name="horizontal" value="0" />
<enum name="vertical" value="1" />
attr>
<attr name="gravity">
<flag name="top" value="0x30" />
<flag name="bottom" value="0x50" />
<flag name="left" value="0x03" />
<flag name="right" value="0x05" />
<flag name="center_vertical" value="0x10" />
<flag name="fill_vertical" value="0x70" />
<flag name="center_horizontal" value="0x01" />
<flag name="fill_horizontal" value="0x07" />
<flag name="center" value="0x11" />
<flag name="fill" value="0x77" />
<flag name="clip_vertical" value="0x80" />
<flag name="clip_horizontal" value="0x08" />
<flag name="start" value="0x00800003" />
<flag name="end" value="0x00800005" />
attr>
declare-styleable>
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mPaddingTop = getPaddingTop();
mPaddingLeft = getPaddingLeft();
mPaddingRight = getPaddingRight();
mPaddingBottom = getPaddingBottom();
childWidth = 0;// child
childHeight = 0;
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
if (child.getVisibility() != View.GONE) {
// child margin ( MarginLayoutParams , , MarginLayoutParams, generateLayoutParams )
LayoutParams lp = (LayoutParams) child.getLayoutParams();
//▲ 1: measureChild measureChildWithMargin
measureChildWithMargins(child, widthMeasureSpec, 0,
heightMeasureSpec, 0);
/* : measureChild(child, widthMeasureSpec,
heightMeasureSpec);*/
if (mOrientation == HORIZONTAL) {
childWidth += child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
childHeight = Math.max(childHeight, child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
} else {
childWidth = Math.max(childWidth, child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
childHeight += child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;
}
}
}
childWidth += mPaddingLeft + mPaddingRight;
childHeight += mPaddingTop + mPaddingBottom;
setMeasuredDimension(resolveSize(childWidth, widthMeasureSpec), resolveSize(childHeight, heightMeasureSpec));
}
child.getLayoutParams()を直接強くMarginLayoutParamsに変換するとエラーが報告されます.書き換える必要があります.または、MarginLayoutParamsを継承し、generateLayoutParamsメソッドを書き換える必要があります.
@Override
protected android.view.ViewGroup.LayoutParams generateDefaultLayoutParams() {
return new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT);
}
@Override
public android.view.ViewGroup.LayoutParams generateLayoutParams(
AttributeSet attrs) {
return new LayoutParams(getContext(), attrs);
}
@Override
protected android.view.ViewGroup.LayoutParams generateLayoutParams(
android.view.ViewGroup.LayoutParams p) {
return new LayoutParams(p);
}
public static class LayoutParams extends MarginLayoutParams {
public int gravity = -1;
public LayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
TypedArray ta = c.obtainStyledAttributes(attrs,
R.styleable.MyViewGroup);
gravity = ta.getInt(R.styleable.MyViewGroup_gravity, -1);
ta.recycle();
}
public LayoutParams(int width, int height) {
this(width, height, -1);
}
public LayoutParams(int width, int height, int gravity) {
super(width, height);
this.gravity = gravity;
}
public LayoutParams(android.view.ViewGroup.LayoutParams source) {
super(source);
}
public LayoutParams(MarginLayoutParams source) {
super(source);
}
}
onLayoutで方向を区別しgravity重心を区別しpaddingとmargin値を追加してviewを配置
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
if (mOrientation == VERTICAL) {
layoutVertical(l, t, r, b);
} else {
layoutHorizontal(l, t, r, b);
}
// getParent()
if (getParent() instanceof MyViewGroup) {
sameDirection = ((MyViewGroup) getParent()).getOrientation() == getOrientation();
}
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
void layoutHorizontal(int l, int t, int r, int b) {
int childLeft = mPaddingLeft;
int childTop = mPaddingTop;
final int height = b - t;
int childBottom = height - mPaddingBottom;
int childSpace = height - mPaddingTop - mPaddingBottom;
final int childCount = getChildCount();
final int majorGravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
final int minorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
final int layoutDirection = getLayoutDirection();
switch (Gravity.getAbsoluteGravity(majorGravity, layoutDirection)) {
case Gravity.RIGHT:
childLeft = mPaddingLeft + r - l - childWidth;
break;
case Gravity.CENTER_HORIZONTAL:
childLeft = mPaddingLeft + (r - l - childWidth) / 2;
break;
case Gravity.LEFT:
default:
childLeft = mPaddingLeft;
break;
}
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
if (child.getVisibility() != View.GONE) {
LayoutParams params = (LayoutParams) child.getLayoutParams();
final int childWidth = child.getMeasuredWidth();
final int childHeight = child.getMeasuredHeight();
int gravity = params.gravity;
if (gravity < 0) {
gravity = minorGravity;
}
switch (gravity & Gravity.VERTICAL_GRAVITY_MASK) {
case Gravity.TOP:
childTop = mPaddingTop + params.topMargin;
break;
case Gravity.CENTER_VERTICAL:
childTop = mPaddingTop + ((childSpace - childHeight) / 2)
+ params.topMargin - params.bottomMargin;
break;
case Gravity.BOTTOM:
childTop = childBottom - childHeight - params.bottomMargin;
break;
default:
childTop = mPaddingTop;
break;
}
childLeft += params.leftMargin;
childTop += params.topMargin;
child.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);
childLeft += childWidth + params.rightMargin;
childTop = mPaddingTop;
}
}
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
void layoutVertical(int l, int t, int r, int b) {
int childLeft = mPaddingLeft;
int childTop = mPaddingTop;
final int width = r - l;
int childRight = width - mPaddingRight;
int childSpace = width - mPaddingLeft - mPaddingRight;
final int childCount = getChildCount();
final int majorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
final int minorGravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
switch (majorGravity) {
case Gravity.BOTTOM:
childTop = mPaddingTop + b - t - childHeight;
break;
case Gravity.CENTER_VERTICAL:
childTop = mPaddingTop + (b - t - childHeight) / 2;
break;
case Gravity.TOP:
default:
childTop = mPaddingTop;
break;
}
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
if (child == null) {
childTop += 0;
} else if (child.getVisibility() != GONE) {
LayoutParams params = (LayoutParams) child.getLayoutParams();
final int childWidth = child.getMeasuredWidth();
final int childHeight = child.getMeasuredHeight();
int gravity = params.gravity;
if (gravity < 0) {
gravity = minorGravity;
}
final int layoutDirection = getLayoutDirection();
final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
case Gravity.CENTER_HORIZONTAL:
childLeft = mPaddingLeft + ((childSpace - childWidth) / 2)
+ params.leftMargin - params.rightMargin;
break;
case Gravity.RIGHT:
childLeft = childRight - childWidth - params.rightMargin;
break;
case Gravity.LEFT:
default:
childLeft = mPaddingLeft + params.leftMargin;
break;
}
childLeft += params.leftMargin;
childTop += params.topMargin;
child.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);
childLeft = mPaddingTop;
childTop += childHeight + params.bottomMargin;
}
}
}
以上、orientationとgravityプロパティを持ち、padding、サブviewのpaddingとmarginを考慮したコンテナを完了しました.
次にスクロールできるようにするonTouchEnventを書き換えるだけでACTION_MOVE中scrollBy(-offsetX,0);スクロールできます
次は完全なコードです
package ai.houzi.xiao.widget;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.TypedArray;
import android.os.Build;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.widget.Scroller;
import com.juxin.common.utils.Logg;
import ai.houzi.xiao.R;
/**
* (LinearLayout), padding,margin, , gravity
*/
public class MyViewGroup extends ViewGroup {
private int mOrientation;
private int mGravity;
private int mPaddingTop;
private int mPaddingLeft;
private int mPaddingRight;
private int mPaddingBottom;
private int childWidth;
private int childHeight;
public static final int HORIZONTAL = 0;
public static final int VERTICAL = 1;
private static final int[] ORIENTATION_FLAGS = {
HORIZONTAL, VERTICAL
};
private Scroller mScroller;
private int mTouchSlop;
private boolean sameDirection;// ( )
private float downX, downY, moveX, moveY, lastX, lastY;
private boolean isFirst;
private VelocityTracker mVelocityTracker;
private int mPointerId;
private int mMaxVelocity;//
public MyViewGroup(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MyViewGroup);
mOrientation = ORIENTATION_FLAGS[a.getInt(R.styleable.MyViewGroup_orientation, 0)];
mGravity = a.getInt(R.styleable.MyViewGroup_gravity, Gravity.START | Gravity.TOP);
a.recycle();
mMaxVelocity = ViewConfiguration.get(context).getScaledMaximumFlingVelocity();
mScroller = new Scroller(context);
mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
if (mOrientation == VERTICAL) {
layoutVertical(l, t, r, b);
} else {
layoutHorizontal(l, t, r, b);
}
// getParent()
if (getParent() instanceof MyViewGroup) {
sameDirection = ((MyViewGroup) getParent()).getOrientation() == getOrientation();
}
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
void layoutHorizontal(int l, int t, int r, int b) {
int childLeft = mPaddingLeft;
int childTop = mPaddingTop;
final int height = b - t;
int childBottom = height - mPaddingBottom;
int childSpace = height - mPaddingTop - mPaddingBottom;
final int childCount = getChildCount();
final int majorGravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
final int minorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
final int layoutDirection = getLayoutDirection();
switch (Gravity.getAbsoluteGravity(majorGravity, layoutDirection)) {
case Gravity.RIGHT:
childLeft = mPaddingLeft + r - l - childWidth;
break;
case Gravity.CENTER_HORIZONTAL:
childLeft = mPaddingLeft + (r - l - childWidth) / 2;
break;
case Gravity.LEFT:
default:
childLeft = mPaddingLeft;
break;
}
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
if (child.getVisibility() != View.GONE) {
LayoutParams params = (LayoutParams) child.getLayoutParams();
final int childWidth = child.getMeasuredWidth();
final int childHeight = child.getMeasuredHeight();
int gravity = params.gravity;
if (gravity < 0) {
gravity = minorGravity;
}
switch (gravity & Gravity.VERTICAL_GRAVITY_MASK) {
case Gravity.TOP:
childTop = mPaddingTop + params.topMargin;
break;
case Gravity.CENTER_VERTICAL:
childTop = mPaddingTop + ((childSpace - childHeight) / 2)
+ params.topMargin - params.bottomMargin;
break;
case Gravity.BOTTOM:
childTop = childBottom - childHeight - params.bottomMargin;
break;
default:
childTop = mPaddingTop;
break;
}
childLeft += params.leftMargin;
childTop += params.topMargin;
child.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);
childLeft += childWidth + params.rightMargin;
childTop = mPaddingTop;
}
}
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
void layoutVertical(int l, int t, int r, int b) {
int childLeft = mPaddingLeft;
int childTop = mPaddingTop;
final int width = r - l;
int childRight = width - mPaddingRight;
int childSpace = width - mPaddingLeft - mPaddingRight;
final int childCount = getChildCount();
final int majorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
final int minorGravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
switch (majorGravity) {
case Gravity.BOTTOM:
childTop = mPaddingTop + b - t - childHeight;
break;
case Gravity.CENTER_VERTICAL:
childTop = mPaddingTop + (b - t - childHeight) / 2;
break;
case Gravity.TOP:
default:
childTop = mPaddingTop;
break;
}
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
if (child == null) {
childTop += 0;
} else if (child.getVisibility() != GONE) {
LayoutParams params = (LayoutParams) child.getLayoutParams();
final int childWidth = child.getMeasuredWidth();
final int childHeight = child.getMeasuredHeight();
int gravity = params.gravity;
if (gravity < 0) {
gravity = minorGravity;
}
final int layoutDirection = getLayoutDirection();
final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
case Gravity.CENTER_HORIZONTAL:
childLeft = mPaddingLeft + ((childSpace - childWidth) / 2)
+ params.leftMargin - params.rightMargin;
break;
case Gravity.RIGHT:
childLeft = childRight - childWidth - params.rightMargin;
break;
case Gravity.LEFT:
default:
childLeft = mPaddingLeft + params.leftMargin;
break;
}
childLeft += params.leftMargin;
childTop += params.topMargin;
child.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);
childLeft = mPaddingTop;
childTop += childHeight + params.bottomMargin;
}
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mPaddingTop = getPaddingTop();
mPaddingLeft = getPaddingLeft();
mPaddingRight = getPaddingRight();
mPaddingBottom = getPaddingBottom();
childWidth = 0;// child
childHeight = 0;
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
if (child.getVisibility() != View.GONE) {
// child margin
LayoutParams lp = (LayoutParams) child.getLayoutParams();
//▲ 1: measureChild measureChildWithMargin
measureChildWithMargins(child, widthMeasureSpec, 0,
heightMeasureSpec, 0);
/* : measureChild(child, widthMeasureSpec,
heightMeasureSpec);*/
if (mOrientation == HORIZONTAL) {
childWidth += child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
childHeight = Math.max(childHeight, child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
} else {
childWidth = Math.max(childWidth, child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
childHeight += child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;
}
}
}
childWidth += mPaddingLeft + mPaddingRight;
childHeight += mPaddingTop + mPaddingBottom;
setMeasuredDimension(resolveSize(childWidth, widthMeasureSpec), resolveSize(childHeight, heightMeasureSpec));
}
@Override
protected android.view.ViewGroup.LayoutParams generateDefaultLayoutParams() {
return new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT);
}
@Override
public android.view.ViewGroup.LayoutParams generateLayoutParams(
AttributeSet attrs) {
return new LayoutParams(getContext(), attrs);
}
@Override
protected android.view.ViewGroup.LayoutParams generateLayoutParams(
android.view.ViewGroup.LayoutParams p) {
return new LayoutParams(p);
}
public static class LayoutParams extends MarginLayoutParams {
public int gravity = -1;
public LayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
TypedArray ta = c.obtainStyledAttributes(attrs,
R.styleable.MyViewGroup);
gravity = ta.getInt(R.styleable.MyViewGroup_gravity, -1);
ta.recycle();
}
public LayoutParams(int width, int height) {
this(width, height, -1);
}
public LayoutParams(int width, int height, int gravity) {
super(width, height);
this.gravity = gravity;
}
public LayoutParams(android.view.ViewGroup.LayoutParams source) {
super(source);
}
public LayoutParams(MarginLayoutParams source) {
super(source);
}
}
public int getOrientation() {
return mOrientation;
}
public void setOrientation(int orientation) {
this.mOrientation = orientation;
if (getParent() instanceof MyViewGroup) {
sameDirection = ((MyViewGroup) getParent()).getOrientation() == orientation;
}
requestLayout();
}
//------------------------ ------------------------------------------------------
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
int action = ev.getAction();
if (action == MotionEvent.ACTION_DOWN) {
downX = lastX = ev.getX();
downY = lastY = ev.getY();
} else if (action == MotionEvent.ACTION_MOVE) {
// move
return true;
}
return false;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
obtainVelocityTracker(event);
int action = event.getAction();
float x = event.getX();
float y = event.getY();
if (action == MotionEvent.ACTION_DOWN) {
if (!mScroller.isFinished()) {
mScroller.abortAnimation();
}
mPointerId = event.getPointerId(0);
lastX = x;
lastY = y;
getParent().requestDisallowInterceptTouchEvent(true);
} else if (action == MotionEvent.ACTION_MOVE) {
if (isFirst) {
lastX = x;
lastY = y;
isFirst = false;
}
if (mOrientation == HORIZONTAL) {
touchMoveHorizontal(event);
} else {
touchMoveVertical(event);
}
if (scrollChangeListener != null) {//
scrollChangeListener.onScrollChange(getScrollX(), getScrollY());
}
mScrollState = SCROLLING;
} else if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
// 1000ms
mVelocityTracker.computeCurrentVelocity(1000, mMaxVelocity);
// x,y mPointerId
final float velocityX = mVelocityTracker.getXVelocity(mPointerId);
final float velocityY = mVelocityTracker.getYVelocity(mPointerId);
if (mOrientation == HORIZONTAL) {
if (getScrollX() < 0) {// ,
mScroller.startScroll(getScrollX(), 0, -getScrollX(), 0, 300);
} else if (getScrollX() + getWidth() > childWidth) {//
mScroller.startScroll(getScrollX(), 0, (int) -(getScrollX() + getWidth() - childWidth), 0, 300);
} else {// , ,
mScroller.fling(getScrollX(), 0, (int) -velocityX, 0, 0, childWidth - getWidth() + 100, 0, 0);
}
} else {
if (getScrollY() < 0) {
mScroller.startScroll(0, getScrollY(), 0, -getScrollY(), 300);
} else if (getScrollY() + getHeight() > childHeight) {
mScroller.startScroll(0, getScrollY(), 0, (int) -(getScrollY() + getHeight() - childHeight), 300);
} else {
mScroller.fling(0, getScrollY(), 0, (int) -velocityY, 0, 0, 0, childHeight - getHeight() + 100);
}
}
isFirst = true;
recycleVelocityTracker();
postInvalidate();// computeScroll ,
}
return true;
}
/**
*
*/
private void touchMoveHorizontal(MotionEvent event) {
float x = event.getX();
int offsetX = 0;
moveX = x;
// ,
if (getScrollX() < 0 || getScrollX() + getWidth() > childWidth) {
offsetX = (int) ((moveX - lastX) / 2.5);
} else {
offsetX = (int) (moveX - lastX);
}
if (!sameDirection) {
//
if (Math.abs(x - downX) + mTouchSlop > Math.abs(event.getY() - downY) && childWidth > getWidth()) {
getParent().requestDisallowInterceptTouchEvent(true);
} else {
getParent().requestDisallowInterceptTouchEvent(false);
}
} else {
//
if (lastX >= downX) {
if (getScrollX() <= 0) {
getParent().requestDisallowInterceptTouchEvent(false);
} else {
getParent().requestDisallowInterceptTouchEvent(true);
}
} else {
if (getScrollX() >= childWidth - getWidth()) {
getParent().requestDisallowInterceptTouchEvent(false);
} else {
getParent().requestDisallowInterceptTouchEvent(true);
}
}
}
if (Math.abs(downX - x) > mTouchSlop) {
scrollBy(-offsetX, 0);
}
lastX = moveX;
}
/**
*
*/
private void touchMoveVertical(MotionEvent event) {
float y = event.getY();
int offsetY = 0;
moveY = y;
// ,
if (getScrollY() < 0 || getScrollY() + getHeight() > childHeight) {
offsetY = (int) ((moveY - lastY) / 2.5);
} else {
offsetY = (int) (moveY - lastY);
}
Logg.e(sameDirection);
if (!sameDirection) {
//
if (Math.abs(event.getX() - downX) < Math.abs(y - downY) + mTouchSlop && childHeight > getHeight()) {
getParent().requestDisallowInterceptTouchEvent(true);
} else {
getParent().requestDisallowInterceptTouchEvent(false);
}
} else {
//
if (lastY >= downY) {
if (getScrollY() <= 0) {
getParent().requestDisallowInterceptTouchEvent(false);
} else {
getParent().requestDisallowInterceptTouchEvent(true);
}
} else {
if (getScrollY() >= childHeight - getHeight()) {
getParent().requestDisallowInterceptTouchEvent(false);
} else {
getParent().requestDisallowInterceptTouchEvent(true);
}
}
}
// , -offsetY
if (Math.abs(downY - y) > mTouchSlop) {
scrollBy(0, -offsetY);
}
lastY = moveY;
}
/**
*
*
* @param event
*/
private void obtainVelocityTracker(MotionEvent event) {
if (null == mVelocityTracker) {
mVelocityTracker = VelocityTracker.obtain();
}
mVelocityTracker.addMovement(event);
}
/**
*
*/
private void recycleVelocityTracker() {
if (null != mVelocityTracker) {
mVelocityTracker.clear();
mVelocityTracker.recycle();
mVelocityTracker = null;
}
}
@Override
public void computeScroll() {
super.computeScroll();
if (mScroller.computeScrollOffset()) {
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
postInvalidate();// , ,
mScrollState = SCROLLING;
} else {
mScrollState = IDLE;
}
}
/**
*
*/
interface ScrollChangeListener {
void onScrollChange(int scrollX, int scrollY);
}
private ScrollChangeListener scrollChangeListener;
/**
*
*
* @param l
*/
public void setScrollChangeListener(ScrollChangeListener l) {
this.scrollChangeListener = l;
}
private int mScrollState;
public static final int IDLE = 0;//
public static final int SCROLLING = 1;//
/**
* @return
*/
public int getScrollState() {
return mScrollState;
}
}