Android onLayout() 和 AsyncTask() 不能一起工作

发布于 2024-12-23 02:48:15 字数 7238 浏览 1 评论 0原文

我需要一个带有固定标题的可滚动表格,所以我关注了这个很棒的博客,一切都很好。

这个想法是使用一张表作为标题,一张表用于滚动视图中添加的内容,两者都在自定义的 LinearLayout 中。在自定义的LinearLayout中,我们将重写onLayout()来获取每行的最大宽度,并为标题和内容表的每行设置宽度。

这是活动及其布局:

package com.stylingandroid.ScrollingTable;

import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TableLayout;
import android.widget.TableRow;

public class ScrollingTable extends LinearLayout
{
    public ScrollingTable( Context context )
    {
        super( context );
    }
public ScrollingTable( Context context, AttributeSet attrs )
{
    super( context, attrs );
}

@Override
protected void onLayout( boolean changed, int l, int t, int r, int b )
{
    super.onLayout( changed, l, t, r, b );

    TableLayout header = (TableLayout) findViewById( R.id.HeaderTable );
    TableLayout body = (TableLayout) findViewById( R.id.BodyTable );
    
    if (body.getChildCount() > 0 ) {
        TableRow bodyRow = (TableRow) body.getChildAt(0);
        TableRow headerRow = (TableRow) header.getChildAt(0);
        
        for ( int cellnum = 0; cellnum < bodyRow.getChildCount(); cellnum++ ){
            View bodyCell = bodyRow.getChildAt(cellnum);
            View headerCell = headerRow.getChildAt(cellnum);
            int bodyWidth = bodyCell.getWidth();
            int headerWidth = headerCell.getWidth();
            int max = Math.max(bodyWidth, headerWidth);
            TableRow.LayoutParams bodyParams = (TableRow.LayoutParams)bodyCell.getLayoutParams();
            bodyParams.width = max;
            TableRow.LayoutParams headerParams = (TableRow.LayoutParams)headerCell.getLayoutParams();
            headerParams.width = max;
        }       
    }
}
}

main.xml

 <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" 
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    
    <com.stylingandroid.ScrollingTable.ScrollingTable
        android:layout_width="match_parent"
        android:orientation="vertical"
        android:layout_height="match_parent">
        
        <TableLayout 
            android:layout_height="wrap_content"
            android:layout_width="match_parent" 
            android:id="@+id/HeaderTable">
        </TableLayout>
        
        <ScrollView 
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
            
            <TableLayout 
                android:layout_height="wrap_content"
                android:layout_width="match_parent" 
                android:id="@+id/BodyTable">
            </TableLayout>
            
        </ScrollView>
        
    </com.stylingandroid.ScrollingTable.ScrollingTable>
    
</LinearLayout>

主要活动

 package com.stylingandroid.ScrollingTable;

