2016-10-28 2 views
0

J'ai un RelativeLayout qui a un ViewDragHelper que j'utilise pour déplacer la vue. Je dois utiliser layout(boolean changed, int left, int top, int right, int bottom) pour mettre à jour la taille de vue sur glisser car les vues à l'intérieur doivent être redimensionnées. Tout cela fonctionne très bien. Maintenant, les vues des enfants à l'intérieur ont toutes leurs positions tactiles erronées. Les boutons fonctionnent comme s'ils étaient sur le dessus de l'écran, par opposition à l'endroit où ils se trouvent.Android - Les positions tactiles de la vue enfant sont incorrectes

private void changeDragViewPosition(int top, float verticalMovementFactor) { 
    int right = calculateViewRightPosition(verticalMovementFactor); 
    int left = right - mainViewLayoutParams.width; 
    int bottom = top + mainViewLayoutParams.height; 

    mainView.layout(left, top, right, bottom); 
} 

Ceci est le code que j'utilise pour déplacer la vue. La vue principale elle-même a des positions tactiles correctes, les vues enfant à l'intérieur de cette vue sont le problème. Y a-t-il quelque chose qui me manque?

EDIT

est ici le point de vue que je utilise.

public class MinimizableView extends RelativeLayout { 

private static final int DEFAULT_MINIMIZED_MARGIN = 2; 
private static final int DEFAULT_SCALE_FACTOR = 2; 
private static final int MIN_SLIDING_DISTANCE_ON_CLICK = 10; 

private View mainView; 
private View unmovableView; 
private ArrayList<View> otherViews; 
private ArrayList<Integer> initialPositions; 

private LayoutParams mainViewLayoutParams; 

private int verticalDragRange; 
private int mainViewOriginalWidth; 
private int mainViewOriginalHeight; 
private float scaleFactor = DEFAULT_SCALE_FACTOR; 
private float minimizedMargin; 
private boolean firstLayoutPass = true; 
private int mainViewInitialPosition; 
private float lastTouchActionDownXPosition; 

private ViewDragHelper viewDragHelper; 

private MinimizableViewListener listener; 

public interface MinimizableViewListener { 
    void onMinimized(); 

    void onMaximized(); 

    void onClosed(); 
} 

public MinimizableView(Context context) { 
    super(context); 

    init(context); 
} 

public MinimizableView(Context context, AttributeSet attrs) { 
    super(context, attrs); 

    init(context); 
} 

public MinimizableView(Context context, AttributeSet attrs, int defStyleAttr) { 
    super(context, attrs, defStyleAttr); 

    init(context); 
} 

private void init(Context context) { 
    minimizedMargin = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, DEFAULT_MINIMIZED_MARGIN, getResources().getDisplayMetrics()); 

    ViewCompat.requestApplyInsets(this); 
} 

