Welcome toTencent Cloud + community, get more Tencent mass technology practice dry goods oh ~

This article was published by Brzhang in cloud + Community

There are many times in APP development when we need to achieve an immersive experience like the one below.

At the beginning of the experience, it seems that everyone will find it difficult to implement. The difficulty is:

  1. As the background image of the head is pushed up, it gradually becomes invisible, the color of the whole area becomes dark, and then the title appears.
  2. The StatusBar becomes transparent and the space can be used, which is the top of our picture.
  3. When our viewPager is pushed below the Actionbar, it is fixed below the Actionbar and cannot be pushed above it.
  4. There is a control at the bottom that slides the list up and out of view range to give more room for the list to display, but the whole immersive experience is about giving more room for the list to display.

Ok, to sum up, the above are our problems, but also need to be solved, one by one, this demand will be realized, so, how do we go step by step to solve the above problems?

1. Fade in and out of the header background and title

First of all, let us analysis the first question, the background of the head in the process of up, slowly become invisible, this sounds like some kind of collapse, therefore, it is easy to think of CollapsingToolbarLayout, CollapsingToolbarLayout if you want to easily understand CollapsingToolbarLayout

Application, I suggest to see this brother’s article, he also gave an animation, a more detailed introduction to the application of this, such as:

For usage, I am not be explained here, but if you don’t understand the application of the layout, I strongly suggest you to look at, can continue to go below, just want to explain, go here, do you have a need to fill in the pit, that is what we can not like that, the title of the animation and or title or centered, note that here, This is the Android design specification, but the designer doesn’t buy the Android specification. Therefore, we have to lay on this hole and then learn from Stack Overflow.

<android.support.v7.widget.Toolbar
    android:id="@+id/toolbar_top"
    android:layout_height="wrap_content"
    android:layout_width="match_parent"
    android:minHeight="? attr/actionBarSize"
    android:background="@color/action_bar_bkgnd"
    app:theme="@style/ToolBarTheme" >


     <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Toolbar Title"
        android:layout_gravity="center"
        android:id="@+id/toolbar_title" />


</android.support.v7.widget.Toolbar>
Copy the code

Assuming that this works, to solve the centring problem, change the back button to our button style, and then, after a bit of trickery, make the title transparent and change the image of the back button:

collapsingToolbarLayout.setCollapsedTitleTextColor(Color.WHITE);
//collapsingToolbarLayout.setExpandedTitleColor(Color.WHITE);
collapsingToolbarLayout.setExpandedTitleColor(Color.TRANSPARENT);
Copy the code

However, the hypothesis, which is always an assumption, is actually not true. When I tried it, I found that the TextView in the Toolbar couldn’t use the Android :layout_gravity=”center” property at all. Well, even if it was forced, the effect would be left.

So, how do I do that? My solution is this

<android.support.design.widget.AppBarLayout
            android:id="@+id/appbarlayout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:elevation="0dp"> <android.support.design.widget.CollapsingToolbarLayout android:id="@+id/collapsing_tool_bar" android:layout_width="match_parent" android:layout_height="wrap_content" app:contentScrim="@color/b_G6" app:expandedTitleMarginEnd="10dp" app:expandedTitleMarginStart="10dp" app:layout_scrollFlags="scroll|exitUntilCollapsed|snap"> <android.support.constraint.ConstraintLayout android:layout_width="match_parent" android:layout_height="match_parent"> <ImageView android:id="@+id/igame_arena_rank_class_header_bg" android:layout_width="match_parent" android:layout_height="0dp" android:scaleType="centerCrop" android:src="@drawable/bg_arena_rank_class" app:layout_constraintDimensionRatio="375:156" / >... </android.support.constraint.ConstraintLayout> <android.support.v7.widget.Toolbar android:id="@+id/common_index_activity_tb_title" android:layout_width="match_parent" android:layout_height="wrap_content" android:minHeight="? android:attr/actionBarSize" android:visibility="visible" app:contentInsetLeft="0dp" app:contentInsetStart="0dp" app:layout_collapseMode="pin"> <include layout="@layout/igame_common_tool_bar" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center" /> </android.support.v7.widget.Toolbar> </android.support.design.widget.CollapsingToolbarLayout> </android.support.design.widget.AppBarLayout>Copy the code

And then, the layout inside the include looks like this

