1

アプリケーションの下部マージンからプルできるメニューを作成しようとしています。これは、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 のスクリーンショット
Emulator のスクリーンショット
これらのスクリーンショットでは、エミュレータからのスクリーンショットで矢印を見ることができます。これは、Hierarchy Viewer によると、画面の下の境界線の上に表示されます。ビューを移動する際に何が間違っていますか?
アニメーションをトリガーし、ビューをエミュレーターの画面上で動かしても、Hierarchy Viewer のプレビューは変化しません。

4

0 に答える 0