private ViewDragHelper.Callback viewDragHelperCallback = new ViewDragHelper.Callback() { 

    private static final int MINIMUM_DX_FOR_HORIZONTAL_DRAG = 5; 
    private static final int MINIMUM_DY_FOR_VERTICAL_DRAG = 15; 
    private static final float X_MIN_VELOCITY = 1500; 
    private static final float Y_MIN_VELOCITY = 1000; 

    @Override 
    public boolean tryCaptureView(View child, int pointerId) { 
     return child.equals(mainView); 
    } 

    @Override 
    public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) { 
     if (!isMainViewAtBottom()) { 
      float verticalMovementFactor = (top - mainViewInitialPosition)/(float) verticalDragRange; 

      changeDragViewScale(verticalMovementFactor); 
      changeDragViewPosition(top, verticalMovementFactor); 
      changeSecondViewAlpha(verticalMovementFactor); 
      changeSecondViewPosition(verticalMovementFactor); 
      changeUnmovableViewAlpha(verticalMovementFactor); 
     } 
    } 

    @Override 
    public void onViewReleased(View releasedChild, float xvel, float yvel) { 
     super.onViewReleased(releasedChild, xvel, yvel); 

     if (isMainViewAtBottom() && !isViewAtRight(releasedChild)) { 
      triggerOnReleaseActionsWhileHorizontalDrag(xvel); 
     } else { 
      triggerOnReleaseActionsWhileVerticalDrag(yvel); 
     } 
    } 

    @Override 
    public int clampViewPositionVertical(View child, int top, int dy) { 
     int newTop = verticalDragRange + mainViewInitialPosition; 
     if (isMinimized() && Math.abs(dy) >= MINIMUM_DY_FOR_VERTICAL_DRAG || (!isMinimized() && !isMainViewAtBottom())) { 
      final int topBound = getPaddingTop() + mainViewInitialPosition; 
      final int bottomBound = verticalDragRange + mainViewInitialPosition; 

      newTop = Math.min(Math.max(top, topBound), bottomBound); 
     } 
     return newTop; 
    } 

    @Override 
    public int clampViewPositionHorizontal(View child, int left, int dx) { 
     int newLeft = mainView.getLeft(); 
     if ((isMinimized() && Math.abs(dx) > MINIMUM_DX_FOR_HORIZONTAL_DRAG) || (isMainViewAtBottom() && !isViewAtRight(mainView))) { 
      newLeft = left; 
     } 
     return newLeft; 
    } 

    private void triggerOnReleaseActionsWhileHorizontalDrag(float xvel) { 
     if (xvel < 0 && xvel <= -X_MIN_VELOCITY) { 
      closeToLeft(); 
     } else if (xvel > 0 && xvel >= X_MIN_VELOCITY) { 
      closeToRight(); 
     } else { 
      if (isNextToLeftBound(mainView)) { 
       closeToLeft(); 
      } else if (isNextToRightBound(mainView)) { 
       closeToRight(); 
      } else { 
       minimize(); 
      } 
     } 
    } 

    private void triggerOnReleaseActionsWhileVerticalDrag(float xvel) { 
     if (xvel < 0 && xvel <= -Y_MIN_VELOCITY) { 
      maximize(); 
     } else if (xvel > 0 && xvel >= Y_MIN_VELOCITY) { 
      minimize(); 
     } else { 
      if (isDragViewAboveTheMiddle(mainView)) { 
       maximize(); 
      } else { 
       minimize(); 
      } 
     } 
    } 
}; 

private void changeDragViewScale(float verticalMovementFactor) { 
    mainViewLayoutParams.width = (int) (mainViewOriginalWidth * (1 - (verticalMovementFactor/scaleFactor))); 
    mainViewLayoutParams.height = (int) (mainViewOriginalHeight * (1 - (verticalMovementFactor/scaleFactor))); 

    mainView.setLayoutParams(mainViewLayoutParams); 
} 

private void changeDragViewPosition(int top, float verticalMovementFactor) { 
    int right = calculateViewRightPosition(verticalMovementFactor); 
    int left = right - mainViewLayoutParams.width; 
    int bottom = top + mainViewLayoutParams.height; 

    mainView.layout(left, top, right, bottom); 
} 

private void changeSecondViewAlpha(float verticalMovementFactor) { 
    for (int i = 0; i < otherViews.size(); i++) { 
     otherViews.get(i).setAlpha(1 - verticalMovementFactor); 
    } 
} 

private void changeSecondViewPosition(float verticalMovementFactor) { 
    int newTop; 
    int initialTop; 
    for (int i = 0; i < otherViews.size(); i++) { 
     initialTop = initialPositions.get(i); 
     newTop = (int) (initialTop + ((getBottom() - initialTop) * verticalMovementFactor)); 

     otherViews.get(i).setY(newTop); 
    } 
} 

private void changeUnmovableViewAlpha(float verticalMovementFactor) { 
    unmovableView.setAlpha(1 - verticalMovementFactor); 
} 

private int calculateViewRightPosition(float verticalMoveFactor) { 
    return (int) (mainViewOriginalWidth - minimizedMargin * verticalMoveFactor); 
} 

private boolean isDragViewAboveTheMiddle(View view) { 
    int parentHeight = getHeight(); 
    float viewYPosition = view.getY() + (view.getHeight() * 0.5f); 

    return viewYPosition < (parentHeight * 0.5); 
} 

