Child Views 花太多时间在屏幕上画画

Child Views Taking too much time to draw on screen

我有 android 布局设计,其中我有一个 AppBar 布局,其中将包含一个 Toolbar 和另一个 LinearLayout 带圆圈的设计 Drawable作为 TextView。这是相同的高度。因此,当我尝试获取 ToolbarAppBar height/width 时,它只返回 0。

activity_main.xml:

    <android.support.design.widget.AppBarLayout
        android:id="@+id/app_bar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?actionBarSize"
            android:background="@color/colorPrimary"
            app:layout_scrollFlags="enterAlways" />
       <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="?actionBarSize"
            android:layout_gravity="center_horizontal"
            android:background="@color/colorPrimary"
            android:orientation="horizontal"
            app:layout_scrollFlags="enterAlways">

            <LinearLayout
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_gravity="center_horizontal"
                android:layout_marginLeft="2dp"
                android:layout_weight="1"
                android:orientation="horizontal">

                <TextView
                    android:id="@+id/marked_questions"
                    style="@style/textview_summary_omr_toolbar_count"
                    android:background="@drawable/bg_percentage_default"
                    android:text="18" />

                <TextView
                    style="@style/textview_heading_summary_omr_toolbar"
                    android:text="Marked" />
            </LinearLayout>

            <LinearLayout
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_gravity="center_horizontal"
                android:layout_marginLeft="2dp"
                android:layout_weight="1"
                android:orientation="horizontal">

                <TextView
                    android:id="@+id/correct"
                    style="@style/textview_summary_omr_toolbar_count"
                    android:background="@drawable/bg_percentage_6"
                    android:text="18"

                    />

                <TextView
                    style="@style/textview_heading_summary_omr_toolbar"
                    android:text="Correct" />
            </LinearLayout>

            <LinearLayout
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_gravity="center_horizontal"
                android:layout_marginLeft="2dp"
                android:layout_weight="1"
                android:orientation="horizontal">

                <TextView
                    android:id="@+id/wrong"
                    style="@style/textview_summary_omr_toolbar_count"
                    android:background="@drawable/bg_percentage_wrong"
                    android:text="18" />

                <TextView
                    style="@style/textview_heading_summary_omr_toolbar"
                    android:text="Wrong" />
            </LinearLayout>


            <LinearLayout
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_gravity="center_horizontal"
                android:layout_marginLeft="2dp"
                android:layout_weight="1"
                android:orientation="horizontal">

                <TextView
                    android:id="@+id/conflicts"
                    style="@style/textview_summary_omr_toolbar_count"
                    android:background="@drawable/bg_percentage_3"
                    android:text="0" />

                <TextView
                    style="@style/textview_heading_summary_omr_toolbar"
                    android:text="Conflicts" />
            </LinearLayout>

            <LinearLayout
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_gravity="center_horizontal"
                android:layout_marginLeft="2dp"
                android:layout_weight="1"
                android:orientation="horizontal">

                <TextView
                    style="@style/textview_summary_omr_toolbar_count"
                    android:text="Score:"
                    android:textColor="@android:color/white" />

                <TextView
                    android:id="@+id/marks_scored"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_gravity="center_vertical"
                    android:gravity="center"
                    android:text="18/30"
                    android:textColor="@android:color/white"
                    android:textSize="18sp" />

            </LinearLayout>


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

MainActivity.java: 在这里,我正在初始化 ToolbarAppBar 并尝试在日志中打印两者的宽度和高度,这让我返回零。

public class MainActivity extends AppCompatActivity {
  private Toolbar toolbar;
  private AppBarLayout app_bar;
protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_main);
       app_bar = (AppBarLayout) findViewById(R.id.app_bar);
       toolbar = (Toolbar) findViewById(R.id.toolbar);
       logToolbarLayoutParams();

}
private void logToolbarLayoutParams() {
        Log.d(TAG,"Toolbar width/Height"+this.toolbar.getWidth()+"/"+this.toolbar.getHeight());
        Log.d(TAG,"App_bar width/height"+this.app_bar.getWidth()+"/"+this.app_bar.getHeight());

}

我在 Whosebug 中也提到了这个问题。