<? xml version="1.0" encoding="utf-8"? > <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" Android :orientation="vertical"> //***** <View Android :id="@+id/common_index_activity_view_status_bar"  android:layout_width="match_parent" android:layout_height="0dp" /> <RelativeLayout android:layout_width="match_parent" android:layout_height="50dp"> <TextView android:id="@+id/tv_toolbar_bg" android:layout_width="match_parent" android:layout_height="50dp" android:layout_centerInParent="true" tools:background="@color/b_G6" /> <TextView android:id="@+id/common_index_header_tv_title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:gravity="center" android:textColor="@color/b_G99" Android :textSize="@dimen/ igame_textsize_XL "Tools :text=" Here is the title" /> <RelativeLayout android:id="@+id/common_index_header_rl_back" android:layout_width="48dp" android:layout_height="48dp" android:layout_centerVertical="true" android:layout_gravity="center_vertical" android:visibility="visible"> <ImageView android:layout_width="match_parent" android:layout_height="match_parent" android:layout_centerInParent="true" android:contentDescription="@string/image_desc" android:scaleType="centerInside" android:src="@drawable/igame_actionbar_arrow_left" /> </RelativeLayout> </RelativeLayout> </LinearLayout>Copy the code

Of course, at this point, the title needs to fade in and out yourself. So, what do we rely on?

appBarLayout.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
            @Override
            public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
                mTitle.setAlpha(-verticalOffset * 1.0f / appBarLayout.getTotalScrollRange()); }});Copy the code

This is based on listening to appBarLayout.

2. Make the statusBar transparent and use its space for our layout content.

 /** * make the status bar transparent and overwrite the status bar. Apis greater than 19 are displayed normally, but interfaces smaller than 19 are extended to the status bar, but the status bar is not transparent */
    @TargetApi(Build.VERSION_CODES.KITKAT)
    public static void transparentAndCoverStatusBar(Activity activity) {
        //FLAG_LAYOUT_NO_LIMITS do not use this. Models with virtual buttons can be particularly problematic

// //FLAG_TRANSLUCENT_STATUS requires an API greater than 19
// activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
// activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN);
// //FLAG_LAYOUT_NO_LIMITS has no API requirements
// activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            Window window = activity.getWindow();
            window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
            window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                    | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
            window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
            window.setStatusBarColor(Color.TRANSPARENT);
            window.setNavigationBarColor(Resources.getSystem().getColor(android.R.color.background_dark));
        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            Window window = activity.getWindow();
            window.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); }}Copy the code

Here is a method found on the Internet, directly call, but the API needs to be greater than 19, I believe that the current basically meet it. Notice that I don’t have this property in my AppBarLayout

android:fitsSystemWindows="true"
Copy the code

If you add this property, hey, hey, the Statusbar, while the space is available, has a color that you can’t get out of it,

And then, you remember in the layout above

//***** please note the View*******//
    <View
        android:id="@+id/common_index_activity_view_status_bar"
        android:layout_width="match_parent"
        android:layout_height="0dp" />
Copy the code

The height of the View is dynamically changed to the height of the statusBar. This is used to offset the original space of the status_bar.

@param context context @return Status bar height */
    public static int getStatusBarHeight(Context context) {
        // Get the status bar height
        int resourceId = context.getResources().getIdentifier("status_bar_height"."dimen"."android");
        return context.getResources().getDimensionPixelSize(resourceId);
    }
Copy the code

Once we’re done, we need to set the height of the toolbar we inserted ourselves to add the height of the Toolbar plus the height of the StatusBar.

3, ViewPager pushed below the Actionbar will not be pushed

CollapsingToolbarLayout has a child view that uses pin mode in the CollapsingToolbarLayout, so that child view is clearly the toolbar

<android.support.v7.widget.Toolbar
                    android:id="@+id/common_index_activity_tb_title"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:minHeight="? android:attr/actionBarSize"
                    android:visibility="visible"
                    app:contentInsetLeft="0dp"
                    app:contentInsetStart="0dp"
                    app:layout_collapseMode="pin">

                    <include
                        layout="@layout/igame_common_tool_bar"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:layout_gravity="center" />
                </android.support.v7.widget.Toolbar>
Copy the code

4. The bottom control is gradually hidden as the list slides

As you can see, the control at the bottom is overlaid on the list. When the list slides up, hide it to free up more controls to view the list. So how do you do that?

Since we are wrapped in CoordinatorLayout, obviously, the best way is to use layout_behavior, and I’ve implemented a BottomBehavior here:

public class BottomBehavior extends CoordinatorLayout.Behavior {
    private int id;
    private float bottomPadding;
    private int screenWidth;
    private float designWidth = 375.0f;// The width of the design view, usually 375dp,

