欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android中的拖拽及嵌套滑动

程序员文章站 2022-03-05 14:23:42
15_Android中的拖拽及嵌套滑动拖拽和嵌套滑动拖拽OnDragListenerViewDragHelper为什么要这两个东西,而不是一个?嵌套滑动嵌套滑动的场景拖拽和嵌套滑动拖拽OnDragListener通过startDrag()来启动拖拽用 setOnDragListener()来监听OnDragListener 内部只有一个方法:onDrag()onDragEvent()方法也会收到拖拽回调(界面中的每个View都会收到)ViewDragHelper需要创建一个 V...

拖拽和嵌套滑动

拖拽

OnDragListener

  • 通过startDrag()来启动拖拽
  • 用 setOnDragListener()来监听
    • OnDragListener 内部只有一个方法:onDrag()
    • onDragEvent()方法也会收到拖拽回调(界面中的每个View都会收到)

ViewDragHelper

  • 需要创建一个 ViewDragHelper 和 Callback()
  • 需要写在 ViewGroup 里面,重写 onInterceptTouchEvent()和 onTouchevent()

为什么要这两个东西,而不是一个?

  • OnDragListener

    • API 11加入的工具类,用于拖拽操作。
    • 使用场景:用户的「拖起-> 放下」操作,重在内容的移动。可以附加拖拽数据
    • 不需要写自定义View,使用startDrag() / startDragAndDrop()手动开启拖拽
    • 拖拽的原理是创造出一个图像在屏幕的最上层,用户的手指拖着图像移动
  • ViewDragHelper

    • 2015年的support v4包中新增的工具类,用于拖拽操作。
    • 使用场景:用户拖动ViewGroup中的某个子View
    • 需要应用在自定义 ViewGroup 中调用 ViewDragHelper.shouldInterceptTouchEvent()和 processTouchEvent(),程序会自动开启拖拽
    • 拖拽的原理是实时修改被拖拽的子View的mLeft, mTop, mRight, mBottom值

嵌套滑动

嵌套滑动的场景

  • 不同向嵌套

    • onInterceptTouchEvent 父 View 拦截
    • requestDisallowInterceptTouchEvent()子 View 阻止父 View 拦截
  • 同向嵌套

    • 父View会彻底卡住子View

      • 原因:抢夺条件一致,但父View的onInterceptTouchEvent()早于子View的 dispatchTouchEvent()
    • 本质上是策略问题:嵌套状态下用户手指滑动,他是想滑谁?

      • 场景一:NestedScrollView
        • 子View能滑动的时候,滑子View;滑不动的时候,滑父View
      • 场景二:Google的样例
        • 父View展开的时候:
          • 上滑:优先滑父View
          • 下滑:滑不动(所以可以说还是优先滑父View)
        • 父View半展开的时候:
          • 向上:优先滑父View,滑到父View完全收齐之后开始滑子View
          • 向下:滑父View,滑到父View完全展开之后开始滑子View
        • 父View收起的时候:
          • 上滑:滑子View (所以可以说是优先滑子View)
          • 下滑:优先滑子View,滑到子View顶部的时候开始滑父View
    • 解决方案:实现策略一一父View、子View谁来消费事件可以实时协商

    • 实现?

      • 其实大多数场景,SDK就解决了
        • 案例1:ScrollView嵌套:没法滑动
        • 案例2:换成NestedScrollView :可以滑动
      • 自己实现?
        • 案例3:按照课上代码,实现NestedScrollingChild2接口来实现自定义的嵌套滑动逻辑

OnDragListener范例1

public class DragListenerGridView extends ViewGroup {
    private static final int COLUMNS = 2;
    private static final int ROWS = 3;

    ViewConfiguration viewConfiguration;
    OnDragListener dragListener = new HenDragListener();
    View draggedView;
    List<View> orderedChildren = new ArrayList<>();

    public DragListenerGridView(Context context, AttributeSet attrs) {
        super(context, attrs);
        viewConfiguration = ViewConfiguration.get(context);
        setChildrenDrawingOrderEnabled(true);
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        for (int i = 0; i < getChildCount(); i++) {
            View child = getChildAt(i);
            orderedChildren.add(child); // 初始化位置
            child.setOnLongClickListener(new OnLongClickListener() {
                @Override
                public boolean onLongClick(View v) {
                    draggedView = v;
                    v.startDrag(null, new DragShadowBuilder(v), v, 0);
                    return false;
                }
            });
            child.setOnDragListener(dragListener);
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int specWidth = MeasureSpec.getSize(widthMeasureSpec);
        int specHeight = MeasureSpec.getSize(heightMeasureSpec);
        int childWidth = specWidth / COLUMNS;
        int childHeight = specHeight / ROWS;

        measureChildren(MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.EXACTLY),
                MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.EXACTLY));

        setMeasuredDimension(specWidth, specHeight);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int count = getChildCount();
        int childLeft;
        int childTop;
        int childWidth = getWidth() / COLUMNS;
        int childHeight = getHeight() / ROWS;
        for (int index = 0; index < count; index++) {
            View child = getChildAt(index);
            childLeft = index % 2 * childWidth;
            childTop = index / 2 * childHeight;
            child.layout(0, 0, childWidth, childHeight);
//            child.layout(childLeft, childTop, childWidth, childHeight);
            child.setTranslationX(childLeft);
            child.setTranslationY(childTop);
        }
    }

    private class HenDragListener implements OnDragListener {
        @Override
        public boolean onDrag(View v, DragEvent event) {
            switch (event.getAction()) {
                case DragEvent.ACTION_DRAG_STARTED:
                    if (event.getLocalState() == v) {
                        v.setVisibility(INVISIBLE);
                    }
                    break;
                case DragEvent.ACTION_DRAG_ENTERED:
                    if (event.getLocalState() != v) {
                        sort(v);
                    }
                    break;
                case DragEvent.ACTION_DRAG_EXITED:
                    break;
                case DragEvent.ACTION_DRAG_ENDED:
                    v.setVisibility(VISIBLE);
                    break;
            }
            return true;
        }
    }

    private void sort(View targetView) {
        int draggedIndex = -1;
        int targetIndex = -1;
        for (int i = 0; i < getChildCount(); i++) {
            View child = orderedChildren.get(i);
            if (targetView == child) {
                targetIndex = i;
            } else if (draggedView == child) {
                draggedIndex = i;
            }
        }
        if (targetIndex < draggedIndex) {
            orderedChildren.remove(draggedIndex);
            orderedChildren.add(targetIndex, draggedView);
        } else if (targetIndex > draggedIndex) {
            orderedChildren.remove(draggedIndex);
            orderedChildren.add(targetIndex, draggedView);
        }
        int childLeft;
        int childTop;
        int childWidth = getWidth() / COLUMNS;
        int childHeight = getHeight() / ROWS;
        for (int index = 0; index < getChildCount(); index++) {
            View child = orderedChildren.get(index);
            childLeft = index % 2 * childWidth;
            childTop = index / 2 * childHeight;
            child.animate()
                    .translationX(childLeft)
                    .translationY(childTop)
                    .setDuration(150);
        }
    }
}

drag_listener_grid_view.xml

<?xml version="1.0" encoding="utf-8"?>
<com.hencoder.a15_drag_nestedscroll.view.drag.DragListenerGridView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <View
        android:id="@+id/view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#EF5350" />

    <View
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#9C27B0" />

    <View
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#1E88E5" />

    <View
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#00695C" />

    <View
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#FDD835" />

    <View
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#546E7A" />

</com.hencoder.a15_drag_nestedscroll.view.drag.DragListenerGridView>

OnDragListener范例2

public class DragToCollectLayout extends RelativeLayout {
    ImageView avatarView;
    ImageView logoView;
    LinearLayout collectorLayout;

    OnLongClickListener dragStarter = new OnLongClickListener() {
        @SuppressLint("Range")
        @Override
        public boolean onLongClick(View v) {
            ClipData imageData = ClipData.newPlainText("name", v.getContentDescription());
            v.setAlpha((float) 0.5);
            return ViewCompat.startDragAndDrop(v, imageData, new DragShadowBuilder(v), null, 0);
        }
    };
    OnDragListener dragListener = new CollectListener();

    public DragToCollectLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();

        avatarView = findViewById(R.id.avatarView);
        logoView = findViewById(R.id.logoView);
        collectorLayout = findViewById(R.id.collectorLayout);

        avatarView.setOnLongClickListener(dragStarter);
        logoView.setOnLongClickListener(dragStarter);
        collectorLayout.setOnDragListener(dragListener);
    }

    class CollectListener implements OnDragListener {
        @Override
        public boolean onDrag(View v, DragEvent event) {
            switch (event.getAction()) {
                case DragEvent.ACTION_DROP:
                    if (v instanceof LinearLayout) {
                        LinearLayout layout = (LinearLayout) v;
                        TextView textView = new TextView(getContext());
                        textView.setTextSize(16);
                        textView.setText(event.getClipData().getItemAt(0).getText());
                        layout.addView(textView);
                    }
                    break;
            }
            return true;
        }
    }

}

drag_to_collect.xml

<?xml version="1.0" encoding="utf-8"?>
<com.hencoder.a15_drag_nestedscroll.view.drag.DragToCollectLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <ImageView
            android:id="@+id/avatarView"
            android:layout_width="0dp"
            android:layout_height="120dp"
            android:layout_weight="1"
            android:src="@drawable/avatar_rengwuxian"
            android:contentDescription="Avatar" />

        <ImageView
            android:id="@+id/logoView"
            android:layout_width="0dp"
            android:layout_height="120dp"
            android:layout_weight="1"
            android:src="@drawable/google_logo"
            android:contentDescription="Logo" />
    </LinearLayout>

    <LinearLayout
        android:id="@+id/collectorLayout"
        android:layout_width="match_parent"
        android:layout_height="80dp"
        android:layout_alignParentBottom="true"
        android:background="#78909C"
        android:orientation="horizontal" />

</com.hencoder.a15_drag_nestedscroll.view.drag.DragToCollectLayout>

ViewDragHelper范例1

public class DragHelperGridView extends ViewGroup {
    private static final int COLUMNS = 2;
    private static final int ROWS = 3;

    ViewDragHelper dragHelper;

    public DragHelperGridView(Context context, AttributeSet attrs) {
        super(context, attrs);
        dragHelper = ViewDragHelper.create(this, new DragCallback());
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int specWidth = MeasureSpec.getSize(widthMeasureSpec);
        int specHeight = MeasureSpec.getSize(heightMeasureSpec);
        int childWidth = specWidth / COLUMNS;
        int childHeight = specHeight / ROWS;

        measureChildren(MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.EXACTLY),
                MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.EXACTLY));

        setMeasuredDimension(specWidth, specHeight);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int count = getChildCount();
        int childLeft;
        int childTop;
        int childWidth = getWidth() / COLUMNS;
        int childHeight = getHeight() / ROWS;
        for (int index = 0; index < count; index++) {
            View child = getChildAt(index);
            childLeft = index % 2 * childWidth;
            childTop = index / 2 * childHeight;
            child.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);
        }
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return dragHelper.shouldInterceptTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        dragHelper.processTouchEvent(event);
        return true;
    }

    @Override
    public void computeScroll() {
        if (dragHelper.continueSettling(true)) {
            ViewCompat.postInvalidateOnAnimation(this);
        }
    }

    private class DragCallback extends ViewDragHelper.Callback {
        float capturedLeft;
        float capturedTop;

        @Override
        public boolean tryCaptureView(@NonNull View child, int pointerId) {
            return true;
        }

        @Override
        public void onViewDragStateChanged(int state) {
            if (state == ViewDragHelper.STATE_IDLE) {
                View capturedView = dragHelper.getCapturedView();
                if (capturedView != null) {
                    capturedView.setElevation(capturedView.getElevation() - 1);
                }
            }
        }

        @Override
        public int clampViewPositionHorizontal(@NonNull View child, int left, int dx) {
            return left;
        }

        @Override
        public int clampViewPositionVertical(@NonNull View child, int top, int dy) {
            return top;
        }

        @Override
        public void onViewCaptured(@NonNull View capturedChild, int activePointerId) {
            capturedChild.setElevation(getElevation() + 1);
            capturedLeft = capturedChild.getLeft();
            capturedTop = capturedChild.getTop();
        }

        @Override
        public void onViewPositionChanged(@NonNull View changedView, int left, int top, int dx, int dy) {
        }

        @Override
        public void onViewReleased(@NonNull View releasedChild, float xvel, float yvel) {
            dragHelper.settleCapturedViewAt((int) capturedLeft, (int) capturedTop);
            postInvalidateOnAnimation();
        }
    }
}

drag_helper_grid_view.xml

<?xml version="1.0" encoding="utf-8"?>
<com.hencoder.a15_drag_nestedscroll.view.drag.DragHelperGridView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <View
        android:id="@+id/view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#EF5350" />

    <View
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#9C27B0" />

    <View
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#1E88E5" />

    <View
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#00695C" />

    <View
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#FDD835" />

    <View
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#546E7A" />