private boolean isMainViewAtTop() { 
    return mainView.getTop() == mainViewInitialPosition; 
} 

private boolean isMainViewAtBottom() { 
    return mainView.getBottom() >= getBottom() - getPaddingBottom() - minimizedMargin - 1; 
} 

private boolean isViewAtRight(View view) { 
    return view.getRight() + minimizedMargin + 10 >= getWidth() - 10; 
} 

private void closeToLeft() { 
    if (viewDragHelper.smoothSlideViewTo(mainView, -mainViewOriginalWidth, getHeight() - getMinHeightPlusMargin())) { 
     ViewCompat.postInvalidateOnAnimation(this); 
    } 
    if (listener != null) { 
     listener.onClosed(); 
    } 
} 

private int getMinHeightPlusMargin() { 
    return (int) (mainViewOriginalHeight * (1 - 1/scaleFactor) + minimizedMargin); 
} 

private int getMinWidthPlusMargin() { 
    return (int) (mainViewOriginalWidth * (1 - 1/scaleFactor) + minimizedMargin); 
} 

private void closeToRight() { 
    if (viewDragHelper.smoothSlideViewTo(mainView, mainViewOriginalWidth, getHeight() - getMinHeightPlusMargin())) { 
     ViewCompat.postInvalidateOnAnimation(this); 
    } 
    if (listener != null) { 
     listener.onClosed(); 
    } 
} 

private boolean isNextToLeftBound(View view) { 
    return (view.getLeft() - minimizedMargin) < getWidth() * 0.05; 
} 

private boolean isNextToRightBound(View view) { 
    return (view.getLeft() - minimizedMargin) > getWidth() * 0.75; 
} 

private boolean isViewHit(View view, int x, int y) { 
    int[] viewLocation = new int[2]; 
    view.getLocationOnScreen(viewLocation); 
    int[] parentLocation = new int[2]; 
    this.getLocationOnScreen(parentLocation); 
    int screenX = parentLocation[0] + x; 
    int screenY = parentLocation[1] + y; 
    return screenX >= viewLocation[0] 
      && screenX < viewLocation[0] + view.getWidth() 
      && screenY >= viewLocation[1] 
      && screenY < viewLocation[1] + view.getHeight(); 
} 

private static final int INVALID_POINTER = -1; 

private int activePointerId; 

@Override 
public boolean onTouchEvent(MotionEvent event) { 
    int actionMasked = MotionEventCompat.getActionMasked(event); 
    if ((actionMasked & MotionEventCompat.ACTION_MASK) == MotionEvent.ACTION_DOWN) { 
     activePointerId = MotionEventCompat.getPointerId(event, actionMasked); 
    } 
    if (activePointerId == INVALID_POINTER) { 
     return false; 
    } 
    viewDragHelper.processTouchEvent(event); 
    if (isClosed()) { 
     return false; 
    } 
    boolean isDragViewHit = isViewHit(mainView, (int) event.getX(), (int) event.getY()); 
    boolean isSecondViewHit = false; 
    for (int i = 0; i < otherViews.size(); i++) { 
     if (isViewHit(otherViews.get(i), (int) event.getX(), (int) event.getY())) { 
      isSecondViewHit = true; 
      break; 
     } 
    } 
    analyzeTouchToMaximizeIfNeeded(event, isDragViewHit); 
    if (isMaximized()) { 
     mainView.dispatchTouchEvent(event); 
    } else { 
     mainView.dispatchTouchEvent(cloneMotionEventWithAction(event, MotionEvent.ACTION_CANCEL)); 
    } 
    return isDragViewHit || isSecondViewHit; 
} 

private void analyzeTouchToMaximizeIfNeeded(MotionEvent ev, boolean isDragViewHit) { 
    switch (ev.getAction()) { 
     case MotionEvent.ACTION_DOWN: 
      lastTouchActionDownXPosition = ev.getX(); 
      break; 
     case MotionEvent.ACTION_UP: 
      float clickOffset = ev.getX() - lastTouchActionDownXPosition; 
      if (shouldMaximizeOnClick(ev, clickOffset, isDragViewHit)) { 
       if (isMinimized()) { 
        maximize(); 
       } 
      } 
      break; 
     default: 
      break; 
    } 
} 

