Android自定义view-弹性ScrollView(下)

前言:

继上篇博客Android自定义view-弹性ScrollView(上)介绍的一种实现弹性ScrollView的方法后,有没有觉得处理touch事件太过于麻烦了委屈好吧,我也觉得麻烦,决定就用现在比较火的ViewDragHelper去实现事件的控制。这篇博文的重点不在于ViewDragHelper的介绍,而是利用ViewDragHelper,所以会在全篇穿插着介绍ViewDragHelper,至于详细的介绍,会有机会写博客来介绍的,当然你也可以多看google官方的文档,那可是权威。

正文:

构造和准备工作都和前篇的一样,没什么区别,最主要的是在dispatchTouchEvent(MotionEvent ev) 上,我们把事件的控制权交给了ViewDragHelper:

@Override
	public boolean onInterceptTouchEvent(MotionEvent ev) {
		boolean shouldInterceptTouchEvent = mViewDragHelper.shouldInterceptTouchEvent(ev);
		return shouldInterceptTouchEvent;

	}

	@Override
	public boolean onTouchEvent(MotionEvent ev) {
		mViewDragHelper.processTouchEvent(ev);
		return true;
	}
通过mViewDragHelper.shouldInterceptTouchEvent(ev)去拦截分发touch事件,通过mViewDragHelper.processTouchEvent(ev)来处理touch事件。至于怎么处理的,目前你不需要清楚,肯定比你写的事件处理要全面。

现在我们把主要的精力放在如何去ViewDragHelper上。

mViewDragHelper = ViewDragHelper.create(this, 1.0f, new Callback() {

				// 控制移动的垂直范围
				@Override
				public int clampViewPositionVertical(View child, int top, int dy) {
					return top;
				}

				// 捕获的childView
				@Override
				public boolean tryCaptureView(View child, int pointerId) {
					return child == contentView;
				}

				/**
				 * view位置改变时
				 */
				@Override
				public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
					canPullDown = top > originalRect.top ? true : false;
					canPullUp = top < -1 * (originalRect.height() - getHeight()) ? true : false;
				}

				/**
				 * 手指释放时view回到初始状态
				 */
				@Override
				public void onViewReleased(View releasedChild, float xvel, float yvel) {
					if (releasedChild == contentView) {
						if (canPullDown) {
							mViewDragHelper.settleCapturedViewAt(originalRect.left, originalRect.top);
						}
						if (canPullUp) {
							mViewDragHelper.settleCapturedViewAt(originalRect.left, -(originalRect.height() - getHeight()));
						}
						invalidateView();
					}
				}

			});
诺,这是我们的主要逻辑代码,和之前的相比是不是特别简单清晰。对,就是这么简单。

对于ViewDragHelper对象的获取我们是在onFinishInflate()中获取的,为什么不放在构造器里呢?自己去思考一下吧。

我们不难发现ViewDragHelper的callback回调接口中有很多方法

• onViewDragStateChanged
当ViewDragHelper状态发生变化时回调(IDLE,DRAGGING,SETTING[自动滚动时])
• onViewPositionChanged
当captureview的位置发生改变时回调
• onViewCaptured
当captureview被捕获时回调
• onViewReleased 
• onEdgeTouched
当触摸到边界时回调。
• onEdgeLock
true的时候会锁住当前的边界,false则unLock。
• onEdgeDragStarted 
• getOrderedChildIndex
改变同一个坐标(x,y)去寻找captureView位置的方法。(具体在:findTopChildUnder方法中)
• getViewHorizontalDragRange 
• getViewVerticalDragRange 
• tryCaptureView 
• clampViewPositionHorizontal 

clampViewPositionVertical 

这里有个注意点,在onViewReleased方法中,调用mViewDragHelper.settleCapturedViewAt(originalRect.left, originalRect.top);此方法需要重写

@Override
	public void computeScroll() {
		if (mViewDragHelper.continueSettling(true)) {
			invalidateView();
		}
	}
大家可以试试把这个方法不复写,会不会有效果。还有一点就是invalidateView()是什么方法?怎么没有这个方法,哈哈,这是自己写的一个方法,主要是重绘view的,我们进行了一定的判断。

// 重绘view
	private void invalidateView() {
		if (Looper.getMainLooper() == Looper.myLooper()) {
			invalidate();
		} else {
			postInvalidate();
		}
	}

有人一定有疑惑,为什么要这么写,其实大家不妨去查一下invalidate和postInvalidate的区别就明白了,你一定会收获另一个知识点的。

贴一下全部代码:

VHFlexibleScrollView.java:

package com.beyole.view;

import android.content.Context;
import android.graphics.Rect;
import android.os.Looper;
import android.support.v4.widget.ViewDragHelper;
import android.support.v4.widget.ViewDragHelper.Callback;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ScrollView;

public class VHFlexibleScrollView extends ScrollView {
	private static final String TAG = "FLEXIBLESCROLLVIEW";
	// ScrollView唯一的一个子view
	private View contentView;
	// 用于记录正常的布局位置
	private Rect originalRect = new Rect();
	// 记录手指按下时是否可以下拉
	private boolean canPullDown = false;
	// 记录手指按下时是否可以上拉
	private boolean canPullUp = false;
	private ViewDragHelper mViewDragHelper;

	public VHFlexibleScrollView(Context context) {
		this(context, null);
	}

