Android中的拖拽及嵌套滑动
程序员文章站
2022-03-05 14:23:42
15_Android中的拖拽及嵌套滑动拖拽和嵌套滑动拖拽OnDragListenerViewDragHelper为什么要这两个东西,而不是一个?嵌套滑动嵌套滑动的场景拖拽和嵌套滑动拖拽OnDragListener通过startDrag()来启动拖拽用 setOnDragListener()来监听OnDragListener 内部只有一个方法:onDrag()onDragEvent()方法也会收到拖拽回调(界面中的每个View都会收到)ViewDragHelper需要创建一个 V...
15_Android中的拖拽及嵌套滑动
拖拽和嵌套滑动
拖拽
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展开的时候:
- 场景一:NestedScrollView
-
解决方案:实现策略一一父View、子View谁来消费事件可以实时协商
-
实现?
- 其实大多数场景,SDK就解决了
- 案例1:ScrollView嵌套:没法滑动
- 案例2:换成NestedScrollView :可以滑动
- 自己实现?
- 案例3:按照课上代码,实现NestedScrollingChild2接口来实现自定义的嵌套滑动逻辑
- 其实大多数场景,SDK就解决了
-
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中判断listview是否滑动到顶部和底部的实现方法
-
详解Android中图片的三级缓存及实例
-
Android开发中4个常用的工具类【Toast、SharedPreferences、网络及屏幕操作】
-
Android中3种全屏方法及3种去掉标题栏的方法
-
Android中RecyclerView 滑动时图片加载的优化
-
如何在Android中实现左右滑动的指引效果
-
如何在Android中实现渐显按钮的左右滑动效果
-
Android应用中Back键的监听及处理实例
-
Android中onInterceptTouchEvent、dispatchTouchEvent及onTouchEvent的调用顺序及内部原理详解
-
策略模式及Android源码中的应用解析