final AppBarLayout app_bar = (AppBarLayout) findViewById(R.id.app_bar);
                ViewTreeObserver vto = app_bar.getViewTreeObserver();
                vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
                    @Override
                    public void onGlobalLayout() {
                        app_bar.getViewTreeObserver().removeGlobalOnLayoutListener(this);
                        Log.d(TAG, "Global layout");
                        logToolbarLayoutParams();
            }
        });

如果我在我的 MainActivity 中添加以下代码,它会得到 Toolbar 的正确高度和宽度。从我之前提到的Whosebug问题,我知道,视图没有绘制在屏幕上,所以我必须等待。但我的疑问是,我指定的设计不是一个复杂的设计吗? 为什么在屏幕上画画要花很多时间?我错过了什么吗?

我已经使用 Hierarchy Viewer 找出导致子布局加载缓慢的原因。我发现我正在使用多个 LinearLayout,其中单个 RelativeLayout 可用于获得相同的结构。我改变了我的布局设计如下。 我仍然面临同样的问题。

更新:activity_main.xml:

<android.support.design.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.AppBarLayout
        android:id="@+id/app_bar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?actionBarSize"
            android:background="@color/colorPrimary"
            app:layout_scrollFlags="enterAlways" />
   <RelativeLayout
         android:id="@+id/summary_bottom_sheet"
         android:layout_width="match_parent"
         android:layout_height="?actionBarSize"
         android:layout_gravity="center_horizontal"
         android:background="@color/colorPrimary">


         <TextView
             android:id="@+id/marked_questions"
             style="@style/textview_summary_omr_toolbar_count"
             android:layout_alignParentLeft="true"
             android:layout_alignParentStart="true"
             android:background="@drawable/bg_percentage_default"
             android:text="18" />

         <TextView
             android:id="@+id/marked_textview"
             style="@style/textview_heading_summary_omr_toolbar"
             android:layout_toEndOf="@+id/marked_questions"
             android:layout_toRightOf="@+id/marked_questions"
             android:text="Marked" />


         <TextView
             android:id="@+id/correct"
             style="@style/textview_summary_omr_toolbar_count"
             android:layout_toEndOf="@+id/marked_textview"
             android:layout_toRightOf="@+id/marked_textview"
             android:background="@drawable/bg_percentage_6"
             android:text="18"

             />

         <TextView
             android:id="@+id/correct_textview"
             style="@style/textview_heading_summary_omr_toolbar"
             android:layout_toEndOf="@+id/correct"
             android:layout_toRightOf="@+id/correct"
             android:text="Correct" />


         <TextView
             android:id="@+id/wrong"
             style="@style/textview_summary_omr_toolbar_count"
             android:layout_toEndOf="@+id/correct_textview"
             android:layout_toRightOf="@+id/correct_textview"
             android:background="@drawable/bg_percentage_wrong"
             android:text="18" />

         <TextView
             android:id="@+id/wrong_textview"
             style="@style/textview_heading_summary_omr_toolbar"
             android:layout_toEndOf="@+id/wrong"
             android:layout_toRightOf="@+id/wrong"
             android:text="Wrong" />


         <TextView
             android:id="@+id/conflicts"
             style="@style/textview_summary_omr_toolbar_count"
             android:layout_toEndOf="@+id/wrong_textview"
             android:layout_toRightOf="@+id/wrong_textview"
             android:background="@drawable/bg_percentage_3"
             android:text="0" />

         <TextView
             android:id="@+id/conflicts_textview"
             style="@style/textview_heading_summary_omr_toolbar"
             android:layout_toEndOf="@+id/conflicts"
             android:layout_toRightOf="@+id/conflicts"
             android:text="Conflicts" />


         <TextView
             android:id="@+id/score_textview"
             style="@style/textview_summary_omr_toolbar_count"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_toEndOf="@+id/conflicts_textview"
             android:layout_toRightOf="@+id/conflicts_textview"
             android:background="@null"
             android:gravity="center"
             android:text="Score:"
             android:textColor="@android:color/white" />

         <TextView
             android:id="@+id/marks_scored"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_centerVertical="true"
             android:layout_gravity="center_vertical"
             android:layout_toEndOf="@+id/score_textview"
             android:layout_toRightOf="@+id/score_textview"
             android:gravity="center"
             android:text="18/30"
             android:textColor="@android:color/white"
             android:textSize="18sp" />
   </RelativeLayout>
 </android.support.design.widget.AppBarLayout>
 </android.support.design.widget.CoordinatorLayout>