public boolean shouldMaximizeOnClick(MotionEvent ev, float deltaX, boolean isDragViewHit) { 
    return (Math.abs(deltaX) < MIN_SLIDING_DISTANCE_ON_CLICK) && ev.getAction() != MotionEvent.ACTION_MOVE && isDragViewHit; 
} 

private MotionEvent cloneMotionEventWithAction(MotionEvent event, int action) { 
    return MotionEvent.obtain(event.getDownTime(), event.getEventTime(), action, event.getX(), event.getY(), event.getMetaState()); 
} 

@Override 
public boolean onInterceptTouchEvent(MotionEvent ev) { 
    if (!isEnabled()) { 
     return false; 
    } 
    switch (MotionEventCompat.getActionMasked(ev) & MotionEventCompat.ACTION_MASK) { 
     case MotionEvent.ACTION_CANCEL: 
     case MotionEvent.ACTION_UP: 
      viewDragHelper.cancel(); 
      return false; 
     case MotionEvent.ACTION_DOWN: 
      int index = MotionEventCompat.getActionIndex(ev); 
      activePointerId = MotionEventCompat.getPointerId(ev, index); 
      if (activePointerId == INVALID_POINTER) { 
       return false; 
      } 
      break; 
     default: 
      break; 
    } 
    boolean interceptTap = viewDragHelper.isViewUnder(mainView, (int) ev.getX(), (int) ev.getY()); 
    return viewDragHelper.shouldInterceptTouchEvent(ev) || interceptTap; 
} 

@Override 
protected void onFinishInflate() { 
    super.onFinishInflate(); 

    otherViews = new ArrayList<>(); 
    initialPositions = new ArrayList<>(); 

    int childCount = getChildCount(); 
    for (int i = 0; i < childCount; i++) { 
     View child = getChildAt(i); 
     if (child instanceof DragView) { 
      mainView = child; 
     } else if (child instanceof UnmovableView) { 
      unmovableView = child; 
     } else { 
      otherViews.add(child); 
     } 
    } 

    viewDragHelper = ViewDragHelper.create(this, 1, viewDragHelperCallback); 

    mainViewLayoutParams = (LayoutParams) mainView.getLayoutParams(); 
} 

@Override 
public void computeScroll() { 
    if (!isInEditMode() && viewDragHelper.continueSettling(true)) { 
     ViewCompat.postInvalidateOnAnimation(this); 
    } 
} 

@Override 
protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 
    if (isInEditMode() || firstLayoutPass) { 
     super.onLayout(changed, left, top, right, bottom); 

     mainViewInitialPosition = mainView.getTop(); 
     verticalDragRange = getMeasuredHeight() - mainViewInitialPosition - getMinHeightPlusMargin() - getPaddingTop() - getPaddingBottom(); 
     for (int i = 0; i < otherViews.size(); i++) { 
      initialPositions.add(otherViews.get(i).getTop()); 
     } 

     firstLayoutPass = false; 
    } else if (isMainViewAtTop()) { 
     mainView.layout(left, mainViewInitialPosition, right, mainViewInitialPosition + mainViewOriginalHeight); 

     changeUnmovableViewAlpha(0); 

     setLayoutPositions(0, left, right, bottom, true); 
    } else { 
     setLayoutPositions(mainView.getTop()/(float) verticalDragRange, left, right, bottom, false); 
    } 
} 

private void setLayoutPositions(float offsetFactor, int left, int right, int bottom, boolean setY) { 
    int newTop; 
    int initialTop; 
    for (int i = 0; i < otherViews.size(); i++) { 
     View view = otherViews.get(i); 

     initialTop = initialPositions.get(i); 
     newTop = (int) (initialTop + ((bottom - initialTop) * offsetFactor)); 

     view.layout(left, newTop, right, newTop + view.getHeight()); 
     if (setY) { 
      view.setY(newTop); 
     } 
    } 
} 

