之前写过的一个APP每日美图,支持最低的API版本是7,也就是2.1以上系统都没有问题,过了一年多没有维护了,前不久用安装有最新的4.2.2系统的手机测试,发现瀑布流图墙不滚动了,这里面其实是自定义的ScrollView中OnTouchListenr捕获手指事件后分发流程的问题,早期的实现有好多方式,大致都是通过在ScrollView上实现自定义的onTouchListener,重写onTouch()方法,把事件进行捕获,但是在API17以上不知道为啥就总是失败,会被ScrollView里面的LinearLayout给屏蔽掉了。
我重新梳理了这部分流程,用实现onGestureListener的自定义ScrollView方式搞定,因为我还需要在这个里面得到手指滚动的方向,这就是曾经一度很流行的LazyScrollView的改进版,支持判断方向的版本,经我测试,在API7-17的版本上都能正常滚动和捕获事件。
下面是ScrollView代码部分,layout和Activity部分就不贴了,和大部分的实现类似。
package com.poyy.utils;
import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.view.GestureDetector.OnGestureListener;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ScrollView;
public class MyScrollView extends ScrollView implements OnGestureListener {
private View view;
private Handler handler;
private GestureDetector mGestureDetector;
public MyScrollView(Context context) {
super(context);
}
public MyScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyScrollView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
protected int computeVerticalScrollOffset() {
return super.computeVerticalScrollOffset();
}
protected int computeVerticalScrollRange() {
return super.computeVerticalScrollRange();
}
public void getView(){
view = getChildAt(0);
if (view != null) init();
}
@SuppressLint(“HandlerLeak”)
private void init() {
mGestureDetector = new GestureDetector(this);
handler = new Handler() {
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 1:
if (view.getMeasuredHeight() <= getScrollY() + getHeight() + 150) {
//Utils.doLog("onBottom: " + view.getMeasuredHeight() + " = " + getScrollY() + " + " + getHeight());
if (onScrollListener != null) {
onScrollListener.onBottom();
}
} else {
if (onScrollListener != null) {
onScrollListener.onScroll(1);
}
}
break;
case 2:
if (getScrollY() < 50) {
//Utils.doLog("onTop(): " + getScrollY());
if (onScrollListener != null) {
onScrollListener.onTop();
}
}
if (onScrollListener != null) {
onScrollListener.onScroll(2);
}
break;
default:
break;
}
}
};
}
// 定义在Activity里面实现的接口,用于回调接收来自scrollView的滚动事件
public interface OnScrollListener {
void onBottom();
void onTop();
void onScroll(int direct);
}
private OnScrollListener onScrollListener;
public void setOnScrollListener(OnScrollListener onScrollListener){
this.onScrollListener = onScrollListener;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
/**
* 此处是关键,onTouchEvent是ScrollView的手指动作处理事件
* 在这里面捕获事件后传递给GestureDector的onTouchEvent来处理
* 从而实现onFling, onScroll等事件的处理
*/
mGestureDetector.onTouchEvent(event);
return super.onTouchEvent(event);
}
//////////////////////// 以下为 onGestureListener 的方法 //////////////////////
public boolean onDown(MotionEvent e) {
return false;
}
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
if (velocityY < 0 ) { //手指向上,屏幕向上滑动
handler.sendMessageDelayed(handler.obtainMessage(1), 100);
} else if (velocityY > 0) { //手指向下,屏幕向下滑动
handler.sendMessageDelayed(handler.obtainMessage(2), 200);
}
return false;
}
public void onLongPress(MotionEvent e) {
}
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
return false;
}
public void onShowPress(MotionEvent e) {
}
public boolean onSingleTapUp(MotionEvent e) {
return false;
}
}
新版的每日美图将在最近几天发布,改进一些bug并提升性能,后台service部分的逻辑也会大大改进。
Leave a Reply