1

我正在尝试创建一个可以从我的应用程序的底部边缘拉出的菜单,我通过创建一个覆盖RelativeLayout的自定义视图来做到这一点,这是我膨胀的relativelayout的xml:

<RelativeLayout  xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >

<ImageView
    android:id="@+id/arrow"
    android:layout_width="60dp"
    android:layout_height="60dp"
    android:layout_centerHorizontal="true"
    android:src="@drawable/up_arrow" />
<FrameLayout
    android:id="@+id/content"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_marginTop="60dp"
    android:background="#77F5F1" >
</FrameLayout>

还有自定义布局的代码:

public class BottomMenuStack extends RelativeLayout {
private static final int ACCEPTABLE_TOUCH_OFFSET = 70;
private boolean firstTime = true;
private int closedY;
private View arrowView;
private boolean arrowShowing = true;
private boolean moving = false;
private View contentView;
private View mainView;
private boolean contentShowing = false;
private ObjectAnimator showArrowAnimation, hideArrowAnimation, showAnimation, hideAnimation;
private GestureDetector gestureDetector;
private float openedY, lastDistance;

public BottomMenuStack(Context context) {
    super(context);
    init(context);
}

public BottomMenuStack(Context context, AttributeSet attrs) {
    super(context, attrs);
    init(context);
}

private void init(Context context) {
    LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    inflater.inflate(R.layout.bottom_menu, this, true);
    arrowView = findViewById(R.id.arrow);
    contentView = findViewById(R.id.content);
    mainView = (View) arrowView.getParent();
    //   Creates a DestureDetector, to handle the scroll, fling and down events
    gestureDetector = new GestureDetector(getContext(), new gestureListener());
    gestureDetector.setIsLongpressEnabled(false);
    //       Initialize the animations
    hideArrowAnimation = ObjectAnimator.ofInt(contentView, "top", 0).setDuration(500);
    showArrowAnimation = ObjectAnimator.ofInt(contentView, "top", 0).setDuration(500);
    hideAnimation = ObjectAnimator.ofFloat(mainView, "translationY", 0).setDuration(500);
    showAnimation = ObjectAnimator.ofFloat(mainView, "translationY", 0).setDuration(500);
    showAnimation.addListener(new AnimatorListener() {
        public void onAnimationStart(Animator animation) {
        }

        public void onAnimationEnd(Animator animation) {
            hideArrow();
        }

        public void onAnimationCancel(Animator animation) {
        }

        public void onAnimationRepeat(Animator animation) {
        }
    });
}

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {

    super.onLayout(changed, l, t, r, b);
    if (firstTime) {
        firstTime = false;
        ViewGroup.LayoutParams params = mainView.getLayoutParams();
        params.height = 2 * (((View) mainView.getParent()).getHeight() / 3);
        mainView.setLayoutParams(params);
        closedY = mainView.getHeight() - arrowView.getHeight();
        openedY = closedY / 3;
        mainView.setTranslationY(closedY);
        postInvalidate();

    }

}

@Override
public boolean onTouchEvent(MotionEvent event) {
    boolean gestureDetectorRes = gestureDetector.onTouchEvent(event);
    if (moving && (event.getAction() == MotionEvent.ACTION_CANCEL || event.getAction() == MotionEvent.ACTION_UP)) {

        if (lastDistance < 0) {
            hideContent();
        } else {
            showContent();
        }
        moving = false;
        return true;
    }
    return gestureDetectorRes;
}

public boolean isTouchAcceptable(float x, float y) {
    if (arrowShowing) {
        if (x > arrowView.getLeft() && x < arrowView.getRight())
            if (y > arrowView.getTop() + mainView.getY() && y < (arrowView.getTop() + mainView.getY() + arrowView.getHeight()))
                return true;
    } else {
        if (y > (mainView.getY() - ACCEPTABLE_TOUCH_OFFSET) && y < (mainView.getY() + ACCEPTABLE_TOUCH_OFFSET))
            return true;
    }
    return false;
}

public void toggleContent() {
    if (contentShowing)
        hideContent();
    else
        showContent();
}

public void hideContent() {
    if (contentShowing) {
        cancelScrollAnimations();
        hideAnimation.setFloatValues(mainView.getY(), closedY);
        hideAnimation.start();
        contentShowing = false;
    }
}

public void showContent() {
    if (!contentShowing) {
        cancelScrollAnimations();
        showAnimation.setFloatValues(mainView.getTranslationY(), openedY);
        showAnimation.start();
        contentShowing = true;
    } else if (!showAnimation.isRunning()) {
        hideArrow();
    }

}

public void toggleArrow() {
    if (arrowShowing)
        hideArrow();
    else
        showArrow();
}

public void showArrow() {
    if (!arrowShowing) {
        cancelArrowAnimations();
        showArrowAnimation.setIntValues(contentView.getTop(), arrowView.getHeight());
        showArrowAnimation.start();
        arrowShowing = true;
    }
}

public void hideArrow() {
    if (arrowShowing) {

        cancelArrowAnimations();
        hideArrowAnimation.setIntValues(contentView.getTop(), 0);
        hideArrowAnimation.start();
        arrowShowing = false;
    }
}

private void cancelScrollAnimations() {
    if (showAnimation != null)
        showAnimation.cancel();
    if (hideAnimation != null)
        hideAnimation.cancel();
}

private void cancelArrowAnimations() {
    if (showArrowAnimation != null)
        showArrowAnimation.cancel();
    if (hideArrowAnimation != null)
        hideArrowAnimation.cancel();
}

private class gestureListener implements OnGestureListener {

    @Override
    public boolean onDown(MotionEvent e) {
        if (isTouchAcceptable(e.getX(), e.getY())) {
            showArrow();
            moving = true;
            return true;
        }
        return false;
    }

    @Override
    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
        if (moving) {
            if (velocityY > 0)
                hideContent();
            else
                showContent();
        }
        return false;
    }

    @Override
    public void onLongPress(MotionEvent e) {

    }

    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
        float y = e2.getY();
        if (moving) {
            lastDistance = distanceY;
            if (y < closedY && y > openedY)
                mainView.setTranslationY(y);
        }
        return false;
    }

    @Override
    public void onShowPress(MotionEvent e) {

    }

    @Override
    public boolean onSingleTapUp(MotionEvent e) {
        return false;
    }

}

    }

问题是,每当我尝试在层次结构查看器上调试此布局时(在模拟器上运行应用程序时),我看到的视图位置与它们在屏幕上实际看到的位置完全不同,这里有一些屏幕截图可以更好地解释这种情况:
Hierarchy Viewer screenshot
Emulator screenshot
在这些屏幕截图中可以看到箭头,即在模拟器的截图中出现在屏幕底部边框的上方,根据 Hierarchy Viewer 是在上边距的正下方,这怎么可能?我在移动视图时做错了什么?
触发动画,使视图在模拟器屏幕上移动,不会改变 Hierarchy Viewer 预览。

4

0 回答 0