</com.hencoder.a15_drag_nestedscroll.view.drag.DragHelperGridView>

ViewDragHelper范例2

public class DragUpDownLayout extends FrameLayout {
    View view;
    ViewDragHelper dragHelper;
    ViewDragHelper.Callback dragListener = new DragListener();
    ViewConfiguration viewConfiguration;

    public DragUpDownLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);

        dragHelper = ViewDragHelper.create(this, dragListener);
        viewConfiguration = ViewConfiguration.get(context);
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        view = findViewById(R.id.view);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return dragHelper.shouldInterceptTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        dragHelper.processTouchEvent(event);
        return true;
    }

    @Override
    public void computeScroll() {
        if (dragHelper.continueSettling(true)) {
            ViewCompat.postInvalidateOnAnimation(this);
        }
    }

    class DragListener extends ViewDragHelper.Callback {
        @Override
        public boolean tryCaptureView(@NonNull View child, int pointerId) {
            return child == view;
        }

        @Override
        public int clampViewPositionVertical(@NonNull View child, int top, int dy) {
            return top;
        }

        @Override
        public void onViewReleased(@NonNull View releasedChild, float xvel, float yvel) {
            if (Math.abs(yvel) > viewConfiguration.getScaledMinimumFlingVelocity()) {
                if (yvel > 0) {
                    dragHelper.settleCapturedViewAt(0, getHeight() - releasedChild.getHeight());
                } else {
                    dragHelper.settleCapturedViewAt(0, 0);
                }
            } else {
                if (releasedChild.getTop() < getHeight() - releasedChild.getBottom()) {
                    dragHelper.settleCapturedViewAt(0, 0);
                } else {
                    dragHelper.settleCapturedViewAt(0, getHeight() - releasedChild.getHeight());
                }
            }
            postInvalidateOnAnimation();
        }
    }

}

drag_up_down.xml

<?xml version="1.0" encoding="utf-8"?>
<com.hencoder.a15_drag_nestedscroll.view.drag.DragUpDownLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <View
        android:id="@+id/view"
        android:layout_width="match_parent"
        android:layout_height="200dp"
        android:background="#388E3C"/>

</com.hencoder.a15_drag_nestedscroll.view.drag.DragUpDownLayout>

案例1
scroll_view.xml

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <View
            android:layout_width="match_parent"
            android:layout_height="400dp"
            android:background="@color/colorAccent" />

        <ScrollView
            android:layout_width="match_parent"
            android:layout_height="400dp">

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="vertical">

                <View
                    android:layout_width="match_parent"
                    android:layout_height="100dp"
                    android:background="#22000000"/>

                <View
                    android:layout_width="match_parent"
                    android:layout_height="100dp"
                    android:background="#44000000"/>

                <View
                    android:layout_width="match_parent"
                    android:layout_height="100dp"
                    android:background="#66000000"/>

                <View
                    android:layout_width="match_parent"
                    android:layout_height="100dp"
                    android:background="#88000000"/>

                <View
                    android:layout_width="match_parent"
                    android:layout_height="100dp"
                    android:background="#aa000000"/>

                <View
                    android:layout_width="match_parent"
                    android:layout_height="100dp"
                    android:background="#22ff0000"/>

                <View
                    android:layout_width="match_parent"
                    android:layout_height="100dp"
                    android:background="#44ff0000"/>

            </LinearLayout>

        </ScrollView>

    </LinearLayout>

</ScrollView>

案例2
nested_scroll_view.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <View
            android:layout_width="match_parent"
            android:layout_height="400dp"
            android:background="@color/colorAccent" />

        <androidx.core.widget.NestedScrollView
            android:layout_width="match_parent"
            android:layout_height="400dp">

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="vertical">

                <View
                    android:layout_width="match_parent"
                    android:layout_height="100dp"
                    android:background="#22000000"/>

                <View
                    android:layout_width="match_parent"
                    android:layout_height="100dp"
                    android:background="#44000000"/>

                <View
                    android:layout_width="match_parent"
                    android:layout_height="100dp"
                    android:background="#66000000"/>

                <View
                    android:layout_width="match_parent"
                    android:layout_height="100dp"
                    android:background="#88000000"/>

                <View
                    android:layout_width="match_parent"
                    android:layout_height="100dp"
                    android:background="#aa000000"/>

                <View
                    android:layout_width="match_parent"
                    android:layout_height="100dp"
                    android:background="#22ff0000"/>

                <View
                    android:layout_width="match_parent"
                    android:layout_height="100dp"
                    android:background="#44ff0000"/>

            </LinearLayout>

        </androidx.core.widget.NestedScrollView>

    </LinearLayout>