import android.app.Activity;
import android.app.ProgressDialog;
import android.graphics.Color;
import android.os.AsyncTask;
import android.os.Bundle;
import android.widget.TableLayout;
import android.widget.TableRow;

    import android.widget.TextView;
    
    public class ScrollingTableActivity extends Activity
    {
        private String[][] tableData = {
                {"header11111111111", "header2","header3","header4"},
                {"column1", "column1","column1","column1"},
                {"column1", "column1","column1","column1"},
                {"column1", "column1","column1","column1"},
            

    {"column1", "column1",

"column1","column1"},
                {"column1", "column1","column1","column1"},
                {"column1", "column1","column1","column1"},
                {"column1", "column1","column1","column1"},
                {"column1", "column1","column1","column1"},
                {"column1", "column1","column1","column1"},
                {"column1", "column1","column1","column1"},
                {"column1", "column1","column1","column1"},
                {"column1", "column1","column1","column1"}
        };
        /** Called when the activity is first created. */
        @Override
        public void onCreate( Bundle savedInstanceState )
        {
            super.onCreate( savedInstanceState );
            setContentView( R.layout.main );
            TableLayout tableHeader = (TableLayout)findViewById(R.id.HeaderTable);
            TableLayout tableBody = (TableLayout)findViewById(R.id.BodyTable);
            
        appendRows(tableHeader, tableBody, tableData);
}

private void appendRows(TableLayout tableHeader ,TableLayout tableContent, String[][] amortization) {
    int rowSize=amortization.length;
    int colSize=(amortization.length > 0)?amortization[0].length:0;
    for(int i=0; i<rowSize; i++) {
        TableRow row1 = new TableRow(this);
        
        for(int j=0; j<colSize; j++) {
            TextView c = new TextView(this);
            c.setText(amortization[i][j]);
            c.setPadding(3, 3, 3, 3);
            if (i == 0) {
                c.setTextColor(Color.BLACK);
            }
            row1.addView(c);
        }
        
        if (i == 0) { 
            row1.setBackgroundColor(Color.LTGRAY);
            tableHeader.addView(row1, new TableLayout.LayoutParams());
        } else {
            tableContent.addView(row1, new TableLayout.LayoutParams());
        }
    }
}

上面的代码工作完美(expected),但是,当我使用 AnysnTask 从服务器获取数据并稍后将数据添加到表中,我的自定义视图中的 onLayout() 不再起作用。我通过注销一些数字来模拟获取数据:

public void onCreate( Bundle savedInstanceState )
    {
        super.onCreate( savedInstanceState );
        setContentView( R.layout.main );
        
        new MyTask().execute();
    }
    
    private class MyTask extends AsyncTask<Void, Void, Void> {
        
        private ProgressDialog progressDialog;
        protected void onPreExecute() {
                progressDialog = ProgressDialog.show(ScrollingTableActivity.this,
                                  "", "Loading. Please wait...", true);
        }
        @Override
        protected Void doInBackground(Void... reportTypes) {
            for (int i = 0; i < 500; i++) { 
                System.out.println(i);
            } 
            return null;
        }
        @Override
        protected void onPostExecute(Void result) {
            progressDialog.dismiss();
            TableLayout tableHeader = (TableLayout)findViewById(R.id.HeaderTable);
            TableLayout tableBody = (TableLayout)findViewById(R.id.BodyTable);
            
            appendRows(tableHeader, tableBody, tableData);
        }
        
    }

因此,仅当我通过将其放入 onCreate() 方法中从主 UI 线程调用appendRows() 时,onLayout() 才起作用。如果我从另一个 UI 线程(在 AsyncTask 的 onPostExecute() 中)调用,则会调用 onLayout() (我通过创建一些日志来检查它),但它不会影响 GUI。我尝试了 invalidate()、forceLayout()、requestLayout() 但没有改变任何内容。wrong

我认为我们需要调用一个方法来刷新 GUI,但不知道它是什么。

I need a scrollable table with fixed header, so I followed this great blog and everything is fine.

The idea is using one table for header, one table for content added in scrollview, both of them are in a customized LinearLayout. In customized LinearLayout, we will overwrite the onLayout() to get the max width of each row and set width for each row of both header and content table.

Here is the activity and its layout:

package com.stylingandroid.ScrollingTable;

import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TableLayout;
import android.widget.TableRow;

public class ScrollingTable extends LinearLayout
{
    public ScrollingTable( Context context )
    {
        super( context );
    }
public ScrollingTable( Context context, AttributeSet attrs )
{
    super( context, attrs );
}

@Override
protected void onLayout( boolean changed, int l, int t, int r, int b )
{
    super.onLayout( changed, l, t, r, b );

    TableLayout header = (TableLayout) findViewById( R.id.HeaderTable );
    TableLayout body = (TableLayout) findViewById( R.id.BodyTable );
    
    if (body.getChildCount() > 0 ) {
        TableRow bodyRow = (TableRow) body.getChildAt(0);
        TableRow headerRow = (TableRow) header.getChildAt(0);
        
        for ( int cellnum = 0; cellnum < bodyRow.getChildCount(); cellnum++ ){
            View bodyCell = bodyRow.getChildAt(cellnum);
            View headerCell = headerRow.getChildAt(cellnum);
            int bodyWidth = bodyCell.getWidth();
            int headerWidth = headerCell.getWidth();
            int max = Math.max(bodyWidth, headerWidth);
            TableRow.LayoutParams bodyParams = (TableRow.LayoutParams)bodyCell.getLayoutParams();
            bodyParams.width = max;
            TableRow.LayoutParams headerParams = (TableRow.LayoutParams)headerCell.getLayoutParams();
            headerParams.width = max;
        }       
    }
}
}

main.xml

 <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" 
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    
    <com.stylingandroid.ScrollingTable.ScrollingTable
        android:layout_width="match_parent"
        android:orientation="vertical"
        android:layout_height="match_parent">
        
        <TableLayout 
            android:layout_height="wrap_content"
            android:layout_width="match_parent" 
            android:id="@+id/HeaderTable">
        </TableLayout>
        
        <ScrollView 
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
            
            <TableLayout 
                android:layout_height="wrap_content"
                android:layout_width="match_parent" 
                android:id="@+id/BodyTable">
            </TableLayout>
            
        </ScrollView>
        
    </com.stylingandroid.ScrollingTable.ScrollingTable>
    
</LinearLayout>

Main activity

 package com.stylingandroid.ScrollingTable;

import android.app.Activity;
import android.app.ProgressDialog;
import android.graphics.Color;
import android.os.AsyncTask;
import android.os.Bundle;
import android.widget.TableLayout;
import android.widget.TableRow;

    import android.widget.TextView;
    
    public class ScrollingTableActivity extends Activity
    {
        private String[][] tableData = {
                {"header11111111111", "header2","header3","header4"},
                {"column1", "column1","column1","column1"},
                {"column1", "column1","column1","column1"},
                {"column1", "column1","column1","column1"},
            

    {"column1", "column1",

"column1","column1"},
                {"column1", "column1","column1","column1"},
                {"column1", "column1","column1","column1"},
                {"column1", "column1","column1","column1"},
                {"column1", "column1","column1","column1"},
                {"column1", "column1","column1","column1"},
                {"column1", "column1","column1","column1"},
                {"column1", "column1","column1","column1"},
                {"column1", "column1","column1","column1"}
        };
        /** Called when the activity is first created. */
        @Override
        public void onCreate( Bundle savedInstanceState )
        {
            super.onCreate( savedInstanceState );
            setContentView( R.layout.main );
            TableLayout tableHeader = (TableLayout)findViewById(R.id.HeaderTable);
            TableLayout tableBody = (TableLayout)findViewById(R.id.BodyTable);
            
        appendRows(tableHeader, tableBody, tableData);
}

private void appendRows(TableLayout tableHeader ,TableLayout tableContent, String[][] amortization) {
    int rowSize=amortization.length;
    int colSize=(amortization.length > 0)?amortization[0].length:0;
    for(int i=0; i<rowSize; i++) {
        TableRow row1 = new TableRow(this);
        
        for(int j=0; j<colSize; j++) {
            TextView c = new TextView(this);
            c.setText(amortization[i][j]);
            c.setPadding(3, 3, 3, 3);
            if (i == 0) {
                c.setTextColor(Color.BLACK);
            }
            row1.addView(c);
        }
        
        if (i == 0) { 
            row1.setBackgroundColor(Color.LTGRAY);
            tableHeader.addView(row1, new TableLayout.LayoutParams());
        } else {
            tableContent.addView(row1, new TableLayout.LayoutParams());
        }
    }
}

The above code work perfectly (expected), however, when I use AnysnTask to get data from server and add data to table later, the onLayout() in my custom view doesn't work anymore. I simulate getting data by log out some number:

public void onCreate( Bundle savedInstanceState )
    {
        super.onCreate( savedInstanceState );
        setContentView( R.layout.main );
        
        new MyTask().execute();
    }
    
    private class MyTask extends AsyncTask<Void, Void, Void> {
        
        private ProgressDialog progressDialog;
        protected void onPreExecute() {
                progressDialog = ProgressDialog.show(ScrollingTableActivity.this,
                                  "", "Loading. Please wait...", true);
        }
        @Override
        protected Void doInBackground(Void... reportTypes) {
            for (int i = 0; i < 500; i++) { 
                System.out.println(i);
            } 
            return null;
        }
        @Override
        protected void onPostExecute(Void result) {
            progressDialog.dismiss();
            TableLayout tableHeader = (TableLayout)findViewById(R.id.HeaderTable);
            TableLayout tableBody = (TableLayout)findViewById(R.id.BodyTable);
            
            appendRows(tableHeader, tableBody, tableData);
        }
        
    }

So the onLayout() only work when I call appendRows() from main UI thread by putting it in onCreate() method. If I call from another UI thread (in onPostExecute() of AsyncTask), the onLayout() is called (I checked it by create some logs) but it doesn't effect to the GUI. I tried with invalidate(), forceLayout(), requestLayout() but doesn't change anything.wrong

I think we need to call a method to make the GUI refresh but don't know what it is.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

快乐很简单 2024-12-30 02:48:15

您可能想看看这个答案:
Android 动态设置 textview 布局宽度

但是,基本上,尝试设置每个 TextView 与标题相同。

这可能需要您将所有操作都执行两次,因为您可能需要让系统进行布局,因此使用 View.INVISIBLE,然后您将需要退出 AsyncTask,调用另一个 AsyncTask,因此布局工作可能会发生。

然后在第二个中,您可以获取不可见的表格,循环查找该列中的最大宽度,然后将该列中的所有 TextView 设置为最大。

这不是最好的解决方案,但应该可行。

我认为 AsyncTask 中的主要问题是需要完成布局,然后您可以进行修复。

You may want to look at this answer:
Android Set textview layout width dynamically

but, basically, try to set the width of each TextView to be the same as the header.

This may require you to do everything twice, as you will probably need to let the system do the layout, so use View.INVISIBLE, then you will need to exit the AsyncTask, calling another one, so the layout work can happen.

Then in the second one, you can then get the invisible tables, loop through to find the largest width in that column, then set all the TextViews in that column to the largest.

This isn't the best solution, but should work.

I think your main problem in the AsyncTask one is that the layout needs to be done, then you can do the fixing.

我终于找到答案了,setColumnCollapsed()使表格布局刷新,但是我们需要将其放在另一个AsyncTask中,否则它将不起作用,奇怪:(。我把最新的代码放在这里,所以希望它有帮助此外,这只是解决方法,所以请随意发布您的答案(如果有)...

private class MyTask extends AsyncTask<Void, Void, Void> {

    private ProgressDialog progressDialog;
    protected void onPreExecute() {
            progressDialog = ProgressDialog.show(ScrollingTableActivity.this,
                              "", "Loading. Please wait...", true);
    }
    @Override
    protected Void doInBackground(Void... reportTypes) {
        for (int i = 0; i < 500; i++) { 
            System.out.println(i);
        } 
        return null;
    }
    @Override
    protected void onPostExecute(Void result) {
        progressDialog.dismiss();
        appendRows(tableHeader, tableBody, tableData);

        new My1Task().execute();
    }
}

private class My1Task extends AsyncTask<Void, Void, Void> {

    @Override
    protected Void doInBackground(Void... reportTypes) {
        return null;
    }

    @Override
    protected void onPostExecute(Void result) {
        tableHeader.setColumnCollapsed(0, false);
        tableBody.setColumnCollapsed(0, false);
    }
}

I finally find out the answer, the setColumnCollapsed() makes the table layout refreshed, however we need to put it in another AsyncTask, otherwise it will not work, strange :( .I put the latest code here, so hope it is helpful for someone. Besides, this is just workaround, so feel free to post your answer if any...

private class MyTask extends AsyncTask<Void, Void, Void> {

    private ProgressDialog progressDialog;
    protected void onPreExecute() {
            progressDialog = ProgressDialog.show(ScrollingTableActivity.this,
                              "", "Loading. Please wait...", true);
    }
    @Override
    protected Void doInBackground(Void... reportTypes) {
        for (int i = 0; i < 500; i++) { 
            System.out.println(i);
        } 
        return null;
    }
    @Override
    protected void onPostExecute(Void result) {
        progressDialog.dismiss();
        appendRows(tableHeader, tableBody, tableData);

        new My1Task().execute();
    }
}

private class My1Task extends AsyncTask<Void, Void, Void> {

    @Override
    protected Void doInBackground(Void... reportTypes) {
        return null;
    }

    @Override
    protected void onPostExecute(Void result) {
        tableHeader.setColumnCollapsed(0, false);
        tableBody.setColumnCollapsed(0, false);
    }
}
独﹏钓一江月 2024-12-30 02:48:15

答案是您应该在 onCreate() 方法之外声明 TableLayouts 并在 onCreate() 中实例化它们。这是解决方案。效果很好。

public class ScrollingTableActivity extends Activity {
    TableLayout tableHeader;
    TableLayout tableBody;

    private String[][] tableData = {
            { "header11111111111", "header2", "header3", "header4" },
            { "column1", "column1", "column1", "column1" },
            { "column1", "column1", "column1", "column1" },
            { "column1", "column1", "column1", "column1" },
            { "column1", "column1", "column1", "column1" } };

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        tableHeader = (TableLayout) findViewById(R.id.HeaderTable);
        tableBody = (TableLayout) findViewById(R.id.BodyTable);
        Log.d("ScrollingTable", "Before appendRows");
        //appendRows(tableHeader, tableBody, tableData);
        new MyTask().execute();
    }

    private void appendRows(TableLayout tableHeader, TableLayout tableContent,
            String[][] amortization) {
        int rowSize = amortization.length;
        int colSize = (amortization.length > 0) ? amortization[0].length : 0;
        for (int i = 0; i < rowSize; i++) {
            TableRow row1 = new TableRow(this);

            for (int j = 0; j < colSize; j++) {
                TextView c = new TextView(this);
                c.setText(amortization[i][j]);
                c.setPadding(3, 3, 3, 3);
                if (i == 0) {
                    c.setTextColor(Color.BLACK);
                }
                row1.addView(c);
            }

            if (i == 0) {
                row1.setBackgroundColor(Color.LTGRAY);
                tableHeader.addView(row1, new TableLayout.LayoutParams());
            } else {
                tableContent.addView(row1, new TableLayout.LayoutParams());
            }
        }
    }


    private class MyTask extends AsyncTask<Void, Void, Void> {

        private ProgressDialog progressDialog;
        protected void onPreExecute() {
                progressDialog = ProgressDialog.show(ScrollingTableActivity.this,
                                  "", "Loading. Please wait...", true);
        }
        @Override
        protected Void doInBackground(Void... reportTypes) {
            for (int i = 0; i < 500; i++) { 
                System.out.println(i);
            } 
            return null;
        }
        @Override
        protected void onPostExecute(Void result) {
            progressDialog.dismiss();
            TableLayout tableHeader = (TableLayout)findViewById(R.id.HeaderTable);
            TableLayout tableBody = (TableLayout)findViewById(R.id.BodyTable);

            appendRows(tableHeader, tableBody, tableData);
        }
    }
}

The answer is that you should declare your TableLayouts outside onCreate() method and instantiate them in onCreate(). Here is the solution. It works well.

public class ScrollingTableActivity extends Activity {
    TableLayout tableHeader;
    TableLayout tableBody;

    private String[][] tableData = {
            { "header11111111111", "header2", "header3", "header4" },
            { "column1", "column1", "column1", "column1" },
            { "column1", "column1", "column1", "column1" },
            { "column1", "column1", "column1", "column1" },
            { "column1", "column1", "column1", "column1" } };

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        tableHeader = (TableLayout) findViewById(R.id.HeaderTable);
        tableBody = (TableLayout) findViewById(R.id.BodyTable);
        Log.d("ScrollingTable", "Before appendRows");
        //appendRows(tableHeader, tableBody, tableData);
        new MyTask().execute();
    }

    private void appendRows(TableLayout tableHeader, TableLayout tableContent,
            String[][] amortization) {
        int rowSize = amortization.length;
        int colSize = (amortization.length > 0) ? amortization[0].length : 0;
        for (int i = 0; i < rowSize; i++) {
            TableRow row1 = new TableRow(this);

            for (int j = 0; j < colSize; j++) {
                TextView c = new TextView(this);
                c.setText(amortization[i][j]);
                c.setPadding(3, 3, 3, 3);
                if (i == 0) {
                    c.setTextColor(Color.BLACK);
                }
                row1.addView(c);
            }

            if (i == 0) {
                row1.setBackgroundColor(Color.LTGRAY);
                tableHeader.addView(row1, new TableLayout.LayoutParams());
            } else {
                tableContent.addView(row1, new TableLayout.LayoutParams());
            }
        }
    }


    private class MyTask extends AsyncTask<Void, Void, Void> {

        private ProgressDialog progressDialog;
        protected void onPreExecute() {
                progressDialog = ProgressDialog.show(ScrollingTableActivity.this,
                                  "", "Loading. Please wait...", true);
        }
        @Override
        protected Void doInBackground(Void... reportTypes) {
            for (int i = 0; i < 500; i++) { 
                System.out.println(i);
            } 
            return null;
        }
        @Override
        protected void onPostExecute(Void result) {
            progressDialog.dismiss();
            TableLayout tableHeader = (TableLayout)findViewById(R.id.HeaderTable);
            TableLayout tableBody = (TableLayout)findViewById(R.id.BodyTable);

            appendRows(tableHeader, tableBody, tableData);
        }
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文