requestLayout() improperly called by CollapsingToo

2020-08-22 07:54发布

问题:

I have a collapsing toolbar layout which contains an image and on collapse shows the toolbar title. I needed to change the toolbar title font so I added a textview inside toolbar layout. Now I'm getting the following error generated repeatedly whenever I collapse the toolbar.

08-12 13:14:19.604 2263-2263/com.panoroma.admin W/View: requestLayout() improperly called by android.support.design.widget.CollapsingToolbarLayout{2d353cd6 V.ED.... ........ 0,0-1080,390 #7f0c0070 app:id/collapsing_toolbar} during second layout pass: posting in next frame 
08-12 13:14:19.604 2263-2263/com.panoroma.admin W/View: requestLayout() improperly called by android.support.v7.widget.AppCompatTextView{1bb84b57 V.ED.... ........ 168,48-407,119 #7f0c0073 app:id/toolbar_title} during second layout pass: posting in next frame

my layout...

<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

<android.support.design.widget.AppBarLayout
    android:id="@+id/appbar"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:theme="@style/AppTheme.AppBarOverlay">

    <android.support.design.widget.CollapsingToolbarLayout
        android:id="@+id/collapsing_toolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:contentScrim="?attr/colorPrimary"
        app:layout_scrollFlags="scroll|enterAlways|enterAlwaysCollapsed|exitUntilCollapsed">

        <ImageView
            android:id="@+id/header"
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:adjustViewBounds="true"
            android:layout_gravity="center"
            android:scaleType="centerCrop"
            android:layout_marginTop="15dp"
            android:layout_marginBottom="15dp"
            android:background="@drawable/dashboard80"
            app:layout_collapseMode="parallax"
            app:layout_collapseParallaxMultiplier="0.5"  />

        <android.support.v7.widget.Toolbar
            android:id="@+id/da_toolbar"
            app:popupTheme="@style/AppTheme.PopupOverlay"
            app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
            xmlns:android="http://schemas.android.com/apk/res/android"
            xmlns:app="http://schemas.android.com/apk/res-auto"
            android:layout_width="match_parent"
            app:layout_collapseMode="pin"
            android:layout_height="?attr/actionBarSize">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textSize="20sp"
                android:textColor="?attr/colorAccent"
                android:id="@+id/toolbar_title"/>

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

    </android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>


<ScrollView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_below="@+id/rel_dash_icon"
    app:layout_behavior="@string/appbar_scrolling_view_behavior" >

.........................

</ScrollView>

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

java file..

Typeface ubuntuC = Typeface.createFromAsset(getAssets(), "ubuntuC.ttf");
Toolbar toolbar = (Toolbar) findViewById(R.id.da_toolbar);
    toolbar.setTitle("");
    setSupportActionBar(toolbar);

    toolbar_title = (TextView)toolbar.findViewById(R.id.toolbar_title);
    toolbar_title.setTypeface(ubuntuC);

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
        getSupportActionBar().setHomeAsUpIndicator(getResources().getDrawable(R.drawable.abc_ic_ab_back_mtrl_am_alpha, null));
    else
        getSupportActionBar().setHomeAsUpIndicator(getResources().getDrawable(R.drawable.abc_ic_ab_back_mtrl_am_alpha));
    getSupportActionBar().setDisplayHomeAsUpEnabled(true);

    final CollapsingToolbarLayout collapsingToolbarLayout = (CollapsingToolbarLayout) findViewById(R.id.collapsing_toolbar);
    AppBarLayout appBarLayout = (AppBarLayout) findViewById(R.id.appbar);
    appBarLayout.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
        boolean isShow = false;
        int scrollRange = -1;

        @Override
        public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
            if (scrollRange == -1) {
                scrollRange = appBarLayout.getTotalScrollRange();
            }
            if (scrollRange + verticalOffset == 0) {
//                    collapsingToolbarLayout.setTitle("Dashboard");
                toolbar_title.setText("Dashboard");
                isShow = true;
            } else if(isShow) {
//                    collapsingToolbarLayout.setTitle("");
                toolbar_title.setText("");
                isShow = false;
            }
        }
    });

I just want a toolbar with a center image which on collapse will display the title. Title will have custom font. Now, is there a better way doing this?

回答1:

Following piece of code worked for me added post Runnable on AppBarLayout

mAppBar.addOnOffsetChangedListener(new AppBarStateChangeListener() {
            @Override
            public void onStateChanged(AppBarLayout appBarLayout, final int state, int done) {
                mAppBar.post(new Runnable() {
                    @Override
                    public void run() {
                        if (state == COLLAPSED) {
                            mToolBarTitle.setText(model.getTitle());
                        } else if (state == EXPANDED || state == IDLE) {
                            mToolBarTitle.setText("");
                        }
                    }
                });
            }
        });


回答2:

