这是我的方案:我有一个登录屏幕,可以打开另一个活动。在活动中,我只需:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_details);
}
布局有点沉重,因为它是由一些碎片组成的,并且需要大约1.5秒才能加载。
现在,如何在ProgressDialog
完成布局时显示setContentView
?我通过将AsyncTask
放在setContentView
中尝试doInBackground
,但当然无法完成,因为UI只能从UI线程更新。
所以我需要在UI线程中调用setContentView
,但是我必须在哪里显示/关闭ProgressDialog
?
感谢您的帮助。
弗拉。
编辑:我跟着@ JohnBoker先前的建议,这是我现在的代码:@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_empty_layout);
new ContentSetterTask().execute("");
}
private class ContentSetterTask extends AsyncTask<String, Void, Void> {
public ProgressDialog prgDlg;
@Override
protected void onPreExecute() {
android.os.Debug.waitForDebugger();
prgDlg = ProgressDialog.show(MultiPaneActivity.this, "", "Loading...", true);
}
@Override
protected Void doInBackground(String... args) {
android.os.Debug.waitForDebugger();
ViewGroup rootView = (ViewGroup)findViewById(R.id.emptyLayout);
LayoutInflater inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View inflated = inflater.inflate(R.layout.activity_details, rootView);
return null;
}
@Override
protected void onPostExecute(Void arg) {
android.os.Debug.waitForDebugger();
if (prgDlg.isShowing())
prgDlg.dismiss();
}
}
}
行
View inflated = inflater.inflate(R.layout.activity_details, rootView);
给我错误:
06-27 16:47:24.010:
ERROR/AndroidRuntime(8830): Caused by:android.view.ViewRoot$CalledFromWrongThreadException:
Only the original thread that created a view hierarchy can touch its views.
答案 0 :(得分:8)
我在创建一个沉重的视图时遇到了同样的问题,我所做的只是在xml文件中放置了一个linearlayout并在其上调用了setContentView,然后我在asynctask中创建了真实视图,并将视图dymanically添加到linearlayout
此方法似乎有效,我可以在此过程中设置进度对话框。
答案 1 :(得分:8)
我决定在这里为未来的读者做一个完整的答案。
在这个问题上花了几个小时之后,我意识到问题是我正在尝试做两件事:
因此,由于两个调用都来自UI线程(onCreate或AsyncTask没有区别,它仍然是UI线程),第一个阻止第二个调用正确显示。 底线是:此问题无法在Android中解决。让我们希望我们可以获得一些更好的API来与UI进行交互,因为我们有点吮吸。
我将通过更改布局并使其更轻(如果可能!)来解决此问题。 谢谢大家!
答案 2 :(得分:0)
这不是一个完美的解决方案,但到目前为止最接近我需要的东西:我们需要一些时间设置进度对话框(或消息视图或其他)可见和调用setContentView(),以便对话/消息实际上是显示的。
为了实现这一点,我启动一个Thread执行一个short sleep(),然后在UI线程中调用setContentView()。
以下是进度对话框的示例:
private ProgressDialog m_Hourglass = null ;
private Thread m_LoadingThread = null ;
...
public void onCreate( Bundle savedInstanceState )
{
super.onCreate( savedInstanceState );
...
m_Hourglass = new ProgressDialog( this );
m_Hourglass.setMessage( "Loading" );
m_Hourglass.setIndeterminate( true );
m_Hourglass.setCancelable( false );
}
public void SetMyView( View vvv ) // Can be called from buttons.
{
if( m_LoadingThread != null ) return; // Avoid multiple press
m_Hourglass.show();
////////////////////////////////////////////////////
// Load in a thread, just to show the above message
// before changing layout (because setContentView()
// freezes the GUI.)
m_LoadingThread = new Thread()
{
@Override
public void run()
{
super.run();
try { sleep( 20 ); }
catch (Exception e) { System.out.println( e ); }
finally { runOnUiThread( new Runnable()
{
public void run()
{
SetMyViewThreaded();
}
});
}
m_LoadingThread = null ; // Now we can load again
}
};
m_LoadingThread.start();
}
public void SetMyViewThreaded()
{
setContentView( R.layout.some_gorgeous_layout );
/////////////////////////////////////////////////////
// Catch when to finally hide the progress dialog:
ImageView iv = (ImageView) findViewById( R.id.some_view_in_the_layout );
ViewTreeObserver obs = iv.getViewTreeObserver();
obs.addOnGlobalLayoutListener( new OnGlobalLayoutListener()
{
@Override
public void onGlobalLayout() // The view is now loaded
{
m_Hourglass.hide();
}
});
//////////////////////////////////////////////////////
// Here we can work on the layout
...
}
我的主要问题是睡眠(20),这是近似值,并且在所有机器上可能都不够。它适用于我,但如果你没有看到进度对话框,你可能需要增加睡眠持续时间。