能够监听是否滑动到底部和顶部的scrollView
package com.danale.cloud.ui.widget;
import com.danale.cloud.utils.LogUtil;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.widget.ScrollView;
public class BottomTopScrollView extends ScrollView {
public BottomTopScrollView(Context context, AttributeSet attrs,
int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public BottomTopScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public BottomTopScrollView(Context context) {
super(context);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int childCount = getChildCount();
//计算子view的高度
childHeightCount = 0;
for(int i = 0 ; i < childCount ; i++){
View childAt = getChildAt(i);
int measuredHeight = childAt.getMeasuredHeight();
LayoutParams params = (LayoutParams) childAt.getLayoutParams();
childHeightCount += measuredHeight + params.topMargin + params.bottomMargin;
}
}
private int childHeightCount;
/**
* clampedX : 表示在x方向是否可以移动 ,能移动false ,不能移动 true
* clampedY : 表示在y方向是否可以移动
*
* 通过上面的参数和scrollxx参数就可以知道其是否滑到底部和滑到顶部
*/
@Override
protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX,boolean clampedY) {
super.onOverScrolled(scrollX, scrollY, clampedX, clampedY);
LogUtil.e("BottomTopScrollView", "childHeightCount : "+childHeightCount);
if(scrollY <= 0){
mScrollState = ScrollState.TOP_STATE;
if(scrollY >= childHeightCount - getMeasuredHeight()){
mScrollState = ScrollState.BOTH_STATE;//表示scrollview 要比childs要高,不需要scrollview 去滑动
}
}else if(scrollY > 0){
mScrollState = ScrollState.SCROLL_STATE;
if(scrollY >= childHeightCount - getMeasuredHeight()){
mScrollState = ScrollState.BOTTOM_STATE;//表示scrollview 要比childs要高,不需要scrollview 去滑动
}
}
LogUtil.e("BottomTopScrollView", "scrollstate : " + mScrollState + "/ scrollY:" + scrollY);
}
private ScrollState mScrollState;
public ScrollState getScrollState(){
LogUtil.e("BottomTopScrollView", "childHeightCount : "+childHeightCount + "/ measuredHeight : " + getMeasuredHeight());
if(childHeightCount <= getMeasuredHeight()){
return ScrollState.BOTH_STATE;
}
return mScrollState;
}
/** 滑动位置的状态 TOP_STATE 到达顶部, BOTTOM_STATE 到达底部 , BOTH_STATE 即到达底部也达到了顶部, SCROLL_STATE 可滑动状态*/
public enum ScrollState{
TOP_STATE , BOTTOM_STATE , BOTH_STATE , SCROLL_STATE;
}
}
PullToRefreshScrollView
package com.danale.cloud.ui.widget;
import com.danale.cloud.R;
import com.danale.cloud.ui.widget.BottomTopScrollView.ScrollState;
import com.danale.cloud.utils.LogUtil;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.widget.LinearLayout;
import android.widget.Scroller;
/**
* 类似于qq中的界面,能够上下的拉动
*/
public class PullToRefreshScrollView extends LinearLayout {
private BottomTopScrollView mScrollView;
private float downY;
private Scroller mScroller;
private int mScaledTouchSlop;
private boolean isInControl = false;
private final int REFRESH_MIN_SCROLL_HEIGH = 100;
public PullToRefreshScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
mScroller = new Scroller(context);
mScaledTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
View view = findViewById(R.id.danale_cloud_id_pull_to_refresh_scrollview);
if(!(view instanceof BottomTopScrollView)){
throw new IllegalArgumentException("must be used by scrollview to viewgroup's child view !");
}
mScrollView = (BottomTopScrollView) view;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
LayoutParams layoutParams = (LayoutParams) mScrollView.getLayoutParams();
layoutParams.height = getMeasuredHeight() - getPaddingTop() - getPaddingBottom();
layoutParams.weight = getMeasuredWidth() - getPaddingLeft() - getPaddingRight();
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
LogUtil.e("PullToRefreshScrollView", "onTouchEvent " + event.getX());
switch (action) {
case MotionEvent.ACTION_DOWN:
downY = event.getY();
return true;
case MotionEvent.ACTION_MOVE:
float moveY = event.getY();
float dY = (moveY - downY)/1.5f;
scrollBy(0, (int) -dY);
downY = moveY;
isInControl = false;
break;
case MotionEvent.ACTION_UP:
float downY = event.getY();
float scrollY = getScrollY();
handlerPullToRefresh(scrollY);
mScroller.startScroll(0, (int)scrollY, 0, (int)-scrollY, (int)Math.abs(scrollY));
invalidate();
break;
default:
break;
}
return super.onTouchEvent(event);
}
private void handlerPullToRefresh(float scrollY) {
if(scrollY < -REFRESH_MIN_SCROLL_HEIGH && mListener != null){
mListener.onRefresh();
}
}
@Override
public void computeScroll() {
if(mScroller.computeScrollOffset()){
scrollTo(0, mScroller.getCurrY());
invalidate();
}
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
downY = ev.getX();
break;
case MotionEvent.ACTION_MOVE:
float moveY = ev.getY();
float dY = moveY - downY;
int scrollY = getScrollY();
if(Math.abs(dY) > mScaledTouchSlop){
if(!isInControl && dY > 0 && (ScrollState.TOP_STATE.equals(mScrollView.getScrollState()) ||
ScrollState.BOTH_STATE.equals(mScrollView.getScrollState())) && scrollY == 0){ // 上滑 ,满足该条件可下拉刷新
isInControl = true;
ev.setAction(MotionEvent.ACTION_CANCEL);
MotionEvent ev2 = MotionEvent.obtain(ev);
dispatchTouchEvent(ev);
ev2.setAction(MotionEvent.ACTION_DOWN);
return dispatchTouchEvent(ev2);
}else if(!isInControl && dY < 0 &&(ScrollState.BOTTOM_STATE.equals(mScrollView.getScrollState()) ||
ScrollState.BOTH_STATE.equals(mScrollView.getScrollState())) && scrollY == 0){//下滑,满足该条件可上拉加载
isInControl = true;
ev.setAction(MotionEvent.ACTION_CANCEL);
MotionEvent ev2 = MotionEvent.obtain(ev);
dispatchTouchEvent(ev);
ev2.setAction(MotionEvent.ACTION_DOWN);
return dispatchTouchEvent(ev2);
}
}
break;
default:
break;
}
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
LogUtil.e("PullToRefreshScrollView","onInterceptTouchEvent");
int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
downY = ev.getY();
break;
case MotionEvent.ACTION_MOVE:
float moveY = ev.getY();
float dY = moveY - downY;
if(Math.abs(dY) > mScaledTouchSlop){
if(dY > 0 && (ScrollState.TOP_STATE.equals(mScrollView.getScrollState()) ||
ScrollState.BOTH_STATE.equals(mScrollView.getScrollState()))){ // 上滑 ,满足该条件可下拉刷新
return true;
}else if(dY < 0 &&(ScrollState.BOTTOM_STATE.equals(mScrollView.getScrollState()) ||
ScrollState.BOTH_STATE.equals(mScrollView.getScrollState()))){//下滑,满足该条件可上拉加载
return true;
}
}
break;
default:
break;
}
return super.onInterceptTouchEvent(ev);
}
public interface OnPullToRefreshListener{
public void onRefresh();
}
private OnPullToRefreshListener mListener;
public void setOnPullToRefreshListener(OnPullToRefreshListener listener){
this.mListener = listener;
}
}
布局相关的问题
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<com.example.drawable_ui.view.PullToRefreshScrollView
android:id="@+id/pull_to_refresh_scroll"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<com.example.drawable_ui.view.BottomTopScrollView
android:id="@id/id_pull_to_refresh_scrollview"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<TextView
android:layout_width="match_parent"
android:layout_height="200dp"
android:background="#ffa22c"
android:gravity="center_vertical"
android:text="sdfadaddddad"
android:clickable="true"
android:onClick="clickTv3" />
若干个textview....
</LinearLayout>
</com.example.drawable_ui.view.BottomTopScrollView>
</com.example.drawable_ui.view.PullToRefreshScrollView>
</LinearLayout>
values 文件加下 ids.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item name="id_pull_to_refresh_scrollview" type="id"></item>
</resources>
在代码中设置setOnPullToRefreshListener(OnPullToRefreshListener listener),就可以实现下拉刷新了