【问题标题】:Asynchronously add views to fragment in Android在Android中将视图异步添加到片段
【发布时间】:2013-05-21 10:28:31
【问题描述】:

我正在尝试创建一个片段,该片段在开始时将显示一个 ProgressBar,其他线程将调用一个函数,该函数将带来一些信息(从远程服务器带来的信息 - 需要几秒钟)来填充一些将被添加的视图到片段。我试图将视图添加到片段中,但出现了一个异常,即我无法更改不在主线程上的线程上的 UI。所以我在 UI 线程上运行它,但这里的问题是 UI 堆叠,直到所有信息从服务器带回的那一刻。

这是我的代码:

public class MainSalePage extends SherlockFragment 
{
    protected View maneSalePage;
    protected List<SaleObj> saleList;
    protected int numberOfsalesOnScreen;
    protected LinearLayout saleConteiner;


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) 
    {
        maneSalePage = inflater.inflate(R.layout.main_page_fragment, container, false);
        numberOfsalesOnScreen = 0;
        saleConteiner = (LinearLayout) maneSalePage.findViewById(R.id.salesContainer);


        getActivity().runOnUiThread(new Runnable()
        {
            @Override
            public void run() 
            {
                SaleService saleService = new SaleService();
                saleList = saleService.getNextSales(numberOfsalesOnScreen); //the operation that takes time.
                for(int i = 0; i < saleList.size(); i++)
                {
                    SaleRow saleRow = new SaleRow(getActivity());
                    saleRow.populateWith(saleList.get(i));

                    saleConteiner.addView(saleRow);
                }
            }
        });

        return maneSalePage;
    }

}

如何在从服务器获取所有数据之前显示片段而不堆叠 UI?

谢谢!

【问题讨论】:

    标签: android multithreading asynchronous android-ui


    【解决方案1】:

    例如,最好启动一个 AsyncTask 以在后台线程中从服务器下载所有数据。 AsyncTask 有有用的处理程序 OnPreExecute 和 OnPostExecute,保证在 UI 线程中运行。因此,您可以在 PreExecute 上放置一个“加载”占位符文本,并在下载数据时,在 onPostExecute 中使用它填充您的 Fragment。

    在极少数情况下,如果您希望数据在下载时逐渐显示,请将 onProgressUpdate 处理程序与 publishProgress 方法结合使用。

    【讨论】:

    • asyncTask 对我不利,因为我拥有的每个服务类(如 SaleService)都有多个任务。
    • 我认为 AsyncTask,正如 alexei 所建议的,是你应该使用的。这是一个很好的标准组件,每个 Android 开发人员都知道如何使用(或应该)提高代码的可维护性。请注意,如果您真的想要,您可以通过为 execute() 方法提供不同的处理程序(针对不同的线程)来并行运行多个 AsyncTask。但是,如果您有多个可以按顺序执行的任务,我会坚持使用 AsyncTask 的 publishProgress() 方法,就像 alexei 也提到的那样。
    • 所以对我的远程服务器的每次调用都应该在不同的 AsyncTask 上?
    • 如果你使用Service来下载你的数据,那么你应该绑定服务并在UI线程中自动调用onReceive来实现BroadcastReceiver。但这完全是另一回事。
    【解决方案2】:

    尝试这样的方法它会起作用,不是最干净的解决方案,但我喜欢更正代码而不是从草图中编写新的代码,这样你就可以看到你在哪里犯了错误

    编辑:在与弗拉德聊天时,一个帮助他的解决方案指南

    @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) 
        {
            maneSalePage = inflater.inflate(R.layout.main_page_fragment, container, false);
            maneSalePage.postDelayed(new Runnable() {
    
                @Override
                public void run() {
                    addContainer();
                }
            }               
            }, 1000);
            return maneSalePage;
        }
    
            private void addContainer(){
                numberOfsalesOnScreen = 0;
                saleConteiner = (LinearLayout)       maneSalePage.findViewById(R.id.salesContainer);
    
               SaleService saleService = new SaleService();
            saleList = saleService.getNextSales(numberOfsalesOnScreen); //the operation that takes time.
            for(int i = 0; i < saleList.size(); i++)
            {
                getActivity().runOnUiThread(new Runnable()
                {
                    @Override
                    public void run() 
                    {                       
                            SaleRow saleRow = new SaleRow(getActivity());
                            saleRow.populateWith(saleList.get(i));  
                            saleConteiner.addView(saleRow);
    
                    }
                });
            }
        }
    

    你是一次发送所有的,这样你就一个接一个地发送

    编辑:不要忘记这将一次创建所有 N = saleList.size() 线程。

    希望这对您有所帮助并享受您的工作

    【讨论】:

    • 谢谢,但即便如此,片段仍会等到 for 循环完成,然后才会在屏幕上显示自己。我希望它显示出来,然后填充它。
    • 问题是你在onCreate上做这个,片段初始化后有什么办法吗?
    • 创建后我不关心。如果可能的话。
    • 尝试编辑的答案,看看区别是saleConteiner.post( 这应该强制先显示salesContainer,然后开始执行所有语句。
    • 你的意思是把saleConteiner.addView(saleRow)换成saleConteiner.post(saleRow)
    猜你喜欢
    • 1970-01-01
    • 2015-06-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多