懒加载的Scrollview

要实现一个功能:当Scrollview滑动到最底端的时候需要触发事件加载其他数据。很多人都以为ScrollView可以像ListViev那样setOnScrollListener,其实沒那么简单,因为ScrollView压根就没有该接口,在baidu上兜了一圈没有找到合适的答案,没办法只能google去了,居然一下子解决了这个问题,还是老外比较牛,呵呵,这是我访问的网址:
[url]http://stackoverflow.com/questions/2864563/how-do-i-know-that-the-scrollview-is-already-scrolled-to-the-bottom[/url]

注意,如果数据不满一页的话,会执行onBottom方法!通常要使用懒加载的话数据都会超过一页,所以我沒仔细考虑这个问题!

我把ScrollView封装成类了,源码如下:

package com.ql.view;

import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ScrollView;

public class LazyScrollView extends ScrollView{
private static final String tag="LazyScrollView";
private Handler handler;
private View view;
public LazyScrollView(Context context) {
super(context);
// TODO Auto-generated constructor stub
}
public LazyScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
}
public LazyScrollView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// TODO Auto-generated constructor stub
}
//这个获得总的高度
public int computeVerticalScrollRange(){
return super.computeHorizontalScrollRange();
}
public int computeVerticalScrollOffset(){
return super.computeVerticalScrollOffset();
}
private void init(){

this.setOnTouchListener(onTouchListener);
handler=new Handler(){
@Override
public void handleMessage(Message msg) {
// process incoming messages here
super.handleMessage(msg);
switch(msg.what){
case 1:
if(view.getMeasuredHeight() <= getScrollY() + getHeight()) {
if(onScrollListener!=null){
onScrollListener.onBottom();
}

}else if(getScrollY()==0){
if(onScrollListener!=null){
onScrollListener.onTop();
}
}
else{
if(onScrollListener!=null){
onScrollListener.onScroll();
}
}
break;
default:
break;
}
}
};

}

OnTouchListener onTouchListener=new OnTouchListener(){

@Override
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_UP:
if(view!=null&&onScrollListener!=null){
handler.sendMessageDelayed(handler.obtainMessage(1), 200);
}
break;

default:
break;
}
return false;
}

};

/**
* 获得参考的View,主要是为了获得它的MeasuredHeight,然后和滚动条的ScrollY+getHeight作比较。
*/
public void getView(){
this.view=getChildAt(0);
if(view!=null){
init();
}
}

/**
* 定义接口
* @author admin
*
*/
public interface OnScrollListener{
void onBottom();
void onTop();
void onScroll();
}
private OnScrollListener onScrollListener;
public void setOnScrollListener(OnScrollListener onScrollListener){
this.onScrollListener=onScrollListener;
}
}


用的时候也很简单,通常这样使用:

scrollView=(LazyScrollView)findViewById(R.id.scrollView);
scrollView.getView();
scrollView.setOnScrollListener(new OnScrollListener() {

@Override
public void onTop() {
// TODO Auto-generated method stub
Log.d(tag,"------滚动到最上方------");
}

@Override
public void onScroll() {
// TODO Auto-generated method stub
Log.d(tag,"没有到最下方,也不是最上方");
}

@Override
public void onBottom() {
// TODO Auto-generated method stub
Log.d(tag,"------滚动到最下方------");
}
});

感激我吧,我呕心沥血才出来了这么个类。呵呵。

重写onScrollChanged()的模式

import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ScrollView;

