web-dev-qa-db-ja.com

Androidスクロール時のツールバーの高さ

私はグーグルマップのように検索バーを実装しようとしますAndroid app:

enter image description here

リサイクラービューが初期状態の場合、ツールバーには標高がありません。ユーザーがスクロールを開始したときのみ、高度が表示されます。また、検索バー(ツールバー)は折りたたまれません。これが私がこれを複製しようとしたものです:

<Android.support.design.widget.CoordinatorLayout 
    Android:layout_width="match_parent"
    Android:layout_height="match_parent"
    Android:fitsSystemWindows="true">

    <Android.support.v7.widget.RecyclerView
        Android:id="@+id/recyclerView"
        Android:layout_width="match_parent"
        Android:layout_height="match_parent" />

    <Android.support.design.widget.AppBarLayout
        Android:id="@+id/appBarLayout"
        Android:layout_width="match_parent"
        Android:layout_height="wrap_content">

        <Android.support.v7.widget.Toolbar
            Android:id="@+id/toolbar"
            Android:layout_width="match_parent"
            Android:layout_height="64dp">

            <!-- content -->

        </Android.support.v7.widget.Toolbar>

    </Android.support.design.widget.AppBarLayout>

</Android.support.design.widget.CoordinatorLayout>

そしてここで結果を見ることができます:

enter image description here

だから私の解決策の問題は、ツールバーの高さが常に見えることです。ただし、リサイクラービューがその後ろにスクロールしたときにのみ表示されるようにします。 Googleマップアプリで見られるような動作を可能にする設計サポートライブラリから何かありますか?

使ってます

com.Android.support:appcompat-v7:23.2.0
com.Android.support:design:23.2.0
20
Upvote

CoordinatorLayoutを使用しているかどうかにかかわらず、_RecyclerView.OnScrollListener_は標高に関する限り、正しい方法のように見えます。ただし、私の経験から、recyclerview.getChild(0).getTop()は信頼性が低く、スクロール状態の判別にはnotを使用する必要があります。代わりに、これが機能しています:

_private static final int SCROLL_DIRECTION_UP = -1;
// ...
// Put this into your RecyclerView.OnScrollListener > onScrolled() method
if (recyclerview.canScrollVertically(SCROLL_DIRECTION_UP) {
   // Remove elevation
} else {
   // Show elevation
}
_

LayoutManagerRecyclerViewに割り当ててください。そうしないと、canScrollVerticallyを呼び出すとクラッシュする可能性があります。

26

これは良い質問ですが、既存の答えはどれも十分に良いものではありません。 getTop()の呼び出しは信頼性が非常に低いため、絶対にお勧めしません。マテリアルデザインの更新(2018)ガイドラインに準拠した新しいバージョンのGoogleアプリを見ると、最初は標高が非表示になり、ユーザーが下にスクロールするとすぐに標高が追加され、ユーザーがスクロールして再び上に到達すると再び非表示になります。

私は以下を使用して同じ効果を達成することができました:

val toolbar: Android.support.v7.widget.Toolbar? = activity?.findViewById(R.id.toolbar);

recyclerView?.addOnScrollListener(object: RecyclerView.OnScrollListener() {
    override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
        super.onScrolled(recyclerView, dx, dy);

        if(toolbar == null) {
            return;
        }

        if(!recyclerView.canScrollVertically(-1)) {
            // we have reached the top of the list
            toolbar.elevation = 0f
        } else {
            // we are not at the top yet
            toolbar.elevation = 50f
        }
    }
});

これは、垂直のリサイクラービューで完全に機能します(タブビューや他のリサイクラービューでも)。

いくつかの重要な注意事項:

  • ここでは、フラグメント内でこれを行っているため、activity?.findViewById...
  • ToolBarがAppBarLayout内にネストされている場合、標高をToolbarLayoutに適用する代わりに、AppBarLayoutに適用する必要があります。
  • 追加する必要がありますAndroid:elevation="0dp"およびapp:elevation="0dp"属性をToolbarまたはAppBarLayoutに追加して、リサイクラービューの最初に標高がないようにします。
4
Vahid Amiri

これは、同じようなことをしたいときにページを見つけましたが、より複雑なビュー階層でした。

調査の結果、カスタムビヘイビアを使用して同じ効果を得ることができました。これは、コーディネーターレイアウトの任意のビューで機能します(RecyclerViewやNestedScrollViewなどのネストされたスクロール要素がある場合)