	public VHFlexibleScrollView(Context context, AttributeSet attrs) {
		this(context, attrs, 0);
	}

	public VHFlexibleScrollView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);

	}

	/**
	 * 在加载完xml后获取唯一的一个childview
	 */
	@Override
	protected void onFinishInflate() {
		if (getChildCount() > 0) {
			// 获取第一个childview
			contentView = getChildAt(0);
			mViewDragHelper = ViewDragHelper.create(this, 1.0f, new Callback() {

				// 控制移动的垂直范围
				@Override
				public int clampViewPositionVertical(View child, int top, int dy) {
					return top;
				}

				// 捕获的childView
				@Override
				public boolean tryCaptureView(View child, int pointerId) {
					return child == contentView;
				}

				/**
				 * view位置改变时
				 */
				@Override
				public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
					canPullDown = top > originalRect.top ? true : false;
					canPullUp = top < -1 * (originalRect.height() - getHeight()) ? true : false;
				}

				/**
				 * 手指释放时view回到初始状态
				 */
				@Override
				public void onViewReleased(View releasedChild, float xvel, float yvel) {
					if (releasedChild == contentView) {
						if (canPullDown) {
							mViewDragHelper.settleCapturedViewAt(originalRect.left, originalRect.top);
						}
						if (canPullUp) {
							mViewDragHelper.settleCapturedViewAt(originalRect.left, -(originalRect.height() - getHeight()));
						}
						invalidateView();
					}
				}

			});
		}
	}

	@Override
	protected void onLayout(boolean changed, int l, int t, int r, int b) {
		super.onLayout(changed, l, t, r, b);
		if (contentView == null)
			return;
		// scrollview唯一的一个子view的位置信息,这个位置信息在整个生命周期中保持不变
		originalRect.set(contentView.getLeft(), contentView.getTop(), contentView.getRight(), contentView.getBottom());

	}

	@Override
	public boolean onInterceptTouchEvent(MotionEvent ev) {
		boolean shouldInterceptTouchEvent = mViewDragHelper.shouldInterceptTouchEvent(ev);
		return shouldInterceptTouchEvent;

	}

	@Override
	public boolean onTouchEvent(MotionEvent ev) {
		mViewDragHelper.processTouchEvent(ev);
		return true;
	}

	@Override
	public void computeScroll() {
		if (mViewDragHelper.continueSettling(true)) {
			invalidateView();
		}
	}

	// 重绘view
	private void invalidateView() {
		if (Looper.getMainLooper() == Looper.myLooper()) {
			invalidate();
		} else {
			postInvalidate();
		}
	}
}
MainActivity.java:

package com.beyole.flexiblescrollview;

import android.app.Activity;
import android.os.Bundle;

public class MainActivity extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
	}

}
activity_main.xml:

<RelativeLayout 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" >

    <com.beyole.view.VHFlexibleScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent" >

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

            <ImageView
                android:layout_width="match_parent"
                android:layout_height="200.0dip"
                android:scaleType="fitXY"
                android:src="@drawable/img1" />

            <ImageView
                android:layout_width="match_parent"
                android:layout_height="200.0dip"
                android:scaleType="fitXY"
                android:src="@drawable/img2" />

            <ImageView
                android:layout_width="match_parent"
                android:layout_height="200.0dip"
                android:scaleType="fitXY"
                android:src="@drawable/img3" />
        </LinearLayout>
    </com.beyole.view.VHFlexibleScrollView>

</RelativeLayout>

下载地址:http://download.csdn.net/detail/smarticeberg/9439380

Github地址:https://github.com/xuejiawei/beyole_FlexibleScrollView,欢迎fork or star


题外话:

android交流群:279031247(广告勿入)

新浪微博:SmartIceberg




  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,针对你的问题,我可以提供一些思路和参考代码。 首先,实现 Android 中的自定义吸顶,可以采用两种方式: 1. 使用固定顶部控件 + ScrollView + ViewPager 的方式实现。 这种方式比较常见,具体实现思路如下: (1)在布局文件中定义一个固定在顶部的控件,例如 LinearLayout,将其设置为可见性为 gone,即不可见。 (2)在 ScrollView 中添加 ViewPager,将其填充满整个布局,用于滑动切换不同的子页面。 (3)监听 ScrollView 的滑动事件,在滑动到一定位置时,将顶部控件设置为可见,实现吸顶效果。 具体实现代码可以参考以下链接: - https://www.jianshu.com/p/4f28a4d0c3b1 - https://www.cnblogs.com/xiaohuafice/p/11050662.html 2. 使用 CoordinatorLayout + AppBarLayout 实现。 这种方式相对来说比较简单,具体实现思路如下: (1)在布局文件中使用 CoordinatorLayout 作为根布局,并添加一个 AppBarLayout 作为子布局。 (2)在 AppBarLayout 中添加一个 Toolbar 控件作为顶部的固定控件,将其设置为可见性为 gone。 (3)在子页面中,使用 NestedScrollView 作为滑动的容器,并将其放在 AppBarLayout 的下面。 (4)监听 NestedScrollView 的滑动事件,在滑动到一定位置时,将 Toolbar 设置为可见,实现吸顶效果。 具体实现代码可以参考以下链接: - https://www.jianshu.com/p/5d0f7e7e7c97 - https://www.jianshu.com/p/5d0f7e7e7c97 希望以上内容能够帮助到你,有什么问题可以再和我交流哦!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值