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),就可以实现下拉刷新了