Управление фрагментами ящика навигации
Я разрабатываю приложение, в котором я использую только 1 главную активность и множественный фрагмент, включая ViewPager
, пользовательское видео/галерею изображений, полноэкранный фрагмент (без панели инструментов или нижней кнопки навигации). Я не уверен, что это хорошая практика или нет, но я столкнулся с несколькими причинами этого.
Изображение выше является фактической иерархией приложений. После проблемы, с которой я столкнулся.
- Панель инструментов не изменяет название фрагмента, когда нажимается кнопка "Назад" или вперед, нажав кнопку или какую-либо ссылку.
- Навигационный гамбургер продолжает показывать, если я
using: getSupportActionBar().setDisplayHomeAsUpEnabled(true);
в стрелку назад, using: getSupportActionBar().setDisplayHomeAsUpEnabled(true);
то стрелка назад открывает выдвижные ящики, но не возвращается к последнему фрагменту. - Fragment State Loss при нажатии кнопки возврата или прямое переключение на фрагмент.
- Является ли хорошей практикой выполнение всей задачи внутри
Fragment
с помощью Single Activity
.
Я также использую одно приложение для панели инструментов. Toolbar.xml
<android.support.v7.widget.Toolbar
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/primary"
app:contentInsetLeft="0dp"
app:contentInsetStart="0dp"
app:contentInsetStartWithNavigation="0dp"
android:fitsSystemWindows="true"
app:layout_collapseMode="pin"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/toolbar_connections"
android:visibility="visible"
android:orientation="horizontal">
<ImageView
android:layout_width="35dp"
android:layout_height="match_parent"
android:id="@+id/appLogo"
android:layout_gravity="center_vertical" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|center_horizontal"
android:textSize="22sp"
android:id="@+id/activityTitle"
android:textColor="@color/primary_text"
/>
</LinearLayout>
<LinearLayout
android:id="@+id/toolbar_chat"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone"
android:orientation="horizontal">
<de.hdodenhof.circleimageview.CircleImageView
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_gravity="center_vertical"
android:layout_marginRight="5dp"
android:src="@drawable/baby"
android:id="@+id/User_Image_Toolbar"/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:id="@+id/User_Name_Toolbar"
android:textSize="17sp"
android:textStyle="bold"
android:layout_marginBottom="5dp"
android:text="My Name"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Online"
android:textStyle="italic"
android:id="@+id/User_Online_Status_Toolbar"
android:layout_marginBottom="5dp"
android:layout_below="@+id/User_Name_Toolbar" />
</LinearLayout>
</LinearLayout>
</android.support.v7.widget.Toolbar>
Ящик навигации (Single Activity, который является родителем всех фрагментов)
public class Navigation_Drawer extends AppCompatActivity implements UserData {
Toolbar toolbar;
DrawerLayout drawerLayout;
NavigationView navigationView;
String navTitles[];
TypedArray navIcons;
RecyclerView.Adapter recyclerViewAdapter;
ActionBarDrawerToggle drawerToggle;
public static final String TAG = "###Navigation Drawer###";
boolean nextScreen;
//Header
ImageView headerImage,headerUserImage;
TextView userName,userViews;
Context context = this;
//Setting Tabs
ViewPager viewPager;
TabAdapter tabAdapter;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.navigation_drawer);
//Initialise Views
drawerLayout = findViewById(R.id.Navigation_Drawer_Main);
navigationView = findViewById(R.id.nvView);
setupToolbar();
navigationView.setItemIconTintList(null);
setupDrawerContent(navigationView);
settingHeaderItems();
drawerToggle = setupDrawerToggle();
getSupportActionBar().setHomeButtonEnabled(true);
drawerLayout.addDrawerListener(drawerToggle);
viewPager = findViewById(R.id.Navigation_Drawer_ViewPager);
tabAdapter = new TabAdapter(getFragmentManager(), this, false);
viewPager.setAdapter(tabAdapter);
}
public void setupToolbar() {
toolbar = findViewById(R.id.Navigation_Drawer_toolbar);
setSupportActionBar(toolbar);
}
private ActionBarDrawerToggle setupDrawerToggle() {
// NOTE: Make sure you pass in a valid toolbar reference. ActionBarDrawToggle() does not require it
// and will not render the hamburger icon without it.
//return new ActionBarDrawerToggle(this, drawerLayout, toolbar, R.string.drawer_open, R.string.drawer_close);
return new ActionBarDrawerToggle(this, drawerLayout,toolbar, R.string.drawer_open, R.string.drawer_close);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
// inflater.inflate(R.menu.main_menu, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
//Handle Item Selection
return super.onOptionsItemSelected(item);
}
private void setupDrawerContent(NavigationView navigationView) {
navigationView.setNavigationItemSelectedListener(
new NavigationView.OnNavigationItemSelectedListener() {
@Override
public boolean onNavigationItemSelected(MenuItem menuItem) {
selectDrawerItem(menuItem);
return true;
}
});
}
public void ChangeFragment_ViewPager(int position, boolean outside) {
if (outside) {
Log.d(TAG, "Change Fragment Calling From Outside");
tabAdapter = new TabAdapter(getFragmentManager(), this, false);
viewPager.setAdapter(tabAdapter);
}
viewPager.setCurrentItem(position);
}
@Override
public void onBackPressed() {
super.onBackPressed();
showSystemUI();
Log.d(TAG, "On Back Pressed");
}
public void showSystemUI() {
if (getWindow() != null) {
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
getSupportActionBar().show();
} else {
return;
}
}
public void selectDrawerItem(MenuItem menuItem) {
// Create a new fragment and specify the fragment to show based on nav item clicked
Fragment fragment = null;
switch (menuItem.getItemId()) {
case R.id.HeaderImageView:
fragment = new EditProfile();
break;
case R.id.home_Fragment:
Log.d(TAG,"Home Fragment Pressed ");
getFragmentManager().popBackStack(null, android.app.FragmentManager.POP_BACK_STACK_INCLUSIVE);
ChangeFragment_ViewPager(0,false);
// Highlight the selected item has been done by NavigationView
menuItem.setChecked(true);
// Set action bar title
setTitle(menuItem.getTitle());
// Close the navigation drawer
drawerLayout.closeDrawers();
return;
case R.id.ppl_Fragment:
Log.d(TAG,"PPL Fragment Pressed ");
ChangeFragment_ViewPager(1,false);
// Highlight the selected item has been done by NavigationView
menuItem.setChecked(true);
// Set action bar title
setTitle(menuItem.getTitle());
// Close the navigation drawer
drawerLayout.closeDrawers();
return;
case R.id.message_Fragment:
Log.d(TAG,"Message Fragment Pressed ");
fragment = new Messages_Fragment();
break;
case R.id.addMedia_Fragment:
Log.d(TAG,"Add Media Fragment Pressed ");
fragment = new UserProfile_Photos();
break;
case R.id.invite_Fragment:
Log.d(TAG,"Invite Fragment Pressed ");
//fragmentClass = fragment_1.class;
onInviteClicked();
// Highlight the selected item has been done by NavigationView
menuItem.setChecked(true);
// Set action bar title
setTitle(menuItem.getTitle());
// Close the navigation drawer
drawerLayout.closeDrawers();
return;
case R.id.setting_Fragment:
Log.d(TAG,"Setting Fragment Pressed ");
fragment = new Setting_NavigationDrawer();
break;
case R.id.help_Fragment:
Log.d(TAG,"Help Fragment Pressed ");
//fragmentClass = fragment_1.class;
fragment=new FullScreen_WebView();
Bundle urlToSend=new Bundle();
urlToSend.putString("webViewURL","http://boysjoys.com/test/Android/Data/help.php");
//urlToSend.putString("webViewURL",chat_wrapper.getGoogleSearch().get(2));
fragment.setArguments(urlToSend);
FragmentTransaction transaction=((Activity)context).getFragmentManager().beginTransaction();
//fragmentTrasaction.replace(R.id.Chat_Screen_Main_Layout,gallery);
//transaction.replace(R.id.Chat_Screen_Main_Layout,fullScreen_webView);
transaction.replace(R.id.Navigation_Main_Layout,fragment);
transaction.addToBackStack(null);
transaction.commit();
// Highlight the selected item has been done by NavigationView
menuItem.setChecked(true);
// Set action bar title
setTitle(menuItem.getTitle());
// Close the navigation drawer
drawerLayout.closeDrawers();
return;
case R.id.signOut_Fragment:
new CheckLoginStatus(this, 0).execute();
new Send_Session_Logout(this).execute();
drawerLayout.closeDrawers();
return;
}
FragmentTransaction fragmentTransaction=getFragmentManager().beginTransaction();
fragmentTransaction.add(R.id.Navigation_Main_Layout, fragment);
fragmentTransaction.setCustomAnimations(R.animator.enter_anim,R.animator.exit_anim);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
// Highlight the selected item has been done by NavigationView
menuItem.setChecked(true);
// Set action bar title
setTitle(menuItem.getTitle());
// Close the navigation drawer
drawerLayout.closeDrawers();
}
private void settingHeaderItems(){
View HeaderLayout = navigationView.inflateHeaderView(R.layout.navigation_header_image);
//Main Screen Tabs With VIew Pager
headerImage = HeaderLayout.findViewById(R.id.HeaderImageView);
headerImage.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
FragmentTransaction fragmentTransaction=getFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.Navigation_Main_Layout, new EditProfile());
fragmentTransaction.setCustomAnimations(R.animator.enter_anim,R.animator.exit_anim);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
drawerLayout.closeDrawers();
}
});
headerUserImage = HeaderLayout.findViewById(R.id.HeaderProfilePicture);
userName = HeaderLayout.findViewById(R.id.myImageViewText);
userViews = HeaderLayout.findViewById(R.id.profileViews);
if (Session.getUserCover().equals("Invalid Image")){
headerImage.setBackgroundResource(R.drawable.cam_icon);
}else {
Log.d(TAG,"Path Of Cover Photo "+Session.getUserCover());
Bitmap coverPhoto= BitmapFactory.decodeFile(Session.getUserCover());
headerImage.setImageBitmap(coverPhoto);
// Glide.with(context).load(Session.getUserCover()).apply(new RequestOptions().skipMemoryCache(true).onlyRetrieveFromCache(false).diskCacheStrategy(DiskCacheStrategy.NONE)).into(holder.HeaderImage);
}
Bitmap bitmap = BitmapFactory.decodeFile(Session.getUserImage());
userName.setText(Session.getUserFname()+" "+Session.getUserLname());
headerUserImage.setImageBitmap(bitmap);
if (Session.getProfileCounter().equals("0")){
userViews.setText("No Profile VIsits");
}
else {
userViews.setText("Profile views: "+ Session.getProfileCounter());
}
}
@Override
protected void onPostCreate(@Nullable Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
drawerToggle.syncState();
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
drawerToggle.onConfigurationChanged(newConfig);
}
}
Я устал много, чтобы решить эту проблему, и после нескольких месяцев поиска в googling и stackoverflow я все еще застрял в одной и той же проблеме.
Пример проблемы в пункте 1: - При загрузке навигационного ящика сначала все выглядит хорошо, просмотр заголовка пейджера изменяется по фрагменту. затем, если я нажму на Меню навигационных ящиков, которые также откроют другой фрагмент (For Ex: Recent Message). то смена названия успешно, но когда я нажимаю кнопку "Назад" или пытаюсь нажать кнопку "домой", которая вызывает viewpager, тогда заголовок останется таким же, как и раньше, т.е. "Недавнее сообщение".
Установка заголовка в каждом фрагменте, как это.
toolbar = (Toolbar) getActivity().findViewById(R.id.Navigation_Drawer_toolbar);
ImageView appLogo = toolbar.findViewById(R.id.appLogo);
TextView fragmentTitle = toolbar.findViewById(R.id.activityTitle);
appLogo.setImageResource(DrawableImage);
fragmentTitle.setText(Title);
Ответы
Ответ 1
Является ли хорошей практикой выполнение всей задачи внутри фрагмента с помощью Single Activity.
Рекомендуется использовать фрагменты, когда вы используете навигационные ящики, вкладки или нижнюю навигацию.
Помимо этого, фрагменты в основном предпочтительны для многоразового пользовательского интерфейса с разными данными.
Вы выполняете всю задачу внутри фрагмента с помощью Single Activity, потому что используете навигационную ничью и вкладки, так что это хорошая практика, я бы сказал.
Панель инструментов не изменяет название фрагмента, когда нажимается кнопка "Назад" или вперед, нажав кнопку или какую-либо ссылку.
Вам нужно вручную установить заголовок для фрагмента, когда вы добавляете/заменяете новый фрагмент ИЛИ
(backstack change), только через Fragment или используя OnBackStackChangedListener, как указано в документах.
Вы просто устанавливаете их, пока вы нажимаете элементы навигации, а не нажимаете кнопку или какую-либо ссылку
Навигационный гамбургер продолжает показывать, если я перехожу в стрелку назад, используя: getSupportActionBar(). SetDisplayHomeAsUpEnabled (true); то стрелка назад открывает выдвижные ящики, но не возвращается к последнему фрагменту.
Это тоже нужно будет обрабатывать вручную. используя onOptionItemSelected при щелчке элемента android.R.id.home. Как выкладывать фрагменты из стека.
Причиной сохраняемости названия на задней панели является настройка заголовка из Activity. т.е. используя этот setTitle(menuItem.getTitle());
вместо этого установите их через Фрагмент, или если вы устанавливаете их через Fragment remove из метода selectDrawerItem
.
Кроме того, в конце метода selectDrawerItem
вы используете fragmentTransaction.add
selectDrawerItem
вместо этого, используйте selectDrawerItem
fragmentTransaction.replace
Ответ 2
Если приложение должно использовать навигационный ящик, который должен присутствовать во всех представлениях, тогда следует использовать фрагмент. И это не плохая практика.
- Панель инструментов не изменяет название фрагмента, когда нажимается кнопка "Назад" или вперед, нажав кнопку или какую-либо ссылку.
Создание метода в базовой операции
public void setFragmentTitle(String title){
if(!TextUtils.isEmpty(title))
mTitleText.setText(title);
}
Получите доступ к этому методу из ваших отдельных фрагментов в onCreateView
((LandingActivity) getActivity()).setFragmentTitle(getActivity().getString(R.string.fragment_title));
-
Навигационный гамбургер продолжает показывать, если я перехожу в стрелку назад, используя: getSupportActionBar(). SetDisplayHomeAsUpEnabled (true); то стрелка назад открывает выдвижные ящики, но не возвращается к последнему фрагменту.
Использовать onOptionItemSelected
при щелчке android.R.id.home
, onOptionItemSelected
текущий Fragment
-
Fragment State Loss при нажатии кнопки возврата или прямое переключение на фрагмент
Вы должны упомянуть ценности, которые необходимо сохранить и повторно заселить.
public class ActivityABC....{
private String mFName;
private TableSelectFragment mFragment;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FragmentManager fm = getSupportFragmentManager();
if (savedInstanceState != null) {
mFragment=(TableSelectFragment)fm.getFragment(savedInstanceState,"TABLE_FRAGMENT");
mFName = savedInstanceState.getString("FNAMETAG");
}else{
mFragment = new TableSelectFragment();
fm.beginTransaction().add(R.id.content_frame,mFragment,"TABLE_FRAGMENT").commit();
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
getSupportFragmentManager().putFragment(outState,"TABLE_FRAGMENT",mFragment);
}
}
В вашем фрагменте
TableSelectFragment{
....
private String mFName;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
outState.putString("FNAMETAG", mFName);
super.onSaveInstanceState(outState);
}
}
РЕДАКТИРОВАТЬ 1: для заголовка фрагмента не обновляется в приложении BackButton
Добавляя Fragment
в backstack, сделайте следующее.
В вашей родительской деятельности
FragmentManager fragMan = getSupportFragmentManager();
FragmentTransaction fragTrans = fragMan.beginTransaction();
LandingFrag landingFrag = LandingFrag.newInstance();
fragTrans.replace(R.id.landing_view, landingFrag,"LandingFrag");
fragTrans.addToBackStack(null);
fragTrans.commit();
landingFrag.setUserVisibleHint(true);
Теперь переопределить onBackPressed в родительской активности
@Override
public void onBackPressed() {
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
if (drawer.isDrawerOpen(GravityCompat.START)) {
drawer.closeDrawer(GravityCompat.START);
} else {
.. POP Fragment Backstack here
Fragment fragment = getActiveFragment();
if(fragment instanceof LandingFrag)
{
LandingFrag landingFrag = (LandingFrag)fragment;
landingFrag.setUserVisibleHint(true);
}
}
public Fragment getActiveFragment() {
if (getSupportFragmentManager().getBackStackEntryCount() == 0) {
return null;
}
Fragment fragment=null;
int trackBackValue = 1;//INCREASE OR DECREASE ACCORDING TO YOUR BACK STACK
try {
fragment = getSupportFragmentManager().getFragments().get(getSupportFragmentManager().getBackStackEntryCount() - trackBackValue);
} catch (Exception e) {
}
return fragment;
}
Сейчас в LandingFrag
public class LandingFrag...
{
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setUserVisibleHint(false);
.....
}
@Override
public void onViewStateRestored(@Nullable Bundle savedInstanceState) {
super.onViewStateRestored(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
................
((LandingActivity) getActivity()).setFragmentTitle("Current Fragment Title");
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if(isVisibleToUser){
try {
((LandingActivity) getActivity()).setFragmentTitle("Current Fragment Title");
} catch (Exception e) {
e.printStackTrace();
}
}
}
}