您的位置:首页 > 移动开发 > Android开发

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

2016-02-22 17:05 302 查看
前言:

继上篇博客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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: