Last active
August 12, 2019 05:20
-
-
Save pythoncat1024/be98b72fd041171efb7e42e6c16fe10f to your computer and use it in GitHub Desktop.
横向滑动view , 实现 HorizontalScrollView 的效果
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
... | |
<HorizontalScrollLayout | |
android:background="@drawable/border_shape" | |
android:layout_width="300dp" | |
android:layout_height="300dp" | |
android:orientation="horizontal"> | |
<LinearLayout | |
android:layout_width="3000dp" | |
android:layout_height="match_parent" | |
android:background="@drawable/long_picture"> | |
</LinearLayout> | |
</HorizontalScrollLayout> | |
... |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.python.cat.needwork.widgets.measure; | |
import android.content.Context; | |
import android.os.SystemClock; | |
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.LinearLayout; | |
import android.widget.OverScroller; | |
import com.apkfuns.logutils.LogUtils; | |
import com.python.cat.needwork.widgets.ViewUtils; | |
public class HorizontalScrollLayout extends LinearLayout { | |
private int touchSlop; | |
private OverScroller mOverScroller; | |
private int tapTimeout; | |
private float downX; | |
private float downY; | |
private long downTime; | |
private boolean isMoving; | |
private int leftBorder; | |
private int rightBorder; | |
private int selfWidth; | |
private VelocityTracker velocityTracker; | |
private ViewConfiguration configuration; | |
private int minimumFlingVelocity; | |
private int maximumFlingVelocity; | |
public HorizontalScrollLayout(Context context) { | |
super(context); | |
init(); | |
} | |
public HorizontalScrollLayout(Context context, AttributeSet attrs) { | |
super(context, attrs); | |
init(); | |
} | |
public HorizontalScrollLayout(Context context, AttributeSet attrs, int defStyleAttr) { | |
super(context, attrs, defStyleAttr); | |
init(); | |
} | |
public HorizontalScrollLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { | |
super(context, attrs, defStyleAttr, defStyleRes); | |
init(); | |
} | |
@Override | |
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { | |
LogUtils.i(ViewUtils.logMeasureSpec(widthMeasureSpec, "LogLayout.width")); | |
LogUtils.i(ViewUtils.logMeasureSpec(heightMeasureSpec, "LogLayout.height")); | |
super.onMeasure(widthMeasureSpec, heightMeasureSpec); | |
} | |
@Override | |
protected void onSizeChanged(int w, int h, int oldw, int oldh) { | |
super.onSizeChanged(w, h, oldw, oldh); | |
LogUtils.e("real width=%s,height=%s", w, h); | |
View childAt = getChildAt(0); | |
ViewGroup.MarginLayoutParams params = (MarginLayoutParams) childAt.getLayoutParams(); | |
leftBorder = params.leftMargin; | |
rightBorder = params.width - params.rightMargin; | |
selfWidth = getWidth() - getPaddingLeft() - getPaddingRight(); | |
LogUtils.e("leftBorder=%s,rightBorder=%s,selfWidth=%s", | |
leftBorder, rightBorder, selfWidth); | |
} | |
private void init() { | |
configuration = ViewConfiguration.get(getContext()); | |
touchSlop = configuration.getScaledTouchSlop(); | |
tapTimeout = ViewConfiguration.getTapTimeout(); | |
minimumFlingVelocity = configuration.getScaledMinimumFlingVelocity(); | |
maximumFlingVelocity = configuration.getScaledMaximumFlingVelocity(); | |
mOverScroller = new OverScroller(getContext()); | |
LogUtils.e("touchSlop=%s,tapTimeout=%s,minFlingV=%s,maxiFlingV=%s", | |
touchSlop, tapTimeout, minimumFlingVelocity, maximumFlingVelocity); | |
} | |
@Override | |
public boolean performClick() { | |
return super.performClick(); | |
} | |
@Override | |
public boolean onTouchEvent(MotionEvent event) { | |
if (velocityTracker == null) { | |
velocityTracker = VelocityTracker.obtain(); | |
} | |
velocityTracker.addMovement(event); | |
switch (event.getAction()) { | |
case MotionEvent.ACTION_DOWN: { | |
downTime = SystemClock.elapsedRealtime(); | |
downX = event.getX(); | |
downY = event.getY(); | |
LogUtils.d("down--- %s,%s", downX, downY); | |
} | |
break; | |
case MotionEvent.ACTION_MOVE: { | |
float moveX = event.getX(); | |
float moveY = event.getY(); | |
float diffX = moveX - downX; | |
float diffY = moveY - downY; | |
LogUtils.i("touchSlop=%s, diffX=%s,diffY=%s", touchSlop, diffX, diffY); | |
if (!isMoving && Math.abs(diffX) > Math.abs(diffY) | |
&& Math.abs(diffX) > touchSlop) { | |
isMoving = true; | |
LogUtils.w("can move dx= %s, scrollX=%s", diffX, getScrollX()); | |
if (diffX > 0 && getScrollX() <= leftBorder) { | |
// don't move, want to move to left | |
LogUtils.e("不要滑动了。。。left " + getScrollX()); | |
} else if (diffX < 0 && getScrollX() + selfWidth >= rightBorder) { | |
// don/t move, want to move to right | |
LogUtils.e("不要滑动了。。。right " + (getScrollX() + selfWidth)); | |
} else if (diffX < 0 | |
&& getScrollX() + Math.abs(diffX) + selfWidth > rightBorder) { | |
scrollTo(rightBorder - selfWidth, 0); | |
} else if (diffX > 0 && getScrollX() - Math.abs(diffX) < leftBorder) { | |
scrollTo(leftBorder, 0); | |
} else { | |
scrollBy((int) -diffX, 0); | |
} | |
} else if (isMoving) { | |
// 这个逻辑的目的是,在连续滑动的过程中,每次滑动距离可以非常小 | |
LogUtils.w("can move dx= %s, scrollX=%s", diffX, getScrollX()); | |
if (diffX > 0 && getScrollX() <= leftBorder) { | |
// don't move, want to move to left | |
LogUtils.e("不要滑动了。。。left " + getScrollX()); | |
} else if (diffX < 0 && getScrollX() + selfWidth >= rightBorder) { | |
// don/t move, want to move to right | |
LogUtils.e("不要滑动了。。。right " + (getScrollX() + selfWidth)); | |
} else if (diffX < 0 | |
&& getScrollX() + Math.abs(diffX) + selfWidth > rightBorder) { | |
scrollTo(rightBorder - selfWidth, 0); | |
} else if (diffX > 0 && getScrollX() - Math.abs(diffX) < leftBorder) { | |
scrollTo(leftBorder, 0); | |
} else { | |
scrollBy((int) -diffX, 0); | |
} | |
} | |
downX = moveX; | |
downY = moveY; | |
} | |
break; | |
case MotionEvent.ACTION_UP: { | |
isMoving = false; | |
velocityTracker.computeCurrentVelocity(1000, maximumFlingVelocity); | |
int xVelocity = Math.round(velocityTracker.getXVelocity()); | |
float upX = event.getX(); | |
float upY = event.getY(); | |
float diffX = upX - downX; | |
float diffY = upY - downY; | |
if (Math.abs(diffX) < touchSlop | |
&& Math.abs(diffY) < touchSlop | |
&& SystemClock.elapsedRealtime() - downTime < tapTimeout) { | |
performClick(); | |
LogUtils.e("is a click event"); | |
} else { | |
LogUtils.e("up----l %s, %s ", getScrollX(), rightBorder - selfWidth - getScrollX()); | |
// 强制结束 mScroller 未完成的动画 | |
mOverScroller.forceFinished(true); | |
final boolean canFling = (getScaleX() > 0 || xVelocity > 0) && | |
(getScrollX() < getScrollRange() || xVelocity < 0); | |
if (canFling && Math.abs(xVelocity) > minimumFlingVelocity) { | |
int width = getWidth() - getPaddingLeft() - getPaddingRight(); | |
int right = getChildAt(0).getWidth(); | |
// 参考:android.widget.ScrollView.fling() 方法完成 | |
mOverScroller.fling(getScrollX(), getScrollY(), -xVelocity, 0, | |
0, Math.max(0, right - width), | |
0, 0, 0, width / 2); | |
} | |
// 滚动到最左边 ok | |
// mOverScroller.startScroll(scrollX, getScrollY(), leftBorder - scrollX, getScrollY()); | |
// 滚动到最右边 ok | |
// mOverScroller.startScroll(scrollX, getScrollY(), rightBorder - selfWidth - scrollX, getScrollY()); | |
// scrollBy(rightBorder - selfWidth - scrollX, 0); // ok | |
// 让 View 重绘 | |
invalidate(); // 一定要调用,否则有时候不触发,如果是使用 Scroller 的话! | |
} | |
velocityTracker.clear(); | |
velocityTracker.recycle(); | |
velocityTracker = null; | |
} | |
break; | |
case MotionEvent.ACTION_CANCEL: { | |
isMoving = false; | |
LogUtils.e("action--- cancel"); | |
velocityTracker.recycle(); | |
} | |
break; | |
default: | |
LogUtils.e("action--- " + event.getAction()); | |
break; | |
} | |
return true; // 不true 的话,只有 down 能接收到 | |
} | |
/** | |
* 参考:android.widget.ScrollView#getScrollRange | |
*/ | |
private int getScrollRange() { | |
int scrollRange = 0; | |
if (getChildCount() > 0) { | |
View child = getChildAt(0); | |
scrollRange = Math.max(0, | |
child.getWidth() - (getWidth() - getPaddingLeft() - getPaddingRight())); | |
} | |
return scrollRange; | |
} | |
@Override | |
public void computeScroll() { | |
// 第三步,重写computeScroll()方法,并在其内部完成平滑滚动的逻辑 | |
if (mOverScroller.computeScrollOffset()) { | |
scrollTo(mOverScroller.getCurrX(), mOverScroller.getCurrY()); | |
postInvalidate(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment