【问题标题】:Create large Android layout programmatically in background在后台以编程方式创建大型 Android 布局
【发布时间】:2021-01-16 02:11:58
【问题描述】:

我必须以编程方式在 Android 中创建一个大型布局,所以我想在后台进行。 此布局是在我的 XML 中定义的垂直 LinearLayout,它将包含大量行。

这是布局容器(在我的 XML 中定义):

private LinearLayout gridL;
        
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    ...
    gridL = (LinearLayout)_fragment.findViewById(R.id.grid);
    ...
}

这是填充此布局的 Thread 类:

private class CreateGridThread extends Thread {

        public void run() {
            Looper.prepare();
            createGrid();
            handler.sendEmptyMessage(101);
        }
    }

我这样称呼这个类:

CreateGridThread gridThread = new CreateGridThread();
gridThread.start();

在 createGrid() 内部,我将组件直接添加到 gridL,所以我得到一个“CalledFromWrongThreadException:只有创建视图层次结构的原始线程才能触及它的视图。”例外。

所以,为了避免这种情况,我创建了一个辅助布局:

private LinearLayout gridLAux;

我更改了我的 createGrid(),因此所有组件都添加到此布局中,而不是 gridL。这是我的 createGrid() 方法(有一些小版本):

public void createGrid()
{
    gridLAux = new LinearLayout(myActivity);
    gridLAux.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
    gridLAux.setOrientation(LinearLayout.VERTICAL);

    LinearLayout currentLayout = null;
    int lastIndex = 0;
    for(int i = 0; i < myData.size(); i++)
    {
        Bundle b = myData.get(i);

    // Here I read my data

        // 3 columns
        lastIndex = i % 3;
        if(lastIndex == 0)
        {
            // Container for the whole row
            currentLayout = new LinearLayout(myActivity);
            LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,
                                                                                          LayoutParams.WRAP_CONTENT);
            currentLayout.setLayoutParams(params);
            currentLayout.setOrientation(LinearLayout.HORIZONTAL);
            currentLayout.setWeightSum(3);
            gridLAux.addView(currentLayout);
        }

        // Container for a cell
        RelativeLayout rowL = new RelativeLayout(myActivity);
        LinearLayout.LayoutParams params1 = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,
                                                                          LinearLayout.LayoutParams.WRAP_CONTENT);
        params1.weight = 1;
        rowL.setLayoutParams(params1);
        rowL.setTag(i);
        currentLayout.addView(rowL);

        // Container for 2 images
        LinearLayout imagesL = new LinearLayout(myActivity);
        RelativeLayout.LayoutParams params2 = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT,
                RelativeLayout.LayoutParams.WRAP_CONTENT);
        params2.addRule(RelativeLayout.ALIGN_PARENT_TOP);
        params2.setMargins(0, 0, 0, 0);
        imagesL.setLayoutParams(params2);
        imagesL.setOrientation(LinearLayout.HORIZONTAL);
        imagesL.setWeightSum(2);
        imagesL.setId(R.id.text);
        rowL.addView(imagesL);

        // Left image
        ImageView leftIV = new ImageView(myActivity);
        LinearLayout.LayoutParams params3 = new LinearLayout.LayoutParams(
                myActivity.getResources().getDimensionPixelSize(R.dimen.img_width),
                myActivity.getResources().getDimensionPixelSize(R.dimen.img_height));
        params3.weight = 1;
        leftIV.setLayoutParams(params3);
        leftIV.setAdjustViewBounds(true);
        leftIV.setScaleType(ScaleType.FIT_XY);
        leftIV.setImageResource(R.drawable.ico_left);
        imagesL.addView(leftIV);

        // Right image
        ImageView rightIV = new ImageView(myActivity);
        LinearLayout.LayoutParams params4 = new LinearLayout.LayoutParams(
                myActivity.getResources().getDimensionPixelSize(R.dimen.img_width),
                myActivity.getResources().getDimensionPixelSize(R.dimen.img_height));
        params4.weight = 1;
        rightIV.setLayoutParams(params4);
        rightIV.setAdjustViewBounds(true);
        rightIV.setScaleType(ScaleType.FIT_XY);
        rightIV.setImageResource(R.drawable.ico_right);
        imagesL.addView(rightIV);
    }
    if(currentLayout != null)
    {
        for(int i = 0; i < 2 - lastIndex; i++)
        {
            LinearLayout imgWrapper = new LinearLayout(myActivity);
            LinearLayout.LayoutParams paramsLayoutWrapper = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
            paramsLayoutWrapper.weight = 1;
            imgWrapper.setLayoutParams(paramsLayoutWrapper);
            currentLayout.addView(imgWrapper);
        }
    }
}

