|
@@ -0,0 +1,330 @@
|
|
|
|
+package com.kdanmobile.reader;
|
|
|
|
+
|
|
|
|
+import android.app.Activity;
|
|
|
|
+import android.content.Context;
|
|
|
|
+import android.support.annotation.NonNull;
|
|
|
|
+import android.util.AttributeSet;
|
|
|
|
+import android.util.DisplayMetrics;
|
|
|
|
+import android.view.GestureDetector;
|
|
|
|
+import android.view.MotionEvent;
|
|
|
|
+import android.widget.LinearLayout;
|
|
|
|
+import android.widget.Scroller;
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * @类名:HorizontalView
|
|
|
|
+ * @类描述:自定义线性布局,实现布局整个滑动功能
|
|
|
|
+ * @作者:zhouguifang
|
|
|
|
+ * @创建时间:2015-7-6-下午5:59:49
|
|
|
|
+ * @修改人:
|
|
|
|
+ * @修改时间:
|
|
|
|
+ * @修改备注:
|
|
|
|
+ * @版本:
|
|
|
|
+ * @Copyright:(c)-2015kdan mobile
|
|
|
|
+ */
|
|
|
|
+@SuppressWarnings("ALL")
|
|
|
|
+public class HorizontalView extends LinearLayout implements
|
|
|
|
+ GestureDetector.OnGestureListener {
|
|
|
|
+ // private static final String TAG = "Scroller";
|
|
|
|
+ private final GestureDetector mGestureDetector;
|
|
|
|
+
|
|
|
|
+ private Scroller mScroller;
|
|
|
|
+
|
|
|
|
+ private float last_position = 0;
|
|
|
|
+ private float start_position = 0;
|
|
|
|
+
|
|
|
|
+ private float x1, x2, y1, y2;
|
|
|
|
+ private boolean isFirst = true;
|
|
|
|
+ private boolean isResult = false;
|
|
|
|
+
|
|
|
|
+ private int moveDistance = 266;
|
|
|
|
+ public boolean isOpen = false;
|
|
|
|
+ private boolean isOk = false;
|
|
|
|
+ private boolean isScroll = true;
|
|
|
|
+
|
|
|
|
+ private boolean isFirstPager = true;
|
|
|
|
+
|
|
|
|
+ private static final int MOVING_DIAGONALLY = 0;
|
|
|
|
+ private static final int MOVING_LEFT = 1;
|
|
|
|
+ private static final int MOVING_RIGHT = 2;
|
|
|
|
+ private static final int MOVING_UP = 3;
|
|
|
|
+ private static final int MOVING_DOWN = 4;
|
|
|
|
+
|
|
|
|
+ public HorizontalView(Context context, AttributeSet attrs) {
|
|
|
|
+ super(context, attrs);
|
|
|
|
+ mScroller = new Scroller(context);
|
|
|
|
+ mGestureDetector = new GestureDetector(this);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public void setScroll(boolean isScroll) {
|
|
|
|
+ this.isScroll = isScroll;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public void setFirstPager(boolean isFirstPager) {
|
|
|
|
+ this.isFirstPager = isFirstPager;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public boolean dispatchTouchEvent(@NonNull MotionEvent ev) {
|
|
|
|
+ // Out.println("dispatchTouchEvent...");
|
|
|
|
+ switch (ev.getAction()) {
|
|
|
|
+ case MotionEvent.ACTION_DOWN:
|
|
|
|
+ // Out.println("ACTION_DOWN...");
|
|
|
|
+ x1 = ev.getX();
|
|
|
|
+ y1 = ev.getY();
|
|
|
|
+ break;
|
|
|
|
+ case MotionEvent.ACTION_MOVE:
|
|
|
|
+ // Out.println("ACTION_MOVE...");
|
|
|
|
+ if (isFirst) {
|
|
|
|
+ x2 = ev.getX();
|
|
|
|
+ y2 = ev.getY();
|
|
|
|
+ float x = Math.abs(x1 - x2);
|
|
|
|
+ float y = Math.abs(y1 - y2);
|
|
|
|
+ float distance = (x * x) + (y * y);
|
|
|
|
+ // Out.println("isFirst...dis:" + distance);
|
|
|
|
+ DisplayMetrics displayMetrics = new DisplayMetrics();
|
|
|
|
+ Activity activity = (Activity) getContext();
|
|
|
|
+ activity.getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
|
|
|
|
+ float density = displayMetrics.density;
|
|
|
|
+ if (distance > (density * 5)) {
|
|
|
|
+ isFirst = false;
|
|
|
|
+ if (x > y) {
|
|
|
|
+ // 第一页向右滑动||第一页向左滑动且控件是打开的
|
|
|
|
+ if ((((x1 - x2) < 0) && isFirstPager)
|
|
|
|
+ || (((x1 - x2) > 0) && isFirstPager && isOpen)) {
|
|
|
|
+ isResult = true;
|
|
|
|
+ start_position = ev.getX();
|
|
|
|
+ last_position = ev.getX();
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ break;
|
|
|
|
+ case MotionEvent.ACTION_UP:
|
|
|
|
+ // LogUtil.print_i(HorizontalView.class,"ACTION_UP...");
|
|
|
|
+ isFirst = true;
|
|
|
|
+ isResult = false;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ return super.dispatchTouchEvent(ev);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
|
|
|
|
+ return isScroll && isResult || super.onInterceptTouchEvent(ev);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public boolean onTouchEvent(@NonNull MotionEvent event) {
|
|
|
|
+ mGestureDetector.onTouchEvent(event);
|
|
|
|
+ int action = event.getAction();
|
|
|
|
+ float x = event.getX();
|
|
|
|
+ switch (action) {
|
|
|
|
+ case MotionEvent.ACTION_DOWN:
|
|
|
|
+ start_position = event.getX();
|
|
|
|
+ last_position = event.getX();
|
|
|
|
+ break;
|
|
|
|
+ case MotionEvent.ACTION_MOVE:
|
|
|
|
+ // LogUtil.print_i(HorizontalView.class,"onTouchEvent...ACTION_MOVE");
|
|
|
|
+ float distant = x - start_position;
|
|
|
|
+ if (!isOpen) {
|
|
|
|
+ if (distant > moveDistance) {
|
|
|
|
+ distant = moveDistance;
|
|
|
|
+ }
|
|
|
|
+ if (distant < 0) {
|
|
|
|
+ distant = 0;
|
|
|
|
+ }
|
|
|
|
+ x = distant + start_position;
|
|
|
|
+ } else {
|
|
|
|
+ if (distant > 0) {
|
|
|
|
+ distant = 0;
|
|
|
|
+ }
|
|
|
|
+ if (distant < -moveDistance) {
|
|
|
|
+ distant = -moveDistance;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ x = distant + start_position;
|
|
|
|
+ smoothScrollBy((int) (last_position - x), 0, 0);
|
|
|
|
+ last_position = x;
|
|
|
|
+ break;
|
|
|
|
+ case MotionEvent.ACTION_UP:
|
|
|
|
+ if (!isOk) {
|
|
|
|
+ if (isOpen) {
|
|
|
|
+ if ((mScroller.getFinalX() + ((moveDistance * 7) / 8)) > 0) {
|
|
|
|
+ smoothScrollTo(0, 0);
|
|
|
|
+ isOpen = false;
|
|
|
|
+ } else {
|
|
|
|
+ smoothScrollTo(0 - moveDistance, 0);
|
|
|
|
+ isOpen = true;
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ if ((mScroller.getFinalX() + ((moveDistance) / 8)) > 0) {
|
|
|
|
+ smoothScrollTo(0, 0);
|
|
|
|
+ isOpen = false;
|
|
|
|
+ } else {
|
|
|
|
+ smoothScrollTo(0 - moveDistance, 0);
|
|
|
|
+ isOpen = true;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ isOk = false;
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ return super.onTouchEvent(event);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
|
|
|
|
+ float velocityY) {
|
|
|
|
+ switch (directionOfTravel(velocityX, velocityY)) {
|
|
|
|
+ case MOVING_LEFT:
|
|
|
|
+ smoothScrollTo(0, 0);
|
|
|
|
+ isOpen = false;
|
|
|
|
+ break;
|
|
|
|
+ case MOVING_RIGHT:
|
|
|
|
+ smoothScrollTo(0 - moveDistance, 0);
|
|
|
|
+ isOpen = true;
|
|
|
|
+ break;
|
|
|
|
+ default:
|
|
|
|
+ // LogUtil.print_i(HorizontalView.class,"x:" + mScroller.getFinalX() + " Y:"
|
|
|
|
+ // + mScroller.getFinalY());
|
|
|
|
+ if (isOpen) {
|
|
|
|
+ if ((mScroller.getFinalX() + ((moveDistance * 7) / 8)) > 0) {
|
|
|
|
+ smoothScrollTo(0, 0);
|
|
|
|
+ isOpen = false;
|
|
|
|
+ } else {
|
|
|
|
+ smoothScrollTo(0 - moveDistance, 0);
|
|
|
|
+ isOpen = true;
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ if ((mScroller.getFinalX() + ((moveDistance) / 8)) > 0) {
|
|
|
|
+ smoothScrollTo(0, 0);
|
|
|
|
+ isOpen = false;
|
|
|
|
+ } else {
|
|
|
|
+ smoothScrollTo(0 - moveDistance, 0);
|
|
|
|
+ isOpen = true;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ isOk = true;
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public void animateToggle() {
|
|
|
|
+ if (isOpen) {
|
|
|
|
+ smoothScrollTo(0, 0);
|
|
|
|
+ } else {
|
|
|
|
+ smoothScrollTo(0 - moveDistance, 0);
|
|
|
|
+ }
|
|
|
|
+ isOpen = !isOpen;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * @方法说明:计算移动方向
|
|
|
|
+ * @方法名称:directionOfTravel
|
|
|
|
+ * @param vx
|
|
|
|
+ * @param vy
|
|
|
|
+ * @return
|
|
|
|
+ * @返回值:int
|
|
|
|
+ */
|
|
|
|
+ private static int directionOfTravel(float vx, float vy) {
|
|
|
|
+ if (Math.abs(vx) > (2 * Math.abs(vy)))
|
|
|
|
+ return (vx > 0) ? MOVING_RIGHT : MOVING_LEFT;
|
|
|
|
+ else if (Math.abs(vy) > (2 * Math.abs(vx)))
|
|
|
|
+ return (vy > 0) ? MOVING_DOWN : MOVING_UP;
|
|
|
|
+ else
|
|
|
|
+ return MOVING_DIAGONALLY;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * @方法说明:调用此方法滚动到目标位置
|
|
|
|
+ * @方法名称:smoothScrollTo
|
|
|
|
+ * @param fx
|
|
|
|
+ * @param fy
|
|
|
|
+ * @返回值:void
|
|
|
|
+ */
|
|
|
|
+ public void smoothScrollTo(int fx, int fy) {
|
|
|
|
+ int dx = fx - mScroller.getFinalX();
|
|
|
|
+ int dy = fy - mScroller.getFinalY();
|
|
|
|
+ smoothScrollBy(dx, dy);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * @方法说明:调用此方法滚动到目标位置
|
|
|
|
+ * @方法名称:smoothScrollTo
|
|
|
|
+ * @param fx
|
|
|
|
+ * @param fy
|
|
|
|
+ * @param drution
|
|
|
|
+ * @返回值:void
|
|
|
|
+ */
|
|
|
|
+ public void smoothScrollTo(int fx, int fy, int drution) {
|
|
|
|
+ int dx = fx - mScroller.getFinalX();
|
|
|
|
+ int dy = fy - mScroller.getFinalY();
|
|
|
|
+ mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(), dx,
|
|
|
|
+ dy, drution);
|
|
|
|
+ invalidate();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * @方法说明:调用此方法设置滚动的相对偏移
|
|
|
|
+ * @方法名称:smoothScrollBy
|
|
|
|
+ * @param dx
|
|
|
|
+ * @param dy
|
|
|
|
+ * @返回值:void
|
|
|
|
+ */
|
|
|
|
+ public void smoothScrollBy(int dx, int dy) {
|
|
|
|
+ // 设置mScroller的滚动偏移量
|
|
|
|
+ mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(), dx,
|
|
|
|
+ dy, 500);
|
|
|
|
+ invalidate();// 这里必须调用invalidate()才能保证computeScroll()会被调用,否则不一定会刷新界面,看不到滚动效果
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public void smoothScrollBy(int dx, int dy, int drution) {
|
|
|
|
+ // 设置mScroller的滚动偏移量
|
|
|
|
+ mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(), dx,
|
|
|
|
+ dy, drution);
|
|
|
|
+ invalidate();// 这里必须调用invalidate()才能保证computeScroll()会被调用,否则不一定会刷新界面,看不到滚动效果
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public void computeScroll() {
|
|
|
|
+ // 先判断mScroller滚动是否完成
|
|
|
|
+ if (mScroller.computeScrollOffset()) {
|
|
|
|
+ // 这里调用View的scrollTo()完成实际的滚动
|
|
|
|
+ scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
|
|
|
|
+
|
|
|
|
+ // 必须调用该方法,否则不一定能看到滚动效果
|
|
|
|
+ postInvalidate();
|
|
|
|
+ }
|
|
|
|
+ super.computeScroll();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public void setMoveDistance(int distance) {
|
|
|
|
+ moveDistance = distance;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public boolean onDown(MotionEvent e) {
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public void onShowPress(MotionEvent e) {
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public boolean onSingleTapUp(MotionEvent e) {
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
|
|
|
|
+ float distanceY) {
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public void onLongPress(MotionEvent e) {
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+}
|