2016-05-12 20 views
5

Scrivo un RecyclerViewRefresh segui SwipeRefreshLayout.class. Quando tiro la vista finché non si muove e poi rilascia, la vista si ripristina all'originale. Il problema è che la vista dovrebbe attivare il timer e quindi il timer resettare la vista. Non ho potuto trovare la ragione.LinearLayout scorrere verso l'alto

Per favore dimmi perché offsetTopAndBottom() può rendere automaticamente la vista di nuovo alla posizione originale. Grazie.

Io uso setY() per risolvere questo problema. Ma voglio anche sapere perché. E ho letto l'origine di offsetTopAndBottom(), inoltre non riesco a trovare alcun indizio.

codice di RecyclerViewRefresh:

public class RecyclerViewRefresh extends LinearLayout { 
private static final String LOG_TAG=RecyclerViewRefresh.class.getSimpleName(); 
private static final int INVALID_POINTER=-1; 
//Default offset in dips from the top of the view to where the progress 
//spinner should stop 
private static final int DEFAULT_CIRCLE_TARGET=64; 
private static final float DRAG_RATE=.5f; 

private View headerView,footerView,thisView; 
private View mTarget; //the target of the gesture 
private ImageView arrowIv; 
private TextView refreshTv; 
private ProgressBar progressBar; 
private OnPullToRefresh refreshListener=null; 
private OnDragToLoad loadListener=null; 
float startY=0; 

private int headerHeight=0; 
private boolean mReturningToStart; 
private boolean mRefreshing=false; 
private boolean mNestedScrollInProgress; 
private int mCurrentTargetOffsetTop; 
protected int mOriginalOffsetTop; 
private boolean mIsBeingDragged; 
private int mActivePointerId=INVALID_POINTER; 
private float mInitailDownY; 
private int mTouchSlop; 
private float mTotalDragDistance=-1; 
private float mInitialMotionY; 
private float mSpinnerFinalOffset; 
private boolean updateHeader=true; 
private Handler handler=new Handler(); 
private Timer timer; 

public RecyclerViewRefresh(Context context) { 
    super(context); 
    initView(context); 
} 

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

public RecyclerViewRefresh(Context context, AttributeSet attrs, int defStyleAttr) { 
    super(context, attrs, defStyleAttr); 
    initView(context); 
} 
private void initView(Context context) 
{ 
    thisView=this; 
    mTouchSlop= ViewConfiguration.get(context).getScaledTouchSlop(); 
    headerView=LayoutInflater.from(context).inflate(R.layout.header_layout,null); 
    footerView=LayoutInflater.from(context).inflate(R.layout.header_layout,null); 
    measureView(headerView); 
    arrowIv=(ImageView)headerView.findViewById(R.id.arrow); 
    refreshTv=(TextView)headerView.findViewById(R.id.tip); 
    progressBar=(ProgressBar)headerView.findViewById(R.id.progress); 
    headerHeight=headerView.getMeasuredHeight(); 
    LinearLayout.LayoutParams lp=new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 
      headerView.getMeasuredHeight()); 
    this.addView(headerView,lp); 
    setTopHeader(headerHeight); 

    final DisplayMetrics metrics=getResources().getDisplayMetrics(); 
    mSpinnerFinalOffset=DEFAULT_CIRCLE_TARGET*metrics.density; 
    mTotalDragDistance=mSpinnerFinalOffset; 
} 
/** 
* 通知父布局,占用的宽,高; 
* 
* @param view 
*/ 
private void measureView(View view) { 
    ViewGroup.LayoutParams p = view.getLayoutParams(); 
    if (p == null) { 
     p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 
       ViewGroup.LayoutParams.WRAP_CONTENT); 
    } 
    int width = ViewGroup.getChildMeasureSpec(0, 0, p.width); 
    int height; 
    int tempHeight = p.height; 
    if (tempHeight > 0) { 
     height = MeasureSpec.makeMeasureSpec(tempHeight, 
       MeasureSpec.EXACTLY); 
    } else { 
     height = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); 
    } 
    view.measure(width, height); 
} 
private void setTopHeader(int height) 
{ 
    if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.HONEYCOMB) 
    { 
     this.setY(-height); 
    }else{ 
     LayoutParams lp=new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,height); 
     lp.topMargin=-height; 
     this.setLayoutParams(lp); 
    } 
    headerView.invalidate(); 
} 

/** 
* Set the listener to be notified when a refresh is triggered via the 
* pull gesture. 
* @param listener 
*/ 
public void setOnPullToRefresh(OnPullToRefresh listener) 
{ 
    this.refreshListener=listener; 
} 

/** 
* Set the listener to be notified when a load is triggered via the 
* drag gesture 
* @param listener 
*/ 
public void setOnDragToLoad(OnDragToLoad listener) 
{ 
    this.loadListener=listener; 
} 


private void ensureTarget(){ 
    if(mTarget==null){ 
     for(int i=0;i<getChildCount();i++) 
     { 
      View child=getChildAt(i); 
      if(child instanceof RecyclerView) 
      { 
       mTarget=child; 
       break; 
      } 
     } 
    } 
} 

/** 
* @return Whether it is possible for the child view of this layout to 
* scroll up.Override this if the child view is a custom view. 
*/ 
public boolean canChildScrollUp(){ 
    if(mTarget==null) 
    { 
     ensureTarget(); 
    } 
    if(Build.VERSION.SDK_INT<14) 
    { 
     if(mTarget instanceof AbsListView) 
     { 
      final AbsListView absListView=(AbsListView)mTarget; 
      return absListView.getChildCount()>0 
        &&(absListView.getFirstVisiblePosition()>0 
        ||absListView.getChildAt(0).getTop()<absListView.getPaddingTop()); 
     }else{ 
      return ViewCompat.canScrollVertically(mTarget,-1)|| mTarget.getScrollY()>0; 
     } 
    }else{ 
     return ViewCompat.canScrollVertically(mTarget,-1); 
    } 
} 

@Override 
public boolean onInterceptTouchEvent(MotionEvent ev) { 
    ensureTarget(); 
    final int action=MotionEventCompat.getActionMasked(ev); 

    if(mReturningToStart && action == MotionEvent.ACTION_DOWN){ 
     mReturningToStart = false; 
    } 

    if(!isEnabled() || mReturningToStart || canChildScrollUp() 
      ||mRefreshing || mNestedScrollInProgress){ 
     return false; 
    } 

    switch (action){ 
     case MotionEvent.ACTION_DOWN: 
      setTargetOffsetTopAndBottom(mOriginalOffsetTop-headerView.getTop(),true); 
      mActivePointerId=MotionEventCompat.getPointerId(ev,0); 
      mIsBeingDragged=false; 
      final float initialDownY=getMotionEventY(ev,mActivePointerId); 
      if(initialDownY==-1){ 
       return false; 
      } 
      mInitailDownY=initialDownY; 
      updateHeader=true; 
      break; 
     case MotionEvent.ACTION_MOVE: 
      if(mActivePointerId==INVALID_POINTER){ 
       Log.e(LOG_TAG, "Got ACTION_MOVE event but don't have an active pointer id."); 
       return false; 
      } 
      final float y=getMotionEventY(ev,mActivePointerId); 
      if(y==-1){ 
       return false; 
      } 
      final float yDiff=y-mInitailDownY; 
      if(yDiff>mTouchSlop && !mIsBeingDragged){ 
       mInitialMotionY=mInitailDownY+mTouchSlop; 
       mIsBeingDragged=true; 
      } 
      break; 
     case MotionEventCompat.ACTION_POINTER_UP: 
      onSecondaryPointerUp(ev); 
      break; 
     case MotionEvent.ACTION_UP: 
     case MotionEvent.ACTION_CANCEL: 
      mIsBeingDragged=false; 
      mActivePointerId=INVALID_POINTER; 
      break; 
    } 
    return mIsBeingDragged; 
} 

private float getMotionEventY(MotionEvent ev,int activePointerId){ 
    final int index=MotionEventCompat.findPointerIndex(ev,activePointerId); 
    if(index<0){ 
     return -1; 
    } 
    return MotionEventCompat.getY(ev,index); 
} 
private void setTargetOffsetTopAndBottom(int offset,boolean requiresUpdate){ 
    if(this.getTop()<headerHeight+5) 
    { 
     this.offsetTopAndBottom(offset); 
     mCurrentTargetOffsetTop=this.getTop(); 
     if(requiresUpdate && Build.VERSION.SDK_INT<11){ 
      invalidate(); 
     } 

     if(this.getTop()>headerHeight) 
     { 
      if(updateHeader){ 
       updateHeader=false; 
       refreshTv.setText(getResources().getText(R.string.releasetorefresh)); 
       RotateAnimation animation=new RotateAnimation(0,180, 
         Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f); 
       animation.setDuration(800); 
       animation.setFillAfter(true); 
       arrowIv.startAnimation(animation); 
      } 
     } 
    } 

} 