</androidx.core.widget.NestedScrollView>

案例3
nested_scalable_image_view.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <View
            android:layout_width="match_parent"
            android:layout_height="400dp"
            android:background="@color/colorAccent" />

        <com.hencoder.a15_drag_nestedscroll.view.nested_scroll.ScalableImageView
            android:layout_width="match_parent"
            android:layout_height="800dp" />

    </LinearLayout>

</androidx.core.widget.NestedScrollView>
public class ScalableImageView extends View implements NestedScrollingChild2 {
    private static final float IMAGE_WIDTH = Utils.dpToPixel(300);
    private static final float OVER_SCALE_FACTOR = 1.5f;

    Bitmap bitmap;
    Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);

    float offsetX;
    float offsetY;
    float originalOffsetX;
    float originalOffsetY;
    float smallScale;
    float bigScale;
    boolean big;
    float currentScale;
    ObjectAnimator scaleAnimator;
    GestureDetectorCompat detector;
    HenGestureListener gestureListener = new HenGestureListener();
    HenFlingRunner henFlingRunner = new HenFlingRunner();
    ScaleGestureDetector scaleDetector;
    HenScaleListener henScaleListener = new HenScaleListener();
    OverScroller scroller;
    NestedScrollingChildHelper childHelper;

    public ScalableImageView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);

        bitmap = Utils.getAvatar(getResources(), (int) IMAGE_WIDTH);
        detector = new GestureDetectorCompat(context, gestureListener);
        scroller = new OverScroller(context);
        scaleDetector = new ScaleGestureDetector(context, henScaleListener);
        childHelper = new NestedScrollingChildHelper(this);
        childHelper.setNestedScrollingEnabled(true);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);

        originalOffsetX = ((float) getWidth() - bitmap.getWidth()) / 2;
        originalOffsetY = ((float) getHeight() - bitmap.getHeight()) / 2;

        if ((float) bitmap.getWidth() / bitmap.getHeight() > (float) getWidth() / getHeight()) {
            smallScale = (float) getWidth() / bitmap.getWidth();
            bigScale = (float) getHeight() / bitmap.getHeight() * OVER_SCALE_FACTOR;
        } else {
            smallScale = (float) getHeight() / bitmap.getHeight();
            bigScale = (float) getWidth() / bitmap.getWidth() * OVER_SCALE_FACTOR;
        }
        currentScale = smallScale;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        boolean result = scaleDetector.onTouchEvent(event);
        if (!scaleDetector.isInProgress()) {
            result = detector.onTouchEvent(event);
            childHelper.startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL);
        }

        return result;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        float scaleFraction = (currentScale - smallScale) / (bigScale - smallScale);
        canvas.translate(offsetX * scaleFraction, offsetY * scaleFraction);
        canvas.scale(currentScale, currentScale, getWidth() / 2f, getHeight() / 2f);
        canvas.drawBitmap(bitmap, originalOffsetX, originalOffsetY, paint);
    }

    private float getCurrentScale() {
        return currentScale;
    }

    private void setCurrentScale(float currentScale) {
        this.currentScale = currentScale;
        invalidate();
    }

    private ObjectAnimator getScaleAnimator() {
        if (scaleAnimator == null) {
            scaleAnimator = ObjectAnimator.ofFloat(this, "currentScale", 0);
        }
        scaleAnimator.setFloatValues(smallScale, bigScale);
        return scaleAnimator;
    }

    @Override
    public boolean startNestedScroll(int axes, int type) {
        return childHelper.startNestedScroll(axes, type);
    }

    @Override
    public void stopNestedScroll(int type) {
        childHelper.stopNestedScroll(type);
    }

    @Override
    public boolean hasNestedScrollingParent(int type) {
        return childHelper.hasNestedScrollingParent(type);
    }

    @Override
    public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, @Nullable int[] offsetInWindow, int type) {
        return childHelper.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, offsetInWindow, type);
    }

    @Override
    public boolean dispatchNestedPreScroll(int dx, int dy, @Nullable int[] consumed, @Nullable int[] offsetInWindow, int type) {
        return childHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow, type);
    }

    class HenGestureListener extends GestureDetector.SimpleOnGestureListener {

        @Override
        public boolean onDown(MotionEvent e) {
            return true;
        }

        @Override
        public void onShowPress(MotionEvent e) {

        }

        @Override
        public boolean onSingleTapUp(MotionEvent e) {
            return false;
        }

        @Override
        public boolean onScroll(MotionEvent down, MotionEvent event, float distanceX, float distanceY) {
            if (big) {
                offsetX -= distanceX;
                offsetY -= distanceY;
//                dispatchNestedPreScroll()
                int unconsumed = fixOffsets();
                if (unconsumed != 0) {
                    childHelper.dispatchNestedScroll(0, 0, 0, unconsumed, null);
                } else {
                    invalidate();
                }
            }
            return false;
        }

        @Override
        public void onLongPress(MotionEvent e) {

        }

        @Override
        public boolean onFling(MotionEvent down, MotionEvent event, float velocityX, float velocityY) {
            if (big) {
                scroller.fling((int) offsetX, (int) offsetY, (int) velocityX, (int) velocityY,
                        - (int) (bitmap.getWidth() * bigScale - getWidth()) / 2,
                        (int) (bitmap.getWidth() * bigScale - getWidth()) / 2,
                        - (int) (bitmap.getHeight() * bigScale - getHeight()) / 2,
                        (int) (bitmap.getHeight() * bigScale - getHeight()) / 2);

                postOnAnimation(henFlingRunner);
            }
            return false;
        }

        @Override
        public boolean onSingleTapConfirmed(MotionEvent e) {
            return false;
        }

        @Override
        public boolean onDoubleTap(MotionEvent e) {
            big = !big;
            if (big) {
                offsetX = (e.getX() - getWidth() / 2f) - (e.getX() - getWidth() / 2) * bigScale / smallScale;
                offsetY = (e.getY() - getHeight() / 2f) - (e.getY() - getHeight() / 2) * bigScale / smallScale;
                fixOffsets();
                getScaleAnimator().start();
            } else {
                getScaleAnimator().reverse();
            }
            return false;
        }

        @Override
        public boolean onDoubleTapEvent(MotionEvent e) {
            return false;
        }
    }

    private int fixOffsets() {
        offsetX = Math.min(offsetX, (bitmap.getWidth() * bigScale - getWidth()) / 2);
        offsetX = Math.max(offsetX, - (bitmap.getWidth() * bigScale - getWidth()) / 2);
        int result = 0;
        if (offsetY < -(bitmap.getHeight() * bigScale - getHeight()) / 2) {
            result = (int) (- (bitmap.getHeight() * bigScale - getHeight()) / 2 - offsetY);
        } else if (offsetY > (bitmap.getHeight() * bigScale - getHeight()) / 2) {
            result = (int) ((bitmap.getHeight() * bigScale - getHeight()) / 2 - offsetY);
        }
        offsetY = Math.min(offsetY, (bitmap.getHeight() * bigScale - getHeight()) / 2);
        offsetY = Math.max(offsetY, - (bitmap.getHeight() * bigScale - getHeight()) / 2);
        return result;
    }

    class HenFlingRunner implements Runnable {

        @Override
        public void run() {
            if (scroller.computeScrollOffset()) {
                offsetX = scroller.getCurrX();
                offsetY = scroller.getCurrY();
                invalidate();
                postOnAnimation(this);
            }
        }
    }

    class HenScaleListener implements ScaleGestureDetector.OnScaleGestureListener {
        float initialScale;

        @Override
        public boolean onScale(ScaleGestureDetector detector) {
            currentScale = initialScale * detector.getScaleFactor();
            invalidate();
            return false;
        }

        @Override
        public boolean onScaleBegin(ScaleGestureDetector detector) {
            initialScale = currentScale;
            return true;
        }

        @Override
        public void onScaleEnd(ScaleGestureDetector detector) {

        }
    }
}
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
//        setContentView(R.layout.drag_helper_grid_view);
//        setContentView(R.layout.drag_listener_grid_view);
//        setContentView(R.layout.drag_to_collect);
//        setContentView(R.layout.drag_up_down);
//        setContentView(R.layout.scroll_view);
//        setContentView(R.layout.nested_scroll_view);
        setContentView(R.layout.nested_scalable_image_view);

    }
}

本文地址:https://blog.csdn.net/aha_jasper/article/details/110672617

相关标签: Android