public class PullScrollView extends RelativeLayout {
public PullScrollView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}
public PullScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public PullScrollView(Context context) {
super(context);
init(context);
}
/**
*首先是初始化mTouchSlop 是触摸手势滑动的最小像素值,也就是说滑动多少
*的距离才算是手势滑动,这样可以防止手势一点点的移动就引起的滑动事件。
*mScroller 是用来处理平滑滚动的。之前的博客有介绍。
**/
private Scroller mScroller ;
private int mTouchSlop ;
private void init(Context context){
ViewConfiguration configuration = ViewConfiguration.get(getContext());
mTouchSlop = configuration.getScaledTouchSlop();
mScroller = new Scroller(context, new DecelerateInterpolator());
}
/**
* 在布局初始化结束之后,得到布局中的两个子孩子,为啥只能有两个孩子那??
* BottomView是用来下拉刷新展示的View contentView 就是我们的
* ScrollView了。如果子孩子多了,怎么知道哪个VIew是需要被隐藏的?所以只
* 处理两个View的情况,当然,如果还有上拉加载更多,就需要三个子孩子了。
**/
private ViewGroup bottomView ;
private ScrollView contentView ;
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
getTopPosition();
return super.dispatchTouchEvent(ev);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
if (getChildCount() > 2) {
throw new RuntimeException("子孩子只能有两个");
}
bottomView = (ViewGroup) getChildAt(0);
contentView = (ScrollView) getChildAt(1);
}
private int startY ;
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (getScrollY() < 0 ) {
return true ;
}
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
startY = (int) ev.getY();
break;
case MotionEvent.ACTION_MOVE:
int moveY = (int) ev.getY();
int delayY = moveY - startY ;
Log.i("Test", delayY + " = " + mTouchSlop) ;
if (getTopPosition() && delayY > mTouchSlop) {
ev.setAction(MotionEvent.ACTION_DOWN);
return true ;
}
break ;
case MotionEvent.ACTION_UP:
break;
}
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
startY = (int) event.getY();
break;
case MotionEvent.ACTION_MOVE:
int delayY = (int) (event.getY() - startY) ;
if (getTopPosition() && getScrollY() <= 0 ) {
pullMove((int) (-delayY * 0.8));
}
startY = (int) event.getY();
return true ;
case MotionEvent.ACTION_UP:
int scrollY = getScrollY();
if (state == PullState.ON_REFRESH && scrollY < 0 && Math.abs(scrollY) > bottomHeight) {
restView(-getScrollY() - bottomHeight);
return true ;
}else if (state == PullState.ON_REFRESH && scrollY < 0 && Math.abs(scrollY) < bottomHeight) {
return true ;
}
if (scrollY < 0 && Math.abs(scrollY) < bottomHeight ) {
returnView();
}else if (scrollY < 0 && Math.abs(scrollY) > bottomHeight && state != PullState.ON_REFRESH) {
if (onreListener != null) {
state = PullState.ON_REFRESH ;
onreListener.refresh();
}
restView(-getScrollY() - bottomHeight);
}
break;
}
return true ;
}
private PullState state = PullState.REST ;
@Override
public void computeScroll() {
super.computeScroll();
if (mScroller.computeScrollOffset()) {
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
postInvalidate();
}
}
private void returnView(){
restView(-getScrollY());
}
private void restView(int dy){
mScroller.startScroll(0, getScrollY(), 0, dy , 340);
postInvalidate();
}
private void pullMove(int delay){
if (getScrollY() <= 0 && (getScrollY() + delay) <= 0 ) {
scrollBy(0, delay);
}else {
scrollTo(0, 0);
}
}
/**
*手势的拦截动作,通过getTopPosition()方式,来判断ScrollView时候处于
*下拉需要显示隐藏子View的状态,delayY > mTouchSlop 是用来判断是不是
*下拉的动作的。 如果符合条件 我们就需要将手势拦截掉,return true。
*为什么我觉得ScrollView做下来刷新比较好做那? 就是因为ScrollView的判断比较好判断。
*/
private boolean getTopPosition(){
if (contentView.getScrollY() <= 0 ) {
return true ;
}
return false ;
}
/**
*onLayout,做过自定义的都应该很熟悉这个方法,放置子孩子位置的一个方法,
*因为我们需要有一个子孩子隐藏掉,当我们需要它显示的时候才去显示,所以
*需要手动的去将BottomView放到布局-hight到0的位置,这样下拉的时候才能
*显示出来。
**/
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
bottomHeight = getBottomViewHeight() ;
Log.i("Test", l + "ceshi" + " t="+t + " r"+r + " b=" + b + " height= " + bottomHeight);
bottomView.layout(l, - bottomHeight, r, t);
contentView.layout(l, 0, r, b);
}
private int bottomHeight = 0 ;
private int getBottomViewHeight(){
return bottomView.getMeasuredHeight();
}
enum PullState{
REST , ON_REFRESH
}
public void stopRefresh(){
state = PullState.REST;
returnView();
}
private onRefreshListener onreListener ;
public void setOnRefreshListener (onRefreshListener onreListener) {
this.onreListener = onreListener ;
}
public interface onRefreshListener{
public void refresh();
}
}
ScrollView下拉刷新
最新推荐文章于 2024-03-16 17:38:03 发布