private void onSecondaryPointerUp(MotionEvent ev){ 
    final int pointerIndex=MotionEventCompat.getActionIndex(ev); 
    final int pointerId=MotionEventCompat.getPointerId(ev,pointerIndex); 
    if(pointerId==mActivePointerId){ 
     //This was our active pointer going up. Choose a new 
     //active pointer and adjust accordingly. 
     final int newPointerIndex=pointerIndex==0?1:0; 
     mActivePointerId=MotionEventCompat.getPointerId(ev,newPointerIndex); 
    } 
} 
@Override 
public boolean onTouchEvent(MotionEvent event) 
{ 
    final int action=MotionEventCompat.getActionMasked(event); 
    int pointerIndex=-1; 

    if(mReturningToStart&&action==MotionEvent.ACTION_DOWN){ 
     mReturningToStart=false; 
    } 
    if(!isEnabled() || mReturningToStart 
      || canChildScrollUp() || mNestedScrollInProgress){ 
     //Fail fast if we're not in a state where a swipe is possible 
     return false; 
    } 
    switch(action){ 
     case MotionEvent.ACTION_DOWN: 
      mActivePointerId=MotionEventCompat.getPointerId(event,0); 
      mIsBeingDragged=false; 
      break; 
     case MotionEvent.ACTION_MOVE:{ 
      pointerIndex=MotionEventCompat.findPointerIndex(event,mActivePointerId); 
      if(pointerIndex<0){ 
       Log.e(LOG_TAG, "Got ACTION_MOVE event but have an invalid active pointer id."); 
       return false; 
      } 
      final float y=MotionEventCompat.getY(event,pointerIndex); 
      final float overscrollTop=(y-mInitialMotionY)*DRAG_RATE; 
      if(mIsBeingDragged){ 
       if(overscrollTop>0){ 
        moveSpinner(overscrollTop); 
       }else{ 
        return false; 
       } 
      } 
      break; 
     } 
     case MotionEventCompat.ACTION_POINTER_DOWN:{ 
      pointerIndex=MotionEventCompat.getActionIndex(event); 
      if(pointerIndex<0){ 
       Log.e(LOG_TAG, "Got ACTION_POINTER_DOWN event but have an invalid action index."); 
       return false; 
      } 
      mActivePointerId=MotionEventCompat.getPointerId(event,pointerIndex); 
      break; 
     } 
     case MotionEvent.ACTION_POINTER_UP: 
      onSecondaryPointerUp(event); 
      break; 
     case MotionEvent.ACTION_UP:{ 
      pointerIndex=MotionEventCompat.findPointerIndex(event,mActivePointerId); 
      if(pointerIndex<0){ 
       Log.e(LOG_TAG, "Got ACTION_UP event but don't have an active pointer id."); 
       return false; 
      } 
      final float y=MotionEventCompat.getY(event,pointerIndex); 
      mIsBeingDragged=false; 
      finishSpinner(); 
      mActivePointerId=INVALID_POINTER; 
      return false; 
     } 
     case MotionEvent.ACTION_CANCEL: 
      return false; 
    } 
    return true; 
} 