最后,任务结束后我调用 Handler:

   private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case 101:
                    gridL.addView(gridLAux);
                     break;
             }
        }
    };

因此,我在后台将所有组件添加到辅助布局中,并在主线程中将此辅助布局添加到好的布局中。

但是,我还是得到了

"CalledFromWrongThreadException: 只有原来创建的线程 视图层次结构可以触及其视图。” addView 调用中的异常 在 createGrid() 内部

。我做错了什么?

【问题讨论】:

  • 是在我的 XML 中定义的垂直线性布局,它将包含大量行。 请使用列表视图或回收器视图跨度>
  • 是的,我认为,但由于设计要求,此布局必须完全显示在 ScrollView 内部,其上方和下方有其他组件,所以如果我使用 listview 或 recycleview 我不会有它们的优势,因为它们会同时显示所有行。
  • 没有但是。你正在描述一个列表,你甚至说你想要很多项目。列表可以有页眉/页脚,我相信您会找到适合您的任何设计要求的解决方案。
  • 我来看看这个方法。

标签: android multithreading android-layout


【解决方案1】:

你总是必须在 UI 线程上添加视图,所以在 MainLooper 上使用 Handler

new Handler(Looper.getMainLooper()).post(new Runnable() {
     @Override
     public void run() {
          gridL.addView(gridLAux);
     }
});

或者,如果您可以访问活动,则可以使用活动的runOnUiThread 方法

【讨论】:

    【解决方案2】:

    我在创建大型动态表单时遇到了类似的问题,我发现某些 UI 小部件只能从主线程创建,而其他 UI 小部件可以从外部创建而没有问题。就我而言,嵌套的线性布局包括您可以命名的各种小部件。我的解决方案是,在主线程执行创建循环(如 Romadro 所说),但不是同时执行每个元素以避免阻塞 UI 并允许我在允许响应式 UI 的同时向用户显示处理消息。希望对您有所帮助。

    【讨论】:

      【解决方案3】:

      我在您的代码中发现了一些问题,您应该注意到它们:

      1. 您应该始终在UI thread 中创建您的View(因为同步)。虽然我在您的观点中没有看到任何大人物,但您可以尝试在 xml 中定义它,然后在 inflate 中定义它。
      2. 您的Handler 将在您称为new Handler 的线程中。因此,如果您在工作线程中调用new Handler -> 处理程序也将在工作线程中。 (您可以改用new Handler(Looper.getMainLooper()))
      3. 我看到你的循环创建a lot of big items,你为什么不试试RecyclerView?它将大大提高您的布局性能。

      【讨论】:

        【解决方案4】:

        Romadro 所说的是正确的,您也可以在 LinearLayout 等布局中膨胀或添加视图,而不会使用 AsyncTask 或 Concurrent 阻塞 UI 线程。看看我的回答:https://stackoverflow.com/a/65741410/12204620你可能会有所了解。

        【讨论】:

          猜你喜欢
          • 2012-08-07
          • 2018-10-08
          • 2012-10-01
          • 1970-01-01
          • 2019-06-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多