web-dev-qa-db-ja.com

Androidスワイプで却下するフローティングアクティビティ

最新のFacebookのAndroidアプリには非常に素敵なフローティングコメントウィンドウがあります。ユーザーはウィンドウを上下にスワイプすると、本当に使いやすくなります。

アプリに同様の動作を実装したいのですが、その方法がわかりません。それを行う方法についてのアイデアや手がかりは本当にありがたいです。

Facebookアプリのスクリーンショット
(申し訳ありませんが、スクリーンショットを撮ったFacebookアプリは日本語です) Swipe upSwipe down

27
Antonio Jose

この終了/サイズ変更の動作に一致するコードをいくつか作成します、それが進むべきかどうかはわかりませんが、私のコードはActivityクラスに基づいています。最初に行うことは、アクティビティを作成し、Transluscentthemeを指定して、背景が透明なアクティビティを取得することです。

manifest.xmlで:

<activity
    Android:name=".PopupActivity"
    Android:label="@string/title_activity_popup"
    <!-- Use Translucent theme to get transparent activity background 
     and NoTitleBar to avoid super old style title bar ;) -->
    Android:theme="@Android:style/Theme.Translucent.NoTitleBar">
</activity>

次に、テキストビュー(Facebookのチャット部分に対応)とビュー(Facebookの「Write your msg」/「send smiley」タブに対応)を含む単純なレイアウトファイルを作成します。

私のレイアウト/activity_popup.xml:

<RelativeLayout
    xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:id="@+id/base_popup_layout"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent"
    Android:padding="8dp"
    Android:background="@Android:color/darker_gray"
    Android:layout_marginBottom="124dp">

    <TextView
        Android:text="@string/hello_world"
        Android:layout_width="match_parent"
        Android:layout_height="200dp"
        Android:background="@Android:color/black"/>

    <View
        Android:layout_width="match_parent"
        Android:layout_height="80dp"
        Android:layout_alignParentBottom="true"
        Android:background="@Android:color/holo_blue_dark"/>

</RelativeLayout>

最後に、PopupActivityクラスでtouchおよびmoveイベントを処理し、onTouchメソッドでコールバックを提供する onTouchListener を使用します。

PopupActivity

public class PopupActivity extends Activity implements View.OnTouchListener{

    private RelativeLayout baseLayout;

    private int previousFingerPosition = 0;
    private int baseLayoutPosition = 0;
    private int defaultViewHeight;

    private boolean isClosing = false;
    private boolean isScrollingUp = false;
    private boolean isScrollingDown = false;

    @Override
    protected void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_popup);
        baseLayout = (RelativeLayout) findViewById(R.id.base_popup_layout);
        baseLayout.setOnTouchListener(this);
    }


    public boolean onTouch(View view, MotionEvent event) {

        // Get finger position on screen
        final int Y = (int) event.getRawY();

        // Switch on motion event type
        switch (event.getAction() & MotionEvent.ACTION_MASK) {

            case MotionEvent.ACTION_DOWN:
                // save default base layout height
                defaultViewHeight = baseLayout.getHeight();

                // Init finger and view position
                previousFingerPosition = Y;
                baseLayoutPosition = (int) baseLayout.getY();
                break;

            case MotionEvent.ACTION_UP:
                // If user was doing a scroll up
                if(isScrollingUp){
                    // Reset baselayout position
                    baseLayout.setY(0);
                    // We are not in scrolling up mode anymore
                    isScrollingUp = false;
                }

                // If user was doing a scroll down
                if(isScrollingDown){
                    // Reset baselayout position
                    baseLayout.setY(0);
                    // Reset base layout size
                    baseLayout.getLayoutParams().height = defaultViewHeight;
                    baseLayout.requestLayout();
                    // We are not in scrolling down mode anymore
                    isScrollingDown = false;
                }
                break;
            case MotionEvent.ACTION_MOVE:
                if(!isClosing){
                    int currentYPosition = (int) baseLayout.getY();

                    // If we scroll up
                    if(previousFingerPosition >Y){
                        // First time Android rise an event for "up" move
                        if(!isScrollingUp){
                            isScrollingUp = true;
                        }

                    // Has user scroll down before -> view is smaller than it's default size -> resize it instead of change it position
                    if(baseLayout.getHeight()<defaultViewHeight){
                        baseLayout.getLayoutParams().height = baseLayout.getHeight() - (Y - previousFingerPosition);
                        baseLayout.requestLayout();
                    }
                    else {
                        // Has user scroll enough to "auto close" popup ?
                        if ((baseLayoutPosition - currentYPosition) > defaultViewHeight / 4) {
                            closeUpAndDismissDialog(currentYPosition);
                            return true;
                        }

                        //
                    }
                    baseLayout.setY(baseLayout.getY() + (Y - previousFingerPosition));

                }
                // If we scroll down
                else{

                    // First time Android rise an event for "down" move
                    if(!isScrollingDown){
                        isScrollingDown = true;
                    }

                    // Has user scroll enough to "auto close" popup ?
                    if (Math.abs(baseLayoutPosition - currentYPosition) > defaultViewHeight / 2)
                    {
                        closeDownAndDismissDialog(currentYPosition);
                        return true;
                    }

                    // Change base layout size and position (must change position because view anchor is top left corner)
                    baseLayout.setY(baseLayout.getY() + (Y - previousFingerPosition));
                    baseLayout.getLayoutParams().height = baseLayout.getHeight() - (Y - previousFingerPosition);
                    baseLayout.requestLayout();
                }

                // Update position
                previousFingerPosition = Y;
            }
            break;
        }
        return true;
    }
}

ユーザーがポップアップを閉じるのに十分なスクロールを持っているときに呼び出される2つの小さなメソッドがあります(つまり、アクティビティをアニメーション化して終了します)。

public void closeUpAndDismissDialog(int currentPosition){
    isClosing = true;
    ObjectAnimator positionAnimator = ObjectAnimator.ofFloat(baseLayout, "y", currentPosition, -baseLayout.getHeight());
    positionAnimator.setDuration(300);
    positionAnimator.addListener(new Animator.AnimatorListener()
    {
        . . .
        @Override
        public void onAnimationEnd(Animator animator)
        {
            finish();
        }
        . . .
    });
    positionAnimator.start();
}

public void closeDownAndDismissDialog(int currentPosition){
    isClosing = true;
    Display display = getWindowManager().getDefaultDisplay();
    Point size = new Point();
    display.getSize(size);
    int screenHeight = size.y;
    ObjectAnimator positionAnimator = ObjectAnimator.ofFloat(baseLayout, "y", currentPosition, screenHeight+baseLayout.getHeight());
    positionAnimator.setDuration(300);
    positionAnimator.addListener(new Animator.AnimatorListener()
     {
        . . .
        @Override
        public void onAnimationEnd(Animator animator)
        {
            finish();
        }
        . . .
    });
    positionAnimator.start();
}

このすべてのコードを使用すると、Facebookのポップアップ動作にグローバルに一致するPopupActivityを開始できるはずです。これは単なるドラフトクラスであり、やるべきことはまだたくさんあります。アニメーションの追加、パラメータのクローズの作業などです...

スクリーンショット:

Screenshot of PopupActivity as written above

31
Gaëtan M.

AppcompatライブラリからBottomSheetDialogFragmentコンポーネントを使用できると思います。情報については、この記事を確認してください: https://medium.com/@nullthemall/new-bottomsheet-caab21aff19b#.gpu1l516z

documentaion からも有用な情報を取得できます。