2016-10-28 1 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> 

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

Вы используете параметр ценности left и right в макете ваш mainView (и вид ребенка через setLayoutPositions). Это неправильно. Имейте в виду, что left, top, right и bottom - относительные позиции вашего родительского вида (MinimizableView); они не должны использоваться непосредственно для размещения представлений вашего ребенка, поскольку представления для детей относительно MinimizableView, а не для родителя MinimizableView.

Например, когда вы размещаете mainView, значения, которые вы передаете в layout(), считаются относительно MinimizableView. Это означает, что левое смещение должно быть 0 (плюс отступ), неleft. Diddo для right. Ваш макет должен выглядеть больше:

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

Обратите внимание, что вышеприведенное не учитывает заполнение.

+0

Спасибо, что нашли время, чтобы посмотреть на это. Я могу установить left как 0, но я не могу установить top как 0, потому что это переместило бы представление вверх.Мне нужен 'mainView', чтобы он оставался там, где он начинался. Метод 'setLayoutPositions' является всего лишь помощником для перемещения' otherViews' внутри родителя. И место размещения не является проблемой здесь его сенсорной позиции. Но я попытаюсь перемещать эти ценности. –