【问题标题】:How to increase layouting performance如何提高布局性能
【发布时间】:2011-07-01 01:00:10
【问题描述】:

我有动态且相当大的内容,需要在用户交互时填充(单击下一页/上一页)。 在 myTableLayout 上以编程方式显示的动态内容(参见下面的 xml)。每次用户点击下一个/上一个 页面然后我打电话给myTableLayout.removeAllViews(),做一些事情并重新填充myTableLayout.addView()。 在小内容上它绘制得非常快,但我注意到如果填充大内容会延迟两到三秒。

我已经看到一个应用程序与我的功能相同(内容也很大),但它的分页速度真的很快 即使在低端设备上。

需要一些关于如何提高布局性能的建议。

谢谢。

注意:上方的TableLayout用作标题(标题),下方的LinearLayout是放置内容的地方。

<TableLayout
    android:layout_width="fill_parent"
    android:layout_height="45dip"
    android:stretchColumns="0,2"
    android:shrinkColumns="0,2"
    >
        <TableRow>
            <LinearLayout
                android:id="@+id/llLeft"
                android:layout_width="wrap_content"
                android:layout_height="fill_parent"
            >
                <TextView
                    android:id="@+id/tvLeft1"
                    android:layout_width="fill_parent"
                    android:layout_height="fill_parent"
                />
            </LinearLayout>

            <LinearLayout
                android:id="@+id/llCenter"
                android:layout_width="wrap_content"
                android:layout_height="fill_parent"
            >
                <TextView
                    android:id="@+id/tvCenter1"
                    android:layout_width="wrap_content"
                    android:layout_height="fill_parent"
                />

                <TextView
                    android:id="@+id/tvCenter2"
                    android:layout_width="wrap_content"
                    android:layout_height="fill_parent"
                />
            </LinearLayout>

            <LinearLayout
                android:id="@+id/llRight"
                android:layout_width="wrap_content"
                android:layout_height="fill_parent"
            >
                <TextView
                    android:id="@+id/tvRight1"
                    android:layout_width="fill_parent"
                    android:layout_height="fill_parent"
                />
            </LinearLayout>
    </TableRow>
</TableLayout>

<LinearLayout
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
>
    <com.MyScrollView
        android:id="@+id/myScrollView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        >

        <TableLayout
            android:id="@+id/**myTableLayout**"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:shrinkColumns="1"
            android:stretchColumns="1"
            />

    </com.MyScrollView>
</LinearLayout>

这是创建动态内容的代码。从内存(ArrayList)中获取的内容应该不是问题。

private void gotoPage() {
    myTableLayout.removeAllViews();
    GlobalVar g = GlobalVar.getInstance();
    ArrayList<String> arrScripts = g.getDatasMap().get(SCRIPT_INDEX);
    ArrayList<String> arrTrans = g.getTranslationMap().get(SCRIPT_INDEX);
    Log.i(APP_NAME, "finished preparing data");

    try {

        // Data Title
        tvCenter1.setText(arrScripts.get(0));
        tvCenter2.setText(" (" + SCRIPT_INDEX + ")");

        int countScripts = arrScripts.size();
        for(int i=1; i<countScripts; i++) {

            // Row Script
            TableRow trScript = new TableRow(this);
            trScript.setPadding(0, 5, 0, 0);
            TextView tvIndex = new TextView(this);
            tvIndex.setGravity(Gravity.CENTER);
            tvIndex.setWidth(40);
            tvIndex.setText("" + i);

            TextView tvScript = new TextView(this);
            tvScript.setGravity(Gravity.RIGHT);
            tvScript.setTypeface(g.getFontTypeFace());
            tvScript.setTextSize(FONT_SIZE);
            tvScript.setPadding(0, 0, 5, 0);
            tvScript.setText(arrScripts.get(i));

            trScript.addView(tvIndex);
            trScript.addView(tvScript);

            TableRow trTrans = null;

            if(IS_USE_TRANSLATION) {
                // Row Translation
                trTrans = new TableRow(this);

                TextView tvBlank = new TextView(this);
                tvBlank.setPadding(0, 0, 0, 5);
                TextView tvTrans = new TextView(this);
                tvTrans.setPadding(0, 0, 0, 5);
                tvTrans.setText(arrTrans.get(i));

                trTrans.addView(tvBlank);
                trTrans.addView(tvTrans);
            }

            // Place for line separator
            View vSep = new View(this);
            ViewGroup.LayoutParams p = new ViewGroup.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
            p.height = 3;
            vSep.setLayoutParams(p);

            // Combine rows to table
            if(IS_USE_SCRIPT) myTableLayout.addView(trScript);
            if(IS_USE_TRANSLATION) myTableLayout.addView(trTrans);
            myTableLayout.addView(vSep);
        }
    } catch (Exception e) {
        Log.e(APP_NAME, e.getMessage());
    }
}

这里是traceview result