    public BottomBehavior() {
        super(a); } public BottomBehavior(Context context, AttributeSet attrs) {super(context, attrs);
        screenWidth = getScreenWidth(context);
        TypedArray typedArray = context.getResources().obtainAttributes(attrs, R.styleable.BottomBehavior);
        id = typedArray.getResourceId(R.styleable.BottomBehavior_anchor_id, - 1);
        bottomPadding = typedArray.getFloat(R.styleable.BottomBehavior_bottom_padding, 0f);
        typedArray.recycle();
    }

    @Override
    public void onAttachedToLayoutParams(@NonNull CoordinatorLayout.LayoutParams params) {
        params.dodgeInsetEdges = Gravity.BOTTOM;
    }

    @Override
    public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
        return dependency.getId() == id;
    }

    @Override
    public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
        child.setTranslationY(-(dependency.getTop() - (screenWidth * bottomPadding / designWidth)));
        Log.e("BottomBehavior"."layoutDependsOn() called with: parent = [" + dependency.getTop());
        return true;
    }


    public static int getScreenWidth(Context context) {
        WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        Display display = null;
        if(wm ! =null) {
            display = wm.getDefaultDisplay();
            Point size = new Point();
            display.getSize(size);
            int width = size.x;
// int height = size.y;
            return width;
        }
        return 0; }}Copy the code

So this one has two custom properties in it, ID, bottomPadding, which is based on which control is changing relative position, and I’m going to base this on viewPager

A layoutDependsOn will only be called if onDependentViewChanged returns ture. BottomPadding represents an initial offset, since the viewPager itself is not at the top of the screen (the image occupies part of the control at the beginning), so you need to subtract that portion of possession.

In the same way, adding a hover on the left, right, slide to hide, stop to show, can also refer to a similar Behavior to reduce code coupling.

conclusion

The final layout looks something like this

<? xml version="1.0" encoding="utf-8"? > <com.tencent.igame.view.common.widget.IGameRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/igame_competition_detail_fragment_refresh" android:layout_width="match_parent" android:layout_height="match_parent"> <android.support.design.widget.CoordinatorLayout 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" app:elevation="0dp"> <android.support.design.widget.CollapsingToolbarLayout android:id="@+id/collapsing_tool_bar" android:layout_width="match_parent" android:layout_height="wrap_content" app:contentScrim="@color/b_G6" app:expandedTitleMarginEnd="10dp" app:expandedTitleMarginStart="10dp" app:layout_scrollFlags="scroll|exitUntilCollapsed|snap"> <android.support.constraint.ConstraintLayout android:layout_width="match_parent" android:layout_height="match_parent"> <ImageView android:id="@+id/igame_arena_rank_class_header_bg" android:layout_width="match_parent" android:layout_height="0dp" android:scaleType="centerCrop" android:src="@drawable/bg_arena_rank_class" app:layout_constraintDimensionRatio="375:156" / >... </android.support.constraint.ConstraintLayout> <android.support.v7.widget.Toolbar android:id="@+id/common_index_activity_tb_title" android:layout_width="match_parent" android:layout_height="wrap_content" android:minHeight="? android:attr/actionBarSize" android:visibility="visible" app:contentInsetLeft="0dp" app:contentInsetStart="0dp" app:layout_collapseMode="pin"> <include layout="@layout/igame_common_tool_bar" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center" /> </android.support.v7.widget.Toolbar> </android.support.design.widget.CollapsingToolbarLayout> </android.support.design.widget.AppBarLayout> <com.tencent.igame.widget.viewpager.IgameViewPager android:id="@+id/igame_arena_rank_class_vp_content" android:layout_width="match_parent" android:layout_height="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior" /> <android.support.constraint.ConstraintLayout android:layout_width="match_parent" android:layout_height="60dp" android:layout_gravity="bottom" android:background="@color/b_G6" android:paddingLeft="12dp" android:paddingRight="12dp" App: anchor_id = "@ + id/igame_arena_rank_class_vp_content" app: bottom_padding = "156.0" app:layout_behavior="com.tencent.igame.common.widget.BottomBehavior"> .......... At the bottom of the layout < / android. Support. The constraint. ConstraintLayout > < / android. Support. The design. The widget. CoordinatorLayout > </com.tencent.igame.view.common.widget.IGameRefreshLayout>Copy the code

Note: IGameRefreshLayout is actually the encapsulated PullToRefreshView, IgameViewPager is the encapsulated Viewpager, reduce the need to write Viewpager routine code every time.

With this framework, you can easily create a layout like this.

Machine learning in action! Quick introduction to online advertising business and CTR knowledge

This article has been authorized by the author to Tencent Cloud + community, more original text pleaseClick on the

Search concern public number “cloud plus community”, the first time to obtain technical dry goods, after concern reply 1024 send you a technical course gift package!

Massive technical practice experience, all in the cloud plus community!