@Override 
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 
    super.onMeasure(widthMeasureSpec, heightMeasureSpec); 

    if (mainViewOriginalWidth == 0) { 
     mainViewOriginalWidth = mainView.getMeasuredWidth(); 
     mainViewOriginalHeight = mainView.getMeasuredHeight(); 
    } 
} 

private boolean smoothSlideTo(float slideOffset) { 
    final int topBound = mainViewInitialPosition + getPaddingTop(); 
    int x = (int) (slideOffset * (getWidth() - getMinWidthPlusMargin())); 
    int y = (int) ((slideOffset * verticalDragRange) + topBound); 
    if (viewDragHelper.smoothSlideViewTo(mainView, x, y)) { 
     ViewCompat.postInvalidateOnAnimation(this); 
     return true; 
    } 
    return false; 
} 

@Override 
public void setPadding(int left, int top, int right, int bottom) { 
    super.setPadding(left, top, right, bottom); 

    mainViewInitialPosition = mainView.getTop(); 
    verticalDragRange = getMeasuredHeight() - mainViewInitialPosition - getMinHeightPlusMargin() - getPaddingTop() - getPaddingBottom(); 
    for (int i = 0; i < otherViews.size(); i++) { 
     initialPositions.add(otherViews.get(i).getTop()); 
    } 
} 

// --------- PUBLIC METHODS ---------- // 
public void maximize() { 
    smoothSlideTo(0); 
    if (listener != null) { 
     listener.onMaximized(); 
    } 
} 

public void minimize() { 
    smoothSlideTo(1); 
    if (listener != null) { 
     listener.onMinimized(); 
    } 
} 

public boolean isMinimized() { 
    return isMainViewAtBottom() && isViewAtRight(mainView); 
} 

public boolean isMaximized() { 
    return mainView.getTop() == mainViewInitialPosition; 
} 

public boolean isClosed() { 
    return mainView.getRight() <= 0 && mainView.getLeft() >= getWidth(); 
} 

public void setMinimizableViewListener(MinimizableViewListener listener) { 
    this.listener = listener; 
} 

public void hide() { 
    changeDragViewScale(1); 
    changeDragViewPosition(verticalDragRange + mainViewInitialPosition - getMinHeightPlusMargin(), 1); 
    minimize(); 
    setVisibility(GONE); 
} 

public void show() { 
    setVisibility(VISIBLE); 
    post(new Runnable() { 
     @Override 
     public void run() { 
      maximize(); 
     } 
    }); 
} 

} 

Comme vous pouvez le voir je la méthode substituée onTouch et il fonctionne très bien. J'ai aussi un bouton à l'intérieur du mainView qui est aussi un RelativeLayout.

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 
xmlns:tools="http://schemas.android.com/tools" 
android:id="@+id/activity_main" 
android:layout_width="match_parent" 
android:layout_height="match_parent" 
tools:context="com.lycatv.dragviewtest.MainActivity"> 

<android.support.v7.widget.Toolbar 
    android:id="@+id/actionBar1" 
    android:layout_width="match_parent" 
    android:layout_height="?attr/actionBarSize" 
    android:background="?attr/colorPrimary" /> 

<TextView 
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" 
    android:layout_below="@+id/actionBar1" 
    android:fitsSystemWindows="true" 
    android:text="Hello World!" /> 

<com.lycatv.dragviewtest.MinimizableView 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:paddingBottom="56dp"> 

    <com.lycatv.dragviewtest.UnmovableView 
     android:id="@+id/actionBar" 
     android:layout_width="match_parent" 
     android:layout_height="wrap_content"> 

     <android.support.v7.widget.Toolbar 
      android:layout_width="match_parent" 
      android:layout_height="?attr/actionBarSize" 
      android:background="?attr/colorPrimaryDark" /> 
    </com.lycatv.dragviewtest.UnmovableView> 

    <FrameLayout 
     android:layout_width="match_parent" 
     android:layout_height="match_parent" 
     android:layout_below="@+id/actionBar" 
     android:background="#1a1e39" /> 

    <FrameLayout 
     android:id="@+id/frameLayout1" 
     android:layout_width="match_parent" 
     android:layout_height="48dp" 
     android:layout_below="@+id/actionBar" /> 

    <com.lycatv.dragviewtest.DragView 
     android:layout_width="match_parent" 
     android:layout_height="wrap_content" 
     android:layout_below="@+id/frameLayout1"> 

     <ImageView 
      android:layout_width="match_parent" 
      android:layout_height="200dp" 
      android:src="#000000" /> 

     <CheckBox 
      android:layout_width="wrap_content" 
      android:layout_height="wrap_content" 
      android:layout_gravity="bottom" 
      android:text="CheckBox" 
      android:textColor="@android:color/white" /> 
    </com.lycatv.dragviewtest.DragView> 