onOffsetChanged() is called so many times and android is throwing that warning because you are requesting layout as part of toolbar_title.setText("Dashboard"); . What you should do is , use a boolean flag and call it only once if the condition is met or check the VISIBILITY flag for the toolbar text view ,something like this.

  1. Add textview inside your toolbar layout under CollapsingLayout

       < android.support.v7.widget.Toolbar
         android:id="@+id/toolbar"
         android:layout_width="match_parent"
         android:layout_height="?attr/actionBarSize"
         app:layout_collapseMode="pin"
         app:popupTheme="@style/ThemeOverlay.AppCompat.Light">
    
             <TextView
                android:id="@+id/toolbar_title"
                style="@style/Toolbar.Title"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:visibility="gone"
                android:layout_gravity="center" />
    
      </android.support.v7.widget.Toolbar>
    
  2. In your activity or fragment class inside OnOffsetChangedListener() modify some thing like below with check. This will stop the warnings.

    appBarLayout.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
      @Override
      public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
        if (verticalOffset == toolbar.getHeight() - collapsingToolbarLayout.getHeight()) {
    
          if (textView.getVisibility() != View.VISIBLE) {
            textView.setVisibility(View.VISIBLE);
            textView.setText(title); // show toolbar title
          }
        } else {
          if (textView.getVisibility() != View.GONE) {
            textView.setVisibility(View.GONE); // hide title bar
          }
        }
      }
    });
    

    }



回答3:

I had the same problem with my onOffsetChangedListener. To fix it, I had to wrap my code a runnable that runs on the UI thread. So with yours you would have to do the following:

    @Override
    public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
        getActivity().runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    if (scrollRange == -1) {
                        scrollRange = appBarLayout.getTotalScrollRange();
                    }
                    if (scrollRange + verticalOffset == 0) {
                        collapsingToolbarLayout.setTitle("Dashboard");
                        toolbar_title.setText("Dashboard");
                        isShow = true;
                    } else if(isShow) {
                        collapsingToolbarLayout.setTitle(" ");
                        toolbar_title.setText(" ");
                        isShow = false;
                    }
                }
        });

Note the space between the quotation marks in setTitle and setText. I believe you need a space or it won't work. Also, make sure in your xml that you have

app:titleEnabled="false"

on your CollapsingToolbarLayout.

Hope that solved the problem :)



回答4:

I had the same problem and this page gives me a hint : here

I removed the collapsingToolbarLayout.setTitle("Dashboard"); collapsingToolbarLayout.setTitle(" "); and created two styles in styles.xml:

<style name="CustomCollapsingCollapsed">
   <item name="android:textColor">@color/colorText</item>
</style>
<style name="CustomCollapsingExpanded">
  <item name="android:textColor">@color/transparent</item>
</style>

And use them in

<android.support.design.widget.CollapsingToolbarLayout
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   app:contentScrim="@color/colorAccent"
   app:layout_scrollFlags="scroll|exitUntilCollapsed"
   app:expandedTitleTextAppearance="@style/CustomCollapsingExpanded"
   app:collapsedTitleTextAppearance="@style/CustomCollapsingCollapsed"
   >

No logs anymore... Hope it helps!



回答5:

You can actually set a title colour for collapsed and expanded mode which will transition between the two when the toolbar is collapsing.

In your case rather then manually handling the collapse/expansion and setting the title you could set the expanded title colour to transparent and the collapsed title colour to whatever you originally wanted it to be.

So now when expanded the toolbar title is invisible and when collapsed the toolbar title is visible.



回答6:

I had the same problem in the implementation of the listener onOffsetChanged.

What I did basically is a flag before assigning the new values. Where once the specific values of the layout parameters are assigned, they are saved and then before being reassigned, they are compared with the old values.

private ConstraintLayout.LayoutParams params;
private float horizontalBias = 0;
private float verticalBias = 0;

@Override
    public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {

    params = (ConstraintLayout.LayoutParams) mOrderPictureDetail.getLayoutParams();

    ...

     if (params.horizontalBias != horizontalBias && params.verticalBias != verticalBias) {
         horizontalBias = params.horizontalBias;
         verticalBias = params.verticalBias;
         mOrderPictureDetail.setLayoutParams(params);
     }

}

GL

Source



回答7:

 appBarLayout.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
        boolean isShow = false;
        int scrollRange = -1;

        @Override
        public void onOffsetChanged(final AppBarLayout appBarLayout, final int verticalOffset) {
            if (scrollRange == -1) {
                scrollRange = appBarLayout.getTotalScrollRange();
            }
            if (scrollRange + verticalOffset == 0) {
                if (mToolbarTitle.getVisibility() == View.GONE) {
                    mToolbarTitle.setVisibility(View.VISIBLE);
                    mCollapsingToolbarLayout.setTitle("昵称");
                    mToolbarTitle.setText("昵称");
                }
                isShow = true;
            } else if (isShow) {
                mToolbarTitle.setVisibility(View.GONE);
                isShow = false;
            }
        }
    });

Hope that solved the problem