下面是层级查看器我正在获取的图片

您尝试在将视图附加到屏幕之前获取其宽度和高度 您可以在 onAttach 方法中创建自定义视图和 wath

我刚刚在同一个案例中解决了同样的问题。 ViewTreeObserver.OnGlobalLayoutListener() 的所有解决方案看起来都很脏,对我不起作用。了解当前屏幕宽度和高度的最简单方法如下所示:

toolbar.post(new Runnable() {
        @Override
        public void run() {
            int headerWidth = toolbar.getWidth();
            int headerHeight = toolbar.getHeight();
            // do whatever you want ...
        }
    });

view.post() 会将您的代码添加到 视图消息队列的末尾 ,此时所有尺寸都已计算完毕。恕我直言,这显然更容易理解,然后 OnGlobalLayoutListener()。 希望对你有所帮助。

无论如何,这个解决方案只是 "delayed getting sizes",就像 OnGlobalLayoutListener()。但这是系统的正常行为,在屏幕上绘制视图需要一些时间。最好的方法是不要期望计算尺寸 - 相反,您应该协调 .xml 文件中视图之间的所有关系。 WRAP_CONTENTMATCH_PARENT 等属性是解决此类问题的最简单方法

绘制视图需要相当长的时间,但我们可以通过删除嵌套布局来减少时间,并通过使用 RelativeLayout 或 Constrain 布局使视图层次结构平坦。

寻找透支。 Select "Show overdraw areas" 在设置中的调试 GPU 透支开发者选项中。

研究 this and this 来自 developer.android 关于优化布局主题的重要文章

你想错了。您的布局没有任何问题(因为它与您的问题有关)。为了证明这一点,彻底改变你的布局再试一次

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    RecyclerView recyclerView = (RecyclerView)findViewById(R.id.recyclerView);
    Log.d("SomeTag", "w: " + recyclerView.getWidth() + ", h: " + recyclerView.getHeight());
    Log.d("SomeTag", "mw: " + recyclerView.getMeasuredWidth() + ", mh: " + recyclerView.getMeasuredHeight());
}

只有 1 个 child 的视图的输出仍然是

12-14 10:17:04.902 3004-3004/? D/SomeTag: w: 0, h: 0

12-14 10:17:04.902 3004-3004/? D/SomeTag: mw: 0, mh: 0

所以你的布局没有任何问题(除了你用词不当。你调用方法 logToolbarLayoutParams,但你根本没有访问布局参数。你试图获取视图的属性 object 在布局传递发生之前。也许这是你的困惑)。所以你缺少的是对 Android 的布局系统、视图组和视图的理解。 Romain Guy 曾在 Devoxx 上就如何构建自定义 FlowLayout 进行了一场非常酷的演讲。网站已关闭或视频已被拉出,但 here's the code in GitHub 如果您查看 onLayout 方法,您可以看到 ViewGroup 循环遍历所有 children 并调用 child.layout() .在调用此方法之前,view.getWidth 和 view.getHeight 方法将 return 0。并且 OS 在调度它时调用此方法。所以基本上,您甚至在 OS 安排布局之前就试图立即访问宽度和高度。自己做一个自定义视图组,添加一些断点,试一试。

toolbar.getWidth() 会告诉你视图的宽度 它已经 测量 铺设出.
在膨胀视图后,它的宽度和高度将始终为 0,因为 measure()layout() 都没有发生。

protected void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   setContentView(R.layout.activity_main);
   toolbar = (Toolbar) findViewById(R.id.toolbar);

   // check after inflation -> will be 0
   logToolbarLayoutParams();
}

onGlobalLayout() 报告了正确的数据,因为正如其名称所暗示的那样,onMeasure() onLayout() 都被调用了。视图的维度是已知的。这确实 意味着视图已被 绘制onDraw() 是第三步,也是 最后 步。

它也不会影响您的布局的复杂程度或您使用的视图。测量和布局过程不会在视图 inflation 之后立即发生。


要获取和使用正确的视图尺寸,您有 3 个选项:

  1. 像您已经建议的那样使用ViewTreeObserver.OnGlobalLayoutListener#onGlobalLayout()
  2. 稍后检查大小,当您可以确定视图已绘制时,(例如,在用户交互时,在 OnClickListener 中)
  3. 自定义并创建您自己的 View。您将有机会自己处理所有 onMeasureonLayoutonDraw