【问题讨论】:

    标签: android performance layout


    【解决方案1】:

    我同意 @amplify91 的观点,它可能来自生成内容,但您可能想要查看的其他内容之一是您正在使用的布局数量。基本思想是“越少越好”。您可以查看一些谷歌layout tricks

    示例:我看到您的标题是 tablelinearlayouts 和 textviews。这不能通过Relative layouttextviews 作为孩子来解决吗?

    您将保存一些视图,以及布局树中的一些“深度”。

    【讨论】:

    • 作为 traceview 的结果,header 没有贡献显着的性能因素。它不在循环内(仅创建一次)。
    • 是的,但基础知识仍然适用于其他视图。您可以使用层次结构查看器检查最终视图是如何生成的,然后检查您是否可以对最终结果进行一些“预算削减”。如果我没看错的话,你有一个带有滚动视图的线性布局和一个带有文本视图行的表格吗?也许你可以减少其中一些,用一些文本视图制作一个相对布局?
    • 似乎我无法以编程方式设置布局参数:RelativeLayout doc。给定的 xml 属性没有“相关方法”。还是我错了?
    • 除了我坚持你只会在xml中放一个RelativeLayout,然后在代码中添加TextViews,你可以只使用layoutParams的构造函数,或者调用“addrule”:它们都在该手册中,但here 是一个随机示例(问题与它无关,但该代码中有相对布局和一些添加规则;)
    • 我已经完全摆脱了 TableLayout 和 TableRow 并使用 RelativeLayout 来实现我的案例。注意到显着的性能提升。谢谢。
    【解决方案2】:

    如果您只想以格式良好的表格格式添加文本(所有列自动调整大小等),则根本不要使用 TableLayout,而只需使用 java.util.Formatter 之类的东西将文本格式化为桌子形状。这需要一些准备工作,例如计算每列的最大字符串长度,但最终会带来很大的性能提升,因为您基本上将视图使用量减少到单个 TextView(大约 2 秒即可将活动加载到几乎即时)。

    例如:

    // Compute max string length of each column
    for (Entry entry : entries) {
        if (entry.column1.length() > column1MaxLength) {
            column1MaxLength = entry.column1.length();
        }
        ...
        if (entry.columnN.length() > columnNMaxLength) {
            columnNMaxLength = entry.columnN.length();
        }
    }
    
    // Formatter internally stores output string in StringBuilder
    Formatter formatter = new Formatter();
    
    // Construct table header
    formatter.format("%1$" + column1MaxLength + "s", "Column 1 Title");
    ...
    formatter.format("%1$" + columnNMaxLength + "s", "Column N Title");
    
    // Add each entry into table
    for (Entry entry : entries) {
        formatter.format("%1$" + column1MaxLength + "s", entry.column1.value);
        ...
        formatter.format("%1$" + columnNMaxLength + "s", entry.columnN.value);
    }
    
    // Get string representing formatted table from Formatter and put into TextView
    textView.setText(formatter.toString());
    

    【讨论】:

      【解决方案3】:

      简单地说,就是把 TableLayout 和 TableRow 和 TextView 干掉!

      如果您从 Eclipse 收集分析数据,您会发现大部分 CPU/实时时间都花在 TabletLayout 和 TextView 的 addView() 方法上。来电!

      每次我用自定义实现替换 TableLayout 和 TextView 都会带来巨大的性能提升!

      TextView应该换成自定义View,TableLayout应该换成自定义ListView。

      编辑:我根据需要创建了一些自定义视图。简单地说,我已经用 onDraw() 和 onMeasure() 方法覆盖了 View 类:

      在下面的代码中,painter 在静态构造函数中创建一次,textBaseline 和 textHeight 也是如此,因为我为所有这些自定义“文本”视图使用单一字体大小。

      @Override
      protected void onDraw(Canvas canvas) 
      {
          final int width = getWidth();
          final float size = painter.measureText(innerText);
      
          canvas.drawText(innerText, width - size - 5, textBaseline, painter);
      
          if (underline)
          {
              canvas.drawLine(0, canvas.getHeight() - 1, canvas.getWidth(), canvas.getHeight() - 1, painter);
          }
      }
      
      @Override
      protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) 
      {
          int w = MeasureSpec.getSize(widthMeasureSpec);
          int h = MeasureSpec.getSize(heightMeasureSpec);
      
          if (innerText != null && painter != null)
          {
              switch (MeasureSpec.getMode(widthMeasureSpec))
              {
                  case MeasureSpec.EXACTLY:
                      break;
                  case MeasureSpec.AT_MOST:
                  case MeasureSpec.UNSPECIFIED:
                      w = (int)( (painter.measureText(innerText) + 5) );
                      break;
              }
          }
          switch (MeasureSpec.getMode(heightMeasureSpec))
          {
              case MeasureSpec.EXACTLY:
                  break;
              case MeasureSpec.AT_MOST:
              case MeasureSpec.UNSPECIFIED:
                  h = (int)( textHeight );
                  break;
          }
      
          setMeasuredDimension(w, h);
      }
      

      【讨论】:

        【解决方案4】:

        我认为你应该用Debug.startMethodTracing("xx")Debug.stopMethodTracing() 调用来包围你的代码,然后使用traceview 来找出发生了什么。其他一切都只是猜测。

        【讨论】:

        • 这是跟踪结果:link。当通过该方法迭代添加 660 个视图时,我看到了一致的模式。那里的大绿点来自 Dalvik GC。所以 2.76 秒确实来自迭代添加的视图。建议?
        【解决方案5】:

        我会说延迟不在于从 XML 构建布局,而在于生成动态内容。它是什么样的内容?你能发布一些你的代码吗?如果您从网站访问它,您可能需要预取它。如果您是在更改页面时创建的,则可能与创建它的方式有关。

        【讨论】:

          猜你喜欢
          • 2018-11-15
          • 2021-10-14
          • 2021-12-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-05-21
          相关资源
          最近更新 更多