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.
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
@Hugo J'ai ajouté le code entier –