2016-10-28 10 views
0

RelativeLayoutにはViewDragHelperのビューを移動するために使用しています。内部でビューのサイズを変更する必要があるため、layout(boolean changed, int left, int top, int right, int bottom)を使用してドラッグ時のビューサイズを更新する必要があります。これはすべて正常に動作します。今では、内部の子ビューはタッチ位置がすべて間違っています。ボタンは、画面の上にあるかのように機能します。Android - チャイルド・ビューのタッチ位置が正しくない

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); 
} 

これは私がビューを移動するために使用するコードです。 mainView自体が正しいタッチ位置を持つため、このビュー内の子ビューが問題になります。私が行方不明のものはありますか?ここで

EDIT

は、私が使用している図です。

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(); 
     } 
    }); 
} 

} 

あなたは私がonTouchメソッドをオーバーライドして、それが正常に動作します見ることができるように。私はmainViewのボタンも持っていて、これも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> 

CheckBoxは動作しません。代わりに、あなたがそれを押すと動作します。どんな量であれ、それは上からオフセットされています。

+0

私はよく分かりますか分かりません。 RelativeLayoutがあり、内部にいくつかのビューがあります。 RelativeLayoutを移動すると、その内部のビューのサイズを変更したいですか?あなたはボタンについて話します、どのボタンですか?多分画像を投稿したり、少し簡単に説明したりできますか?申し訳ありません、ありがとうございます – Hugo

+0

@Hugo私はコード全体を追加しました –

答えて

1

私はあなたのコードがしていることをすべて理解するふりをしません。しかし、私はほとんど間違っているonLayoutの何かに気づいた。あなたが投稿したコードはこれです:

@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); 
    } 
} 

このコードではほとんど、特定のエラーがここにlayout方法の使用している:あなたは、パラメータを使用している

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

がレイアウトにleftrightmainView(子ビューはsetLayoutPositions経由)。 これは間違っています。lefttoprightbottomは親ビュー(MinimizableView)の相対位置です。子ビューはMinimizableViewの親ではなく、MinimizableViewの親ではなく、子のビューは相対的なものであるため、に直接を使用しないでください。

たとえば、mainViewのレイアウトでは、layout()に渡す値はMinimizableViewからの相対値として解釈されます。つまり、左のオフセットは0(プラスの埋め込み)、ではなく、leftです。 Diddo for right。レイアウトの呼び出しは次のようになります。

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

上記はパディングを考慮していないことに注意してください。

+0

ありがとうございました。私は0として左に設定することができますが、上にビューを移動するので、私は0としてトップを設定することはできません。開始位置にとどまるために 'mainView'が必要です。 'setLayoutPositions'メソッドは親の中の' otherViews'を動かすヘルパーに過ぎません。そして配置はここでのタッチ位置の問題ではありません。しかし、私はこれらの価値を動かそうとします。 –

関連する問題