我遇到了這個問題,並提出了無數的帖子在這裏和其他地方涉及到這個問題。Android IllegalStateException:片段沒有附加到活動
此應用程序的目標是android 16,但min被設置爲8.所以我使用Android支持庫訪問更新的apis。
我有一個活動,加載一個是ListFragment的片段。如果以橫向模式在平板電腦上運行,則第一個片段在單擊列表項時將以2窗格佈局加載第二個片段。如果在電話上,新的活動加載第二個片段。
第二個片段使用CursorLoader訪問ContentProvider作爲其後備數據存儲。當從它自己的活動調用這個片段時,沒有問題。但是,當它處於2窗格佈局中時,CursorLoader會因上述異常而失敗。
下面是代碼:
public class ProgramGroupFragment extends MythtvListFragment implements LoaderManager.LoaderCallbacks<Cursor> {
private static final String TAG = ProgramGroupFragment.class.getSimpleName();
private ProgramCursorAdapter adapter;
private String programGroup = "*";
public ProgramGroupFragment() { }
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
String[] projection = { BaseColumns._ID, ProgramConstants.FIELD_TITLE, ProgramConstants.FIELD_SUB_TITLE };
String[] selectionArgs = { programGroup };
CursorLoader cursorLoader = new CursorLoader(getActivity(), ProgramConstants.CONTENT_URI, projection, ProgramConstants.FIELD_TITLE + "=?", selectionArgs, ProgramConstants.FIELD_SUB_TITLE);
return cursorLoader;
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
Log.v(TAG, "onLoadFinished : enter");
adapter.swapCursor(cursor);
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
adapter.swapCursor(null);
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
adapter = new ProgramCursorAdapter(
getActivity().getApplicationContext(), R.layout.program_row,
null, new String[] { ProgramConstants.FIELD_SUB_TITLE }, new int[] { R.id.program_sub_title },
CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);
setListAdapter(adapter);
getLoaderManager().initLoader(0, null, this);
}
public void loadPrograms(String name) {
programGroup = name;
try {
getLoaderManager().restartLoader(0, null, this);
} catch(Exception e) {
Log.w(TAG, e.getLocalizedMessage(), e);
}
}
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
Intent i = new Intent(getActivity(), VideoActivity.class);
i.putExtra(VideoActivity.EXTRA_PROGRAM_KEY, id);
startActivity(i);
}
private class ProgramCursorAdapter extends SimpleCursorAdapter {
public ProgramCursorAdapter(Context context, int layout, Cursor c, String[] from, int[] to, int flags) {
super(context, layout, c, from, to, flags);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = super.getView(position, convertView, parent);
getCursor().moveToPosition(position);
try {
int idIndex = getCursor().getColumnIndexOrThrow(BaseColumns._ID);
int titleIndex = getCursor().getColumnIndexOrThrow(ProgramConstants.FIELD_TITLE);
int subTitleIndex = getCursor().getColumnIndexOrThrow(ProgramConstants.FIELD_SUB_TITLE);
int id = getCursor().getInt(idIndex);
String title = getCursor().getString(titleIndex);
String subTitle = getCursor().getString(subTitleIndex);
if(row == null) {
LayoutInflater inflater = getActivity().getLayoutInflater();
row = inflater.inflate(R.layout.program_row, parent, false);
}
TextView t = (TextView) row.findViewById(R.id.program_sub_title);
t.setText(!"".equals(subTitle) ? subTitle : title);
} catch(Exception e) {
Log.e(TAG, "getView : error", e);
}
return row;
}
}
}
的問題發生在方法loadPrograms,具體的行:
getLoaderManager().restartLoader(0, null, this);
這被投擲IllegalStateException異常。在仔細閱讀文檔和大量例子之後,我從來沒有看到過這個問題,爲了繼續下去而不得不被抓住。
爲了看看會發生什麼,我將代碼包裝在try/catch塊中,令我驚訝的是,該應用程序按預期工作。
編輯: 這裏是活動的代碼,建立2窗格佈局:
public class RecordingsActivity extends AbstractRecordingsActivity implements RecordingsFragment.OnProgramGroupListener {
private static final String TAG = RecordingsActivity.class.getSimpleName();
private static final int REFRESH_ID = Menu.FIRST + 2;
private long requestId;
private BroadcastReceiver requestReceiver;
private DvrServiceHelper mDvrServiceHelper;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_dvr_recordings);
RecordingsFragment recordingsFragment = (RecordingsFragment) getSupportFragmentManager().findFragmentById(R.id.fragment_dvr_program_groups);
recordingsFragment.setOnProgramGroupListener(this);
}
@TargetApi(11)
@Override
public boolean onCreateOptionsMenu(Menu menu) {
Log.v(TAG, "onCreateOptionsMenu : enter");
MenuItem refresh = menu.add(Menu.NONE, REFRESH_ID, Menu.NONE, "Refresh");
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
refresh.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
}
return true;
}
/* (non-Javadoc)
* @see org.mythtv.client.ui.dvr.AbstractRecordingsActivity#onOptionsItemSelected(android.view.MenuItem)
*/
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch(item.getItemId()) {
case REFRESH_ID:
requestId = mDvrServiceHelper.getRecordingedList();
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
protected void onResume() {
super.onResume();
IntentFilter filter = new IntentFilter(DvrServiceHelper.ACTION_REQUEST_RESULT);
requestReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
long resultRequestId = intent.getLongExtra(DvrServiceHelper.EXTRA_REQUEST_ID, 0);
if(resultRequestId == requestId) {
int resultCode = intent.getIntExtra(DvrServiceHelper.EXTRA_RESULT_CODE, 0);
if(resultCode == 200) {
Log.d(TAG, "Updating UI with new data");
} else {
Log.d(TAG, "error occurred");
}
} else {
Log.d(TAG, "Result is NOT for our request ID");
}
}
};
mDvrServiceHelper = DvrServiceHelper.getInstance(this);
registerReceiver(requestReceiver, filter);
}
/* (non-Javadoc)
* @see android.support.v4.app.FragmentActivity#onPause()
*/
@Override
public void onPause() {
super.onPause();
// Unregister for broadcast
if(null != requestReceiver) {
try {
unregisterReceiver(requestReceiver);
requestReceiver = null;
} catch(IllegalArgumentException e) {
Log.e(TAG, e.getLocalizedMessage(), e);
}
}
}
public void onProgramGroupSelected(String programGroup) {
Log.d(TAG, "onProgramGroupSelected : enter");
if(null != findViewById(R.id.fragment_dvr_program_group)) {
FragmentManager manager = getSupportFragmentManager();
ProgramGroupFragment programGroupFragment = (ProgramGroupFragment) manager.findFragmentById(R.id.fragment_dvr_program_group);
FragmentTransaction transaction = manager.beginTransaction();
if(null == programGroupFragment) {
Log.v(TAG, "onProgramGroupSelected : creating new programGroupFragment");
programGroupFragment = new ProgramGroupFragment();
transaction
.add(R.id.fragment_dvr_program_group, programGroupFragment)
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
.addToBackStack(null)
.commit();
}
programGroupFragment.loadPrograms(programGroup);
} else {
Intent i = new Intent(this, ProgramGroupActivity.class);
i.putExtra(ProgramGroupActivity.EXTRA_PROGRAM_GROUP_KEY, programGroup);
startActivity(i);
}
}
}
監聽方法onProgramGroupSelected是其中單對2窗格佈局被確定。監聽器在左側疼痛中的列表項被點擊時被調用。
任何人都可以解釋爲什麼這個例外發生?
該項目的完整代碼位於https://github.com/MythTV-Android/mythtv-for-android
在您的代碼庫中,我沒有看到您曾在兩個窗格佈局中創建「ProgramGroupFragment」的位置。是否有創建片段的場景,但暫時不會將其附加到活動中? – CommonsWare
@CommonsWare我使用調用的活動編輯了原始帖子,創建了2窗格佈局。 – dmfrey
這對我來說並不明顯,爲什麼你會得到你的例外 - 抱歉。 – CommonsWare