private void moveSpinner(float overscrollTop){ 
    float originalDragPercent=overscrollTop/mTotalDragDistance; 
    float dragPercent=Math.min(1f,Math.abs(originalDragPercent)); 
    float adjustedPercent=(float)Math.max(dragPercent-.4,0)*5/3; 
    float extraOS=Math.abs(overscrollTop)-mTotalDragDistance; 
    float slingshotDist=mSpinnerFinalOffset; 
    float tensionSlingshotPercent=Math.max(0,Math.min(extraOS,slingshotDist*2)/slingshotDist); 
    float tensionPercent=(float)((tensionSlingshotPercent/4)-Math.pow(
      (tensionSlingshotPercent/4),2))*2f; 
    float extraMove=(slingshotDist)*tensionPercent*2; 

    int targetY=mOriginalOffsetTop+(int)((slingshotDist*dragPercent)+extraMove); 
    setTargetOffsetTopAndBottom(targetY-mCurrentTargetOffsetTop,true); 
} 
private void finishSpinner(){ 
    if(this.getTop()>headerHeight){ 
     setRefreshing(true,true); 
    }else{ 
     //cancel refresh 
     mRefreshing=false; 
     animateOffsetToStartPosition(); 
    } 
} 
private void setRefreshing(boolean refreshing,final boolean notify) 
{ 
    if(mRefreshing!=refreshing){ 
     ensureTarget(); 
     mRefreshing=refreshing; 
     if(mRefreshing){ 
      refreshListener.onRefresh(); 
      arrowIv.setVisibility(View.GONE); 
      progressBar.setVisibility(View.VISIBLE); 
     }else{ 
      arrowIv.setVisibility(View.VISIBLE); 
      progressBar.setVisibility(View.GONE); 
      animateOffsetToStartPosition(); 
     } 
    } 
} 
public void setRefreshing(boolean refreshing){ 
    if(!refreshing){ 
     setRefreshing(refreshing,false); 
    } 
} 
private void animateOffsetToStartPosition(){ 
    refreshTv.setText(getResources().getText(R.string.pulltorefresh)); 
    arrowIv.clearAnimation(); 
    Log.d(LOG_TAG,"getTop="+this.getTop()+" timer="+((timer==null)?"null":"notnumm")); 
    if(timer==null&&this.getTop()>0) 
    { 
     timer=new Timer(); 
     timer.schedule(new TimerTask() { 
      @Override 
      public void run() { 
       handler.post(new Runnable() { 
        @Override 
        public void run() { 
         if(thisView.getTop()>0) 
         { 
          thisView.offsetTopAndBottom(-1); 
          mCurrentTargetOffsetTop = headerView.getTop(); 
          if (Build.VERSION.SDK_INT < 11) { 
           invalidate(); 
          } 
         }else{ 
          Log.d(LOG_TAG,"cancel"); 
          timer.cancel(); 
          timer=null; 
         } 
        } 
       }); 
      } 
     },10,10); 
    } 
} 

/** 
* Classes that wish to be notified when the pull gesture correctly 
* triggers a refresh should implement this interface. 
*/ 
public interface OnPullToRefresh{ 
    public void onRefresh(); 
} 

/** 
* Classes that wish to be notified when the drag gesture correctly 
* triggers a load should implement this interface. 
*/ 
public interface OnDragToLoad{ 
    public void onLoad(); 
}} 
+0

Uso già setY() per risolvere questo problema. Ma voglio anche sapere perché. E ho letto l'origine di offsetTopAndBottom(), inoltre non riesco a trovare alcun indizio. –

risposta

0

offsetTopAndBottom (offset) aggiungerà MTop e mBottom di vista da offset.

private void animateOffsetToStartPosition(){ 
    refreshTv.setText(getResources().getText(R.string.pulltorefresh)); 
    arrowIv.clearAnimation(); 
    Log.d(LOG_TAG,"getTop="+this.getTop()+" timer="+((timer==null)?"null":"notnumm")); 
    if(timer==null&&this.getTop()>0) 
    { 
     timer=new Timer(); 
     timer.schedule(new TimerTask() { 
      @Override 
      public void run() { 
       handler.post(new Runnable() { 
        @Override 
        public void run() { 
         if(thisView.getTop()>0) 
         { 
          //this line says that if top of thisView is not 0,add mtop and mBottom of thisView by -1 
          //this timer will change the mTop to 0.thisView will be back to original place if mTop == 0. 
          thisView.offsetTopAndBottom(-1); 
          mCurrentTargetOffsetTop = headerView.getTop(); 
          if (Build.VERSION.SDK_INT < 11) { 
           invalidate(); 
          } 
         }else{ 
          Log.d(LOG_TAG,"cancel"); 
          timer.cancel(); 
          timer=null; 
         } 
        } 
       }); 
      } 
     },10,10); 
    } 
} 
+0

Lo so. A volte torna automaticamente in cima. –

+0

Vuoi dire che se lo tiri spesso, tornerà immediatamente all'inizio? –

0

Sety() chiama setTranslationY(), che fa due chiamate a invalidateViewProperty (invalidateParent booleano, forceRedraw booleano). In setTranslationY(), quando chiama invalidateViewProperty, sta passando a forceRedraw come true, che ridisegna la vista e la riporta allo stato originale.

Problemi correlati