2016-10-28 3 views
0

RelativeLayoutViewDragHelper이 있는데 주변을 이동하는 데 사용합니다. 내부의보기 크기를 조정해야하므로 드래그시 뷰 크기를 업데이트하려면 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 자체가 올바른 터치 위치를 가졌으므로이 뷰 내부의 자식 뷰가 문제가됩니다. 제가 빠진 것이 있습니까?

편집 여기

내가 사용하고 전망이다.

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 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 경유). 잘못되었습니다.left, top, rightbottom은 상위 뷰 (MinimizableView)의 상대적 위치입니다. 자식 뷰는 MinimizableView의 부모가 아닌 MinimizableView에 대한 상대이므로 은 직접으로 사용하면 안됩니다.

예를 들어, mainView을 배치하면 layout()에 전달한 값은 MinimizableView에 상대적인 것으로 해석됩니다. 즉, 왼쪽 오프셋은 0 (더하기 안쪽 여백), 이 아니고left이 아님을 의미합니다. Diddo는 right입니다. 레이아웃 호출은 다음과 같이 표시되어야합니다.

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

위의 내용은 패딩을 고려하지 않습니다.

+0

시간을내어 주셔서 감사합니다. 나는 왼쪽으로 0을 설정할 수 있지만, 위로 이동하면보기가 0으로 설정 될 수 없습니다.그것이 시작된 곳을 유지하기 위해서는'mainView'가 필요합니다. 'setLayoutPositions' 메소드는 부모 내에서'otherViews'를 움직이는 데 도움이됩니다. 그리고 그 위치는 터치 위치의 문제가 아닙니다. 그러나 나는이 값들을 움직이려고 노력할 것이다. –

관련 문제