Здравствуйте, товарищи программисты!
Наблюдаю странное поведение на android 9 устройствах. Следующий код отлично работает на Android 5.1.1
Я хочу показать простой фрагмент, который будет загружаться при нажатии кнопки.
private void openHistoryFragment(boolean withSelfHealing) {
try {
FragmentManager fragmentManager = getSupportFragmentManager();
HistoryFragment mPreviousHistoryInstance = null;
if (historyFragment != null && historyFragment.isVisible())
mPreviousHistoryInstance = historyFragment;
historyFragment = new HistoryFragment();
historyFragment.setSettingsCallback(this);
if (withSelfHealing)
historyFragment.addFixingRunnable(() -> {
Timber.w("Self healing view behaviour");
openLoginDialog(LoginDialog.LoginMode.TEXT_ENTER, false);
hideLoginDialog();
Timber.w("Self healing view behaviour --- END");
});
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
if (mPreviousHistoryInstance != null)
transaction.remove(mPreviousHistoryInstance);
transaction.add(R.id.fragment_holder, historyFragment, "historyFragment");
//transaction.setReorderingAllowed(true);
transaction.commit();
} catch (Exception e) {
Timber.e(e, "Failed to display HistoryFragment ! ");
}
Это отлично работает, когда мой Activity ( Действие A) загружено. Однако, если я запустил другое действие B, затем вернусь к действию A. Фрагмент больше не отображается при вызове этой функции.
Вот код для фрагмента:
public class HistoryFragment extends ErgoDialogFragment implements
LoaderManager.LoaderCallbacks<Cursor>,
View.OnClickListener, AdapterView.OnItemClickListener,
WorkoutDetailFragment.OnFragmentInteractionListener
{
private static final int USER_LOADER = 2;
private static final int WORKOUT_LOADER = 1;
private ProgressBar spinner;
private OldSettingsFragmentCallback settings;
//private View view;
private CursorAdapter cursorAdapter;
private ListView listView;
private WeekStatsView weeklyView;
private TextView emptyView;
private Switcher switcher;
private LoaderManager mLoaderManager;
public HistoryFragment() {
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
View view;
view = inflater.inflate(R.layout.history_fragment, container, false);
RelativeLayout background = view.findViewById(R.id.background_layout);
try {
//background.setBackgroundResource(R.drawable.background_blue_gauss2);
Drawable backgroundDrawable = ErgoApplication.getBackgroundDrawableBlurred();
if (backgroundDrawable != null) {
background.setBackground(backgroundDrawable);
}
} catch (Exception e) {
LogCatcher.write(e);
}
DialogHeader header = view.findViewById(R.id.dialogHeader1);
header.setTitle(getString(R.string.history_title));
header.setCloseBtnOnClickListener(new ErgoOnClickListener() {
@Override
public void onClickEvent(View view) {
dismiss();
}
});
listView = view.findViewById(R.id.backgroundListView);
weeklyView = view.findViewById(R.id.weekly_view);
cursorAdapter = new WorkoutCursorAdapter(getContext(), null, true);
listView.setAdapter(cursorAdapter);
listView.setOnItemClickListener(this);
emptyView = view.findViewById(R.id.emptyView);
listView.setEmptyView(emptyView);
switcher = view.findViewById(R.id.mode_switcher);
switcher.setTextLeft(getString(R.string.history_switcher_personal));
switcher.setTextRight(getString(R.string.history_switcher_all));
spinner = view.findViewById(R.id.spinner);
mLoaderManager = LoaderManager.getInstance(this);
if (ErgoApplication.getUserManager() != null
&& ErgoApplication.getUserManager().getIfisUser() != null) {
switchToUserLoader();
switcher.setActiveSide(Switcher.SwitcherSide.SWITCHER_LEFT);
switcher.show();
} else {
switchToGeneralLoader();
switcher.hide();
switcher.setActiveSide(Switcher.SwitcherSide.SWITCHER_RIGHT);
}
switcher.setLeftOnClickListener(new ErgoOnClickListener() {
@Override
public void onClickEvent(View view) {
switchToUserLoader();
switcher.setActiveSide(Switcher.SwitcherSide.SWITCHER_LEFT);
}
});
switcher.setRightOnClickListener(new ErgoOnClickListener() {
@Override
public void onClickEvent(View view) {
switchToGeneralLoader();
switcher.setActiveSide(Switcher.SwitcherSide.SWITCHER_RIGHT);
}
});
return view;
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
view.postDelayed(this::hideKeyboard, 500);
}
private void switchToGeneralLoader() {
if (mLoaderManager == null)
return;
mLoaderManager.destroyLoader(USER_LOADER);
mLoaderManager.initLoader(WORKOUT_LOADER, null, this);
}
public void switchToUserLoader() {
Timber.w("Using USER specific loader ! ");
if (mLoaderManager == null)
return;
mLoaderManager.destroyLoader(WORKOUT_LOADER);
mLoaderManager.initLoader(USER_LOADER, null, this);
}
/*public View getView() {
return view;
}*/
public void stopSpinning() {
if (spinner != null)
spinner.setVisibility(View.INVISIBLE);
}
/**
* Shows a spinner to show that work is in progress
*/
public void showLoadingLayout() {
weeklyView.fetchData(null);
emptyView.setVisibility(View.INVISIBLE);
if (spinner != null)
spinner.setVisibility(View.VISIBLE);
}
public void setSettingsCallback(OldSettingsFragmentCallback settings) {
this.settings = settings;
}
public void doNothing(View v) {
}
@Override
public void onClick(View v) {
LogCatcher.dropBreadCrump("close-history-fragment");
settings.onCloseClick(getTag());
}
/**
* Hides the keyboard
*/
public void hideKeyboard() {
try {
InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
if (listView != null)
imm.hideSoftInputFromWindow(listView.getWindowToken(), 0);
getActivity().getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
} catch (Exception e) {
}
}
@Override
public void onResume() {
super.onResume();
if (getContext() != null)
FirebaseAnalytics.getInstance(getContext()).setCurrentScreen(getActivity(),
"HistoryFragment", null);
EventLogger.logView(getActivity(), getClass().getSimpleName());
Timber.i("onResume()");
if (switcher != null) {
switcher.postDelayed(this::hideKeyboard, 500);
if (switcher.getActiveSide() != null) {
if (switcher.getActiveSide() == Switcher.SwitcherSide.SWITCHER_LEFT) {
//user mode
String user = ErgoApplication.getUserManager().getIfisUser();
Timber.i("onResume() -- user is %s", (user != null ? user : "undefined"));
weeklyView.fetchData(user);
} else {
//other side
Timber.i("onResume() -- switcher is in right position");
}
} else {
Timber.i("onResume() -- no active side");
}
} else
Timber.i("onResume() -- no switcher found");
}
@NonNull
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
switch (id) {
case WORKOUT_LOADER:
showLoadingLayout();
return new CursorLoader(getActivity(), MyContentProvider.WORKOUT_URI,
null, null, null, WorkoutSummaryTable.COLUMN_ID + " DESC");
case USER_LOADER:
//TODO hier geht es weiter
String currentUser = ErgoApplication.getUserManager().getIfisUser();
if (currentUser != null) {
showLoadingLayout();
String selectString = WorkoutSummaryTable.COLUMN_STAGING + "=?";
return new CursorLoader(getActivity(), MyContentProvider.WORKOUT_URI,
null, selectString, new String[]{currentUser}, WorkoutSummaryTable.COLUMN_ID + " DESC");
} else {
stopSpinning();
}
default:
return new CursorLoader(getActivity(), MyContentProvider.WORKOUT_URI,
null, null, null, WorkoutSummaryTable.COLUMN_ID + " DESC");
}
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
try {
stopSpinning();
cursorAdapter.changeCursor(data);
if (weeklyView != null) {
String user = null;
if (ErgoApplication.getUserManager() != null)
user = ErgoApplication.getUserManager().getIfisUser();
if (loader.getId() == WORKOUT_LOADER)
user = null;
weeklyView.fetchData(user);
}
Timber.e("Cursor returned %d entries ", data.getCount());
} catch (Exception e) {
}
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
if (cursorAdapter != null)
cursorAdapter.changeCursor(null);
}
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
Timber.d("Openening details of #%d", l);
if (settings == null) {
Timber.w("No - not openening .... activity is gone");
return;
}
LogCatcher.dropBreadCrump("history-detail @" + l);
HistoryDetailFragment mFragment = HistoryDetailFragment.newInstance(l);
mFragment.setInteractionListener(this);
mFragment.setSettingsCallback(settings);
settings.onOpenSubFragmentRequest(mFragment);
}
@Override
public void onFragmentInteraction(Uri uri) {
//getChildFragmentManager().popBackStack();
Timber.d("Clicked onFragmentInteraction");
FragmentManager mRef = getFragmentManager();
if (mRef != null)
mRef.popBackStack(); //remove the detail fragment
}
@Override
public void onDismiss(DialogInterface dialog) {
super.onDismiss(dialog);
}
@Override
public void onDestroyView() {
try {
/**
* Clear refs here since this view will be gone after this function returns
*/
if (mLoaderManager != null) {
mLoaderManager.destroyLoader(USER_LOADER);
mLoaderManager.destroyLoader(WORKOUT_LOADER);
mLoaderManager = null;
}
spinner = null;
settings = null;
//view = null;
cursorAdapter = null;
listView = null;
weeklyView = null;
emptyView = null;
switcher = null;
} catch (Exception err) {
Timber.e(err, "failed to destroy view release");
}
super.onDestroyView();
}
}
Используя инспектор логов и макетов, я решил, что фрагмент раздувается с нулевой высотой и нулевым размером. HistoryFragment.onCreateView в этом случае никогда не вызывается.
В качестве обходного пути я заставил фрагмент «самовосстанавливаться», расширив этот базовый (DialogFragment) класс:
public class ErgoDialogFragment extends DialogFragment {
boolean hasInflatedProper = false;
private Runnable selfFix;
private Handler healHandler;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setStyle(STYLE_NO_FRAME, android.R.style.Theme_Holo_Light_NoActionBar_Fullscreen);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if (ErgoApplication.isAndroid9())
view.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
@Override
public void onLayoutChange(View v, int left, int top,
int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
Timber.d("Inflated history fragment has left = %d,
top=%d, right =%d, bottom=%d", left, top, right, bottom);
if (bottom != 800 && right != 1280) {
for (int i = 0; i < 10; i++)
Timber.e("HistoryFragment inflated, but not shown ! ");
} else
hasInflatedProper = true;
v.removeOnLayoutChangeListener(this);
}
});
}
@Override
public void onResume() {
super.onResume();
if (selfFix != null) {
if (ErgoApplication.isAndroid9()) {
//To be executed only for new tablets.....
if (healHandler == null) {
healHandler = new Handler();
DefaultExecutorSupplier.getInstance().registerHandler("history_healing", healHandler);
}
healHandler.postDelayed(() -> {
if (!hasInflatedProper) {
try {
if (selfFix != null)
selfFix.run();
} catch (Exception e) {
Timber.w(e, "Failed to self heal view.....");
}
}
}, 500);
}
}
}
public void addFixingRunnable(Runnable runner) {
this.selfFix = runner;
}
@Override
public void onPause() {
if (healHandler != null)
healHandler.removeCallbacksAndMessages(null);
super.onPause();
}
}
Если фрагмент не отображается должным образом - я открываю другой DialogFragment (см. AddFixingRunnable) и закрываю его через мгновение - в результате отображается желаемый фрагмент.
Кто-нибудь замечает, что я делаю не так ? Я хочу реализовать это правильно ....
Обозначьте: я ничего не делаю с фрагментами / supportFragmentManager в хостинге FragmentActivitys onPause / onResume
Для справки, это макет HistoryFragment:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/background_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/solid_white_transparency"
android:orientation="vertical"
tools:showIn="@layout/startmenu"
android:onClick="doNothing">
<de.ergo.frontend.custumviews.DialogHeader
android:id="@+id/dialogHeader1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true">
</de.ergo.frontend.custumviews.DialogHeader>
<de.ergo.frontend.custumviews.Switcher
android:id="@+id/mode_switcher"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignTop="@+id/dialogHeader1"
android:layout_alignEnd="@+id/dialogHeader1"
android:padding="15dp" />
<de.ergo.frontend.custumviews.WeekStatsView
android:id="@+id/weekly_view"
android:layout_width="match_parent"
android:visibility="visible"
android:layout_below="@id/dialogHeader1"
android:layout_height="wrap_content">
</de.ergo.frontend.custumviews.WeekStatsView>
<ListView
android:id="@+id/backgroundListView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="false"
android:divider="@color/invisible"
android:layout_below="@id/weekly_view"
android:dividerHeight="20dp"
android:headerDividersEnabled="true"
android:listSelector="@android:color/transparent"
android:scrollbars="vertical"
tools:listitem="@layout/item_workout_rowing"
tools:visibility="invisible">
</ListView>
<ProgressBar
android:id="@+id/spinner"
style="?android:attr/progressBarStyleLarge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:gravity="center"
android:indeterminate="true" />
<TextView
android:id="@+id/emptyView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="128dp"
android:text="@string/history_empty_text"
android:layout_below="@id/spinner"
android:layout_centerHorizontal="true"
android:gravity="center"
android:textColor="@color/white"
android:textSize="40sp" />
</RelativeLayout>
Я попытался переключиться обратно на обычный фрагмент без диалога. Также принудительное переопределение параметров макета жестко заданными значениями по-прежнему не работает. Я изменил цель фрагментов, например, на определенный держатель xml или с помощью android .R.id.content - все равно безуспешно.
Я использую фрагменты androidx и диспетчер фрагментов поддержки
Просвети меня, пожалуйста!