注:これはAPI 21以上でのみ機能します。ViewCompat.setElevationはLollipop以前に効果がないようで、AppBarLayout#setTargetElevationは非推奨です。

ShadowScrollBehavior.Java

public class ShadowScrollBehavior extends AppBarLayout.ScrollingViewBehavior
        implements View.OnLayoutChangeListener {

    int totalDy = 0;
    boolean isElevated;
    View child;

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

    @Override
    public boolean layoutDependsOn(CoordinatorLayout parent, View child,
                                   View dependency) {
        parent.addOnLayoutChangeListener(this);
        this.child = child;
        return super.layoutDependsOn(parent, child, dependency);
    }

    @Override
    public boolean onStartNestedScroll(@NonNull CoordinatorLayout coordinatorLayout,
                                       @NonNull View child, @NonNull View directTargetChild,
                                       @NonNull View target, int axes, int type) {
        // Ensure we react to vertical scrolling
        return axes == ViewCompat.SCROLL_AXIS_VERTICAL ||
                super.onStartNestedScroll(coordinatorLayout, child, directTargetChild,
                        target, axes, type);
    }

    @Override
    public void onNestedPreScroll(@NonNull CoordinatorLayout coordinatorLayout,
                                  @NonNull View child, @NonNull View target,
                                  int dx, int dy, @NonNull int[] consumed, int type) {
        totalDy += dy;
        if (totalDy <= 0) {
            if (isElevated) {
                ViewGroup parent = (ViewGroup) child.getParent();
                if (parent != null) {
                    TransitionManager.beginDelayedTransition(parent);
                    ViewCompat.setElevation(child, 0);
                }
            }
            totalDy = 0;
            isElevated = false;
        } else {
            if (!isElevated) {
                ViewGroup parent = (ViewGroup) child.getParent();
                if (parent != null) {
                    TransitionManager.beginDelayedTransition(parent);
                    ViewCompat.setElevation(child, dp2px(child.getContext(), 4));
                }
            }
            if (totalDy > target.getBottom())
                totalDy = target.getBottom();
            isElevated = true;
        }
        super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed, type);
    }


    private float dp2px(Context context, int dp) {
        Resources r = context.getResources();
        float px = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, r.getDisplayMetrics());
        return px;
    }


    @Override
    public void onLayoutChange(View view, int i, int i1, int i2, int i3, int i4, int i5, int i6, int i7) {
        totalDy = 0;
        isElevated = false;
        ViewCompat.setElevation(child, 0);
    }
}

my_activity_layout.xml

<Android.support.design.widget.CoordinatorLayout
    Android:fitsSystemWindows="true"
    Android:layout_height="match_parent"
    Android:layout_width="match_parent">

    <Android.support.v7.widget.RecyclerView
        Android:id="@+id/recyclerView"
        Android:layout_height="match_parent"
        Android:layout_width="match_parent" />

    <Android.support.design.widget.AppBarLayout
        Android:id="@+id/appBarLayout"
        Android:layout_height="wrap_content"
        Android:layout_width="match_parent"
        app:layout_behavior="com.myapp.ShadowScrollBehavior">


        <Android.support.v7.widget.Toolbar
            Android:id="@+id/toolbar"
            Android:layout_height="64dp"
            Android:layout_width="match_parent">

            <!-- content -->

        </Android.support.v7.widget.Toolbar>

    </Android.support.design.widget.AppBarLayout>

</Android.support.design.widget.CoordinatorLayout>
3
user3425867

フラグメントにRecyclerViewがあります。以下のコードを使用して同様の効果を得ることができます:

それは最も賢い方法ではなく、より良い答えを待つことができます。

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {

    // Initial Elevation
    final Toolbar toolbar = (Toolbar) getActivity().findViewById(R.id.toolbar);
    if(toolbar!= null)
        toolbar.setElevation(0);

    // get initial position
    final int initialTopPosition = mRecyclerView.getTop();

    // Set a listener to scroll view
    mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {

        @Override
        public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
            super.onScrollStateChanged(recyclerView, newState);
        }

        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            super.onScrolled(recyclerView, dx, dy);

            if(toolbar!= null && mRecyclerView.getChildAt(0).getTop() < initialTopPosition ) {
                toolbar.setElevation(50);
            } else {
                toolbar.setElevation(0);
            }
        }
    });
}
3
W0rmH0le