</com.lycatv.dragviewtest.MinimizableView> 

Le CheckBox ne fonctionne pas lorsque vous appuyez dessus. Au lieu de cela, cela fonctionne quand vous appuyez dessus. Quelle que soit la quantité, elle est décalée par rapport au sommet.

+0

Je ne sais pas si je comprends bien. Vous avez un RelativeLayout et à l'intérieur vous avez des vues. Lorsque vous déplacez le RelativeLayout vous voulez redimensionner les vues à l'intérieur, non? Ensuite, vous parlez de boutons, quels boutons? pourriez-vous poster peut-être une image ou essayer de l'expliquer un peu plus facilement? désolé et merci – Hugo

+0

@Hugo J'ai ajouté le code entier –

Répondre

1

Je ne prétendrai pas comprendre tout ce que fait votre code. Cependant, j'ai remarqué quelque chose dans onLayout qui est presque certainement faux. Le code affiché est le suivant:

@Override 
protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 
    if (isInEditMode() || firstLayoutPass) { 
     super.onLayout(changed, left, top, right, bottom); 

     mainViewInitialPosition = mainView.getTop(); 
     verticalDragRange = getMeasuredHeight() - mainViewInitialPosition - getMinHeightPlusMargin() - getPaddingTop() - getPaddingBottom(); 
     for (int i = 0; i < otherViews.size(); i++) { 
      initialPositions.add(otherViews.get(i).getTop()); 
     } 

     firstLayoutPass = false; 
    } else if (isMainViewAtTop()) { 
     mainView.layout(left, mainViewInitialPosition, right, mainViewInitialPosition + mainViewOriginalHeight); 

     changeUnmovableViewAlpha(0); 

     setLayoutPositions(0, left, right, bottom, true); 
    } else { 
     setLayoutPositions(mainView.getTop()/(float) verticalDragRange, left, right, bottom, false); 
    } 
} 

La quasi certaine erreur dans ce code est dans l'utilisation de la méthode layout ici:

mainView.layout(left, mainViewInitialPosition, right, mainViewInitialPosition + mainViewOriginalHeight); 

Vous utilisez le paramètre valeurs left et right à la mise en page votre mainView (et les vues enfant via setLayoutPositions). C'est faux. Gardez à l'esprit que left, top, right et bottom sont les positions relatives de votre affichage parent (MinimizableView); ils ne doivent pas être utilisés directement pour mettre en forme vos vues enfant, car les vues enfant sont relatives à MinimizableView, et non au parent de MinimizableView.

Par exemple, lorsque vous mettez en page mainView, les valeurs que vous transmettez dans layout() sont interprétées comme étant relatives à MinimizableView. Cela signifie que le décalage à gauche doit être 0 (plus rembourrage), pasleft. Diddo pour right. Vous appel de mise en page devrait ressembler davantage:

mainView.layout(0, 0, mainView.getMeasuredWidth(), mainView.getMeasuredHeight()); 

Notez que le dessus ne tient pas compte de rembourrage.

+0

Merci d'avoir pris le temps de regarder cela. Je peux mettre à gauche comme 0 mais je ne peux pas mettre le haut comme 0 parce que cela déplacerait la vue vers le haut.J'ai besoin du 'mainView' pour rester là où il a commencé. La méthode 'setLayoutPositions' est juste une aide pour déplacer le' otherViews' dans le parent. Et le placement n'est pas la question ici, c'est la position tactile. Mais je vais essayer de déplacer ces valeurs autour. –