/**
* BorderScrollView
* <ul>
* <li>onTop and onBottom response ScrollView</li>
* <li>you can {@link #setOnBorderListener(OnBorderListener)} to set your top and bottom response</li>
* </ul>
*
* @author trinea@trinea.cn 2013-5-21
*/
public class BorderScrollView extends ScrollView {

private OnBorderListener onBorderListener;
private View contentView;

public BorderScrollView(Context context) {
super(context);
}

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

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

@Override
protected void onScrollChanged(int x, int y, int oldx, int oldy) {
super.onScrollChanged(x, y, oldx, oldy);
doOnBorderListener();
}

public void setOnBorderListener(final OnBorderListener onBorderListener) {
this.onBorderListener = onBorderListener;
if (onBorderListener == null) {
return;
}

if (contentView == null) {
contentView = getChildAt(0);
}
}

/**
* OnBorderListener, Called when scroll to top or bottom
*
* @author <a href="http://www.trinea.cn" target="_blank">Trinea</a> 2013-5-22
*/
public static interface OnBorderListener {

/**
* Called when scroll to bottom
*/
public void onBottom();

/**
* Called when scroll to top
*/
public void onTop();
}

private void doOnBorderListener() {
if (contentView != null && contentView.getMeasuredHeight() <= getScrollY() + getHeight()) {
if (onBorderListener != null) {
onBorderListener.onBottom();
}
} else if (getScrollY() == 0) {
if (onBorderListener != null) {
onBorderListener.onTop();
}
}
}
}


顺便记一下老外使用fullScroll的做法。当然也可以直接fullScroll而不需要放入post()。

scrollView.post(new Runnable() {
@Override
public void run() {
scrollView.fullScroll(View.FOCUS_DOWN);
}
});

只要把fullScroll改成scrollTo就可以做一个书签效果了:
[url]http://yangsongjing.iteye.com/blog/1855063[/url]

Android-ObservableScrollView
[url]https://github.com/ksoichiro/Android-ObservableScrollView[/url]

[color=red]在HorizontalScrollView中使用ScrollView相互影响问题的解决办法:[/color]
On my ScrollView, I needed to override the onInterceptTouchEvent method to only intercept the touch event if the Y motion is > the X motion. It seems like the default behavior of a ScrollView is to intercept the touch event whenever there is ANY Y motion. So with the fix, the ScrollView will only intercept the event if the user is deliberately scrolling in the Y direction and in that case pass off the ACTION_CANCEL to the children.

Here is the code for my Scroll View class that contains the HorizontalScrollView:

public class CustomScrollView extends ScrollView {
private GestureDetector mGestureDetector;
View.OnTouchListener mGestureListener;

public CustomScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
mGestureDetector = new GestureDetector(new YScrollDetector());
setFadingEdgeLength(0);
}

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return super.onInterceptTouchEvent(ev) && mGestureDetector.onTouchEvent(ev);
}

// Return false if we're scrolling in the x direction
class YScrollDetector extends SimpleOnGestureListener {
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
if(Math.abs(distanceY) > Math.abs(distanceX)) {
return true;
}
return false;
}
}
}


[url="http://fariytale.iteye.com/category/184642"]android监听ScrollView滑动停止[/url]

[color=red]在ScrollView中嵌入GridView[/color]
[url]http://fariytale.iteye.com/blog/1420254[/url]
做android程序开发的都知道,不能在一个拥有Scrollbar的组件中嵌入另一个拥有Scrollbar的组件,因为这不科学,会混淆滑动事件,导致只显示一到两行数据。那么就换一种思路,首先让子控件的内容全部显示出来,禁用了它的滚动。如果超过了父控件的范围则显示父控件的scrollbar滚动显示内容,思路是这样,一下是代码。
具体的方法是自定义GridView组件,继承自GridView。重载onMeasure方法:

public class MyGridView extends GridView
{
public MyGridView(android.content.Context context,
android.util.AttributeSet attrs)
{
super(context, attrs);
}

/**
* 设置不滚动
*/
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,
MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, expandSpec);

}

}

其中onMeasure函数决定了组件显示的高度与宽度;
makeMeasureSpec函数中第一个函数决定布局空间的大小,第二个参数是布局模式
MeasureSpec.AT_MOST的意思就是子控件需要多大的控件就扩展到多大的空间
之后在ScrollView中添加这个组件就OK了,同样的道理,ListView也适用。

[color=red]滚动监听的ScrollView[/color]

import android.content.Context;
import android.util.AttributeSet;
import android.widget.ScrollView;

public class NotifyingScrollView extends ScrollView {

public interface OnScrollChangedListener {
void onScrollChanged(ScrollView who, int l, int t, int oldl, int oldt);
}

private OnScrollChangedListener mOnScrollChangedListener;

public NotifyingScrollView(Context context) {
super(context);
}

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

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

@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
if (mOnScrollChangedListener != null) {
mOnScrollChangedListener.onScrollChanged(this, l, t, oldl, oldt);
}
}

public void setOnScrollChangedListener(OnScrollChangedListener listener) {
mOnScrollChangedListener = listener;
}

}



[color=red]ScrollView也可以实现OnTouchListener来监听是否滑动到最底部[/color]

import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.widget.ScrollView;
import android.app.Activity;
/**
* Demo描述:
* 监听ScrollView滑动到顶端和底部
*
* 注意事项:
* 1 mScrollView.getChildAt(0).getMeasuredHeight()表示:
* ScrollView所占的高度.即ScrollView内容的高度.常常有一
* 部分内容要滑动后才可见,这部分的高度也包含在了
* mScrollView.getChildAt(0).getMeasuredHeight()中
*
* 2 view.getScrollY表示:
* ScrollView顶端已经滑出去的高度
*
* 3 view.getHeight()表示:
* ScrollView的可见高度
*
*/
public class MainActivity extends Activity {
private ScrollView mScrollView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
init();
}
private void init(){
mScrollView=(ScrollView) findViewById(R.id.scrollView);
mScrollView.setOnTouchListener(new TouchListenerImpl());
}
private class TouchListenerImpl implements OnTouchListener{
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
switch (motionEvent.getAction()) {
case MotionEvent.ACTION_DOWN:

break;
case MotionEvent.ACTION_MOVE:
int scrollY=view.getScrollY();
int height=view.getHeight();
int scrollViewMeasuredHeight=mScrollView.getChildAt(0).getMeasuredHeight();
if(scrollY==0){
System.out.println("滑动到了顶端 view.getScrollY()="+scrollY);
}
if((scrollY+height)==scrollViewMeasuredHeight){
System.out.println("滑动到了底部 scrollY="+scrollY);
System.out.println("滑动到了底部 height="+height);
System.out.println("滑动到了底部 scrollViewMeasuredHeight="+scrollViewMeasuredHeight);
}
break;

default:
break;
}
return false;
}

};
}


在滚动的视图观测滚动事件的Android库:Android-ObservableScrollView
[url]http://www.open-open.com/lib/view/open1415854429070.html[/url]



import android.content.Context;
import android.util.AttributeSet;
import android.widget.ScrollView;

public class ObservableScrollView extends ScrollView {

private ScrollViewListener scrollViewListener = null;

public ObservableScrollView(Context context) {
super(context);
}

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

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

public void setScrollViewListener(ScrollViewListener scrollViewListener) {
this.scrollViewListener = scrollViewListener;
}

@Override
protected void onScrollChanged(int x, int y, int oldx, int oldy) {
super.onScrollChanged(x, y, oldx, oldy);
if (scrollViewListener != null) {
scrollViewListener.onScrollChanged(this, x, y, oldx, oldy);
}
}

}


网上说的方法乱七八糟,能用的就是自己算高度,其实sdk-9中,ScrollView已经加入了一个方法,能监听到是否已经不能滚动,稍加处理,就可以监听是否滑到底部了。

先上自定义的ScrollView方法:

import android.content.Context;
import android.util.AttributeSet;
import android.widget.ScrollView;

public class BottomScrollView extends ScrollView {

private OnScrollToBottomListener onScrollToBottom;

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

public BottomScrollView(Context context) {
super(context);
}

@Override
protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX,
boolean clampedY) {
super.onOverScrolled(scrollX, scrollY, clampedX, clampedY);
if(scrollY != 0 && null != onScrollToBottom){
onScrollToBottom.onScrollBottomListener(clampedY);
}
}

public void setOnScrollToBottomLintener(OnScrollToBottomListener listener){
onScrollToBottom = listener;
}

public interface OnScrollToBottomListener{
public void onScrollBottomListener(boolean isBottom);
}
}

调用方法:

BottomScrollView scroll = (BottomScrollView)findViewById(R.id.id_scroll);
scroll.setOnScrollToBottomLintener(new OnScrollToBottomListener() {

@Override
public void onScrollBottomListener(boolean isBottom) {
// TODO Auto-generated method stub
Log.e("SCROLLVIEW", isBottom + "");

}
});
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值