如何正确实现导航抽屉中的顶部后退按钮?
How to properly implement the top back button in navigation drawer?
我目前正在尝试将导航抽屉添加到我的天气应用程序中,所以我看了一个 youtube
关于它的教程,并能够按照我想要的方式实现它,直到我意识到
我看的教程没有涵盖如何实现 up/top 返回
导航抽屉的按钮,事实上,我目前无法返回
打开任何导航选项卡后我的默认片段。我搜索了几个
网站和 youtube 视频寻找关于如何实现顶背的教程
按钮,但 seen/been 找不到它。我也搜索了这个网站,但仍然
在这里没有找到有类似问题的人。拜托,有人能帮忙吗?
这是我的应用当前的屏幕截图:https://i.stack.imgur.com/SeSjV.png 但如果我打开任何导航栏选项,即设置并单击返回,我无法 return 返回到默认片段显示天气。它也没有向上返回按钮。
目前,点击返回只会退出应用程序。
这是我尝试过但没有用的唯一代码:
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) {
int backStackCount = fragmentManager.getBackStackEntryCount();//check currently how many frags loaded
if (backStackCount > 0) {
fragmentManager.popBackStack(); //go back to previously loaded fragment
}
}
return super.onOptionsItemSelected(item);
}
它给出了以下错误:
error: cannot find symbol
int backStackCount = fragmentManager.getBackStackEntryCount();//check currently how many
frags loaded
这是我的 activity 代码:
public class HomeActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener {
private DrawerLayout drawer;
// Last update time, click sound, search button, search panel.
TextView timeField;
MediaPlayer player;
ImageView Search;
EditText textfield;
// For scheduling background image change(using constraint layout, start counting from dubai, down to statue of liberty.
ConstraintLayout constraintLayout;
public static int count = 0;
int[] drawable = new int[]{R.drawable.dubai, R.drawable.norway, R.drawable.eiffel_tower, R.drawable.hong_kong, R.drawable.statue_of_liberty,
R.drawable.beijing, R.drawable.chicago, R.drawable.colombia, R.drawable.vienna,R.drawable.tokyo};
Timer _t;
private WeatherDataViewModel viewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
// use home activity layout.
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
// Allow activity to make use of the toolbar
drawer = findViewById(R.id.drawer_layout);
NavigationView navigationView = findViewById(R.id.nav_view);
navigationView.setNavigationItemSelectedListener(this);
viewModel = new ViewModelProvider(this).get(WeatherDataViewModel.class);
// Trigger action to open & close nevigation drawer
ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(this, drawer, toolbar
, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
drawer.addDrawerListener(toggle);
toggle.syncState();
timeField = findViewById(R.id.textView9);
Search = findViewById(R.id.imageView4);
textfield = findViewById(R.id.textfield);
// find the id's of specific variables.
BottomNavigationView bottomNavigationView = findViewById(R.id.bottomNavigationView);
// host 3 fragments along with bottom navigation.
final NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.fragment);
assert navHostFragment != null;
final NavController navController = navHostFragment.getNavController();
NavigationUI.setupWithNavController(bottomNavigationView, navController);
// Make hourly & daily tab unusable
bottomNavigationView.setOnNavigationItemSelectedListener(item -> {
if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
getSupportFragmentManager().popBackStack();
}
return false;
});
navController.addOnDestinationChangedListener((controller, destination, arguments) -> navController.popBackStack(destination.getId(), false));
// For scheduling background image change
constraintLayout = findViewById(R.id.layout);
constraintLayout.setBackgroundResource(R.drawable.dubai);
_t = new Timer();
_t.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
// run on ui thread
runOnUiThread(() -> {
if (count < drawable.length) {
constraintLayout.setBackgroundResource(drawable[count]);
count = (count + 1) % drawable.length;
}
});
}
}, 5000, 5000);
Search.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// make click sound when search button is clicked.
player = MediaPlayer.create(HomeActivity.this, R.raw.click);
player.start();
getWeatherData(textfield.getText().toString().trim());
// make use of some fragment's data
Fragment currentFragment = navHostFragment.getChildFragmentManager().getFragments().get(0);
if (currentFragment instanceof FirstFragment) {
FirstFragment firstFragment = (FirstFragment) currentFragment;
firstFragment.getWeatherData(textfield.getText().toString().trim());
} else if (currentFragment instanceof SecondFragment) {
SecondFragment secondFragment = (SecondFragment) currentFragment;
secondFragment.getWeatherData(textfield.getText().toString().trim());
} else if (currentFragment instanceof ThirdFragment) {
ThirdFragment thirdFragment = (ThirdFragment) currentFragment;
thirdFragment.getWeatherData(textfield.getText().toString().trim());
}
}
private void getWeatherData(String name) {
ApiInterface apiInterface = ApiClient.getClient().create(ApiInterface.class);
Call<Example> call = apiInterface.getWeatherData(name);
call.enqueue(new Callback<Example>() {
@Override
public void onResponse(@NonNull Call<Example> call, @NonNull Response<Example> response) {
try {
assert response.body() != null;
timeField.setVisibility(View.VISIBLE);
timeField.setText("First Updated:" + " " + response.body().getDt());
} catch (Exception e) {
timeField.setVisibility(View.GONE);
timeField.setText("First Updated: Unknown");
Log.e("TAG", "No City found");
Toast.makeText(HomeActivity.this, "No City found", Toast.LENGTH_SHORT).show();
}
}
@Override
public void onFailure(@NotNull Call<Example> call, @NotNull Throwable t) {
t.printStackTrace();
}
});
}
});
}
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.settings_id:
getSupportFragmentManager().beginTransaction().replace(R.id.fragment,
new Settings()).commit();
break;
case R.id.ads_upgrade_id:
getSupportFragmentManager().beginTransaction().replace(R.id.fragment,
new Upgrade()).commit();
break;
case R.id.privacy_policy_id:
getSupportFragmentManager().beginTransaction().replace(R.id.fragment,
new Privacy_Policy()).commit();
break;
}
drawer.closeDrawer(GravityCompat.START);
return true;
}
@Override
public void onBackPressed() {
if (drawer.isDrawerOpen(GravityCompat.START)) {
drawer.closeDrawer(GravityCompat.START);
} else {
super.onBackPressed();
// Open/close drawer animation
}
}
我认为您应该覆盖 MainActivity
处的 onKeyDown
,它控制整个应用程序的虚拟后退按钮的行为。现在任何点击这个按钮都会退出应用程序,因为你只有一个“页面”包含 fragments
并在它们之间切换,所以如果你从这个页面返回,你会退出应用程序...
我在 MainActivty
处有一个 public String
,它包含 current_fragment,每次切换时我都会更新它 fragment
:
MainActivity(onCreate()
之前)
public String current_page = "";
任意片段
public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
mainActivity.current_fragment = "anyFragment";
首页片段
public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
mainActivity.current_fragment = "home_page";
MainActivity
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) { // you only want to treat the back click, not any click...
if (drawerLayout.isDrawerOpen(GravityCompat.START)) { // if drawer is open, close it
drawerLayout.closeDrawer(GravityCompat.START);
return true;
} else if (!current_page.equals ("home_page") {
setHomePage(); // a method that switch the fragment to homePageFragment
return true;
}
}
return super.onKeyDown(keyCode, event);
}
关于 setHomePage () - 这是一种在 Fragment 之间切换的方法,就像您已经在您的应用程序中所做的那样...
您可以尝试在 mobile_navigation.xml 的根布局中添加 app:startDestination="@+id/your default fragment"
属性。
mobile_navigation.xml 是一个文件,您可以在其中管理您的片段,就像在这个例子中一样
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/mobile_navigation"
app:startDestination="@+id/nav_home">
<fragment
android:id="@+id/nav_home"
android:name="com.example.navd.ui.home.HomeFragment"
android:label="@string/menu_home"
tools:layout="@layout/fragment_home" />
<fragment
android:id="@+id/nav_gallery"
android:name="com.example.navd.ui.gallery.GalleryFragment"
android:label="@string/menu_gallery"
tools:layout="@layout/fragment_gallery" />
<fragment
android:id="@+id/nav_slideshow"
android:name="com.example.navd.ui.slideshow.SlideshowFragment"
android:label="@string/menu_slideshow"
tools:layout="@layout/fragment_slideshow" />
if (item.getItemId() == android.R.id.home) {
int backStackCount = fragmentManager.getBackStackEntryCount();//check currently how many frags loaded
if (backStackCount > 0) {
fragmentManager.popBackStack(); //go back to previously loaded fragment
}
}
Currently, clicking back only exits the app.
使用popBackStack()
会弹出backstack让你的应用程序存在,但你只需要return到默认片段。
要解决此问题,您需要更改抽屉汉堡按钮的行为,以便有时可以使用它来打开抽屉 navView 布局,而其他时候则可以返回默认片段;后者是您想要添加顶部后退按钮的时候。
how I can implement the up/top back button for the nav drawer
这需要访问 setToolbarNavigationClickListener
方法,该方法使您可以为汉堡点击添加侦听器。
在这种情况下,您需要根据需要return回到主页片段,在onCreate()
方法中添加:
toggle.setToolbarNavigationClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// Enable the functionality of opening the side drawer, when the burger icon is clicked
toggle.setDrawerIndicatorEnabled(true); // Show the burger icon & enable the drawer funcionality
navController.navigate(R.id.home); // Back to default fragment (replace home with your default fragment id in the navGraph)
}
});
剩下的部分是当你想转到某个片段时显示后退按钮
并在单击 home/burger 图标时使用 toggle.setDrawerIndicatorEnabled()
来 enable/disable opening/closing 抽屉的功能
navController.addOnDestinationChangedListener(new NavController.OnDestinationChangedListener() {
@Override
public void onDestinationChanged(@NonNull NavController controller, @NonNull NavDestination destination, @Nullable Bundle arguments) {
// Repeat this condition for all the Fragments that you want to show the back button
if (destination.getId() == R.id.settings_id) { // replace `settings_id` with your fragment id in the navGraph that you want to show the back button
// Disable the functionality of opening the side drawer, when the burger icon is clicked & show the UP button instead
toggle.setDrawerIndicatorEnabled(false);
}
}
});
我目前正在尝试将导航抽屉添加到我的天气应用程序中,所以我看了一个 youtube 关于它的教程,并能够按照我想要的方式实现它,直到我意识到 我看的教程没有涵盖如何实现 up/top 返回 导航抽屉的按钮,事实上,我目前无法返回 打开任何导航选项卡后我的默认片段。我搜索了几个 网站和 youtube 视频寻找关于如何实现顶背的教程 按钮,但 seen/been 找不到它。我也搜索了这个网站,但仍然 在这里没有找到有类似问题的人。拜托,有人能帮忙吗?
这是我的应用当前的屏幕截图:https://i.stack.imgur.com/SeSjV.png 但如果我打开任何导航栏选项,即设置并单击返回,我无法 return 返回到默认片段显示天气。它也没有向上返回按钮。
目前,点击返回只会退出应用程序。
这是我尝试过但没有用的唯一代码:
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) {
int backStackCount = fragmentManager.getBackStackEntryCount();//check currently how many frags loaded
if (backStackCount > 0) {
fragmentManager.popBackStack(); //go back to previously loaded fragment
}
}
return super.onOptionsItemSelected(item);
}
它给出了以下错误:
error: cannot find symbol int backStackCount = fragmentManager.getBackStackEntryCount();//check currently how many frags loaded
这是我的 activity 代码:
public class HomeActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener {
private DrawerLayout drawer;
// Last update time, click sound, search button, search panel.
TextView timeField;
MediaPlayer player;
ImageView Search;
EditText textfield;
// For scheduling background image change(using constraint layout, start counting from dubai, down to statue of liberty.
ConstraintLayout constraintLayout;
public static int count = 0;
int[] drawable = new int[]{R.drawable.dubai, R.drawable.norway, R.drawable.eiffel_tower, R.drawable.hong_kong, R.drawable.statue_of_liberty,
R.drawable.beijing, R.drawable.chicago, R.drawable.colombia, R.drawable.vienna,R.drawable.tokyo};
Timer _t;
private WeatherDataViewModel viewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
// use home activity layout.
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
// Allow activity to make use of the toolbar
drawer = findViewById(R.id.drawer_layout);
NavigationView navigationView = findViewById(R.id.nav_view);
navigationView.setNavigationItemSelectedListener(this);
viewModel = new ViewModelProvider(this).get(WeatherDataViewModel.class);
// Trigger action to open & close nevigation drawer
ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(this, drawer, toolbar
, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
drawer.addDrawerListener(toggle);
toggle.syncState();
timeField = findViewById(R.id.textView9);
Search = findViewById(R.id.imageView4);
textfield = findViewById(R.id.textfield);
// find the id's of specific variables.
BottomNavigationView bottomNavigationView = findViewById(R.id.bottomNavigationView);
// host 3 fragments along with bottom navigation.
final NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.fragment);
assert navHostFragment != null;
final NavController navController = navHostFragment.getNavController();
NavigationUI.setupWithNavController(bottomNavigationView, navController);
// Make hourly & daily tab unusable
bottomNavigationView.setOnNavigationItemSelectedListener(item -> {
if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
getSupportFragmentManager().popBackStack();
}
return false;
});
navController.addOnDestinationChangedListener((controller, destination, arguments) -> navController.popBackStack(destination.getId(), false));
// For scheduling background image change
constraintLayout = findViewById(R.id.layout);
constraintLayout.setBackgroundResource(R.drawable.dubai);
_t = new Timer();
_t.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
// run on ui thread
runOnUiThread(() -> {
if (count < drawable.length) {
constraintLayout.setBackgroundResource(drawable[count]);
count = (count + 1) % drawable.length;
}
});
}
}, 5000, 5000);
Search.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// make click sound when search button is clicked.
player = MediaPlayer.create(HomeActivity.this, R.raw.click);
player.start();
getWeatherData(textfield.getText().toString().trim());
// make use of some fragment's data
Fragment currentFragment = navHostFragment.getChildFragmentManager().getFragments().get(0);
if (currentFragment instanceof FirstFragment) {
FirstFragment firstFragment = (FirstFragment) currentFragment;
firstFragment.getWeatherData(textfield.getText().toString().trim());
} else if (currentFragment instanceof SecondFragment) {
SecondFragment secondFragment = (SecondFragment) currentFragment;
secondFragment.getWeatherData(textfield.getText().toString().trim());
} else if (currentFragment instanceof ThirdFragment) {
ThirdFragment thirdFragment = (ThirdFragment) currentFragment;
thirdFragment.getWeatherData(textfield.getText().toString().trim());
}
}
private void getWeatherData(String name) {
ApiInterface apiInterface = ApiClient.getClient().create(ApiInterface.class);
Call<Example> call = apiInterface.getWeatherData(name);
call.enqueue(new Callback<Example>() {
@Override
public void onResponse(@NonNull Call<Example> call, @NonNull Response<Example> response) {
try {
assert response.body() != null;
timeField.setVisibility(View.VISIBLE);
timeField.setText("First Updated:" + " " + response.body().getDt());
} catch (Exception e) {
timeField.setVisibility(View.GONE);
timeField.setText("First Updated: Unknown");
Log.e("TAG", "No City found");
Toast.makeText(HomeActivity.this, "No City found", Toast.LENGTH_SHORT).show();
}
}
@Override
public void onFailure(@NotNull Call<Example> call, @NotNull Throwable t) {
t.printStackTrace();
}
});
}
});
}
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.settings_id:
getSupportFragmentManager().beginTransaction().replace(R.id.fragment,
new Settings()).commit();
break;
case R.id.ads_upgrade_id:
getSupportFragmentManager().beginTransaction().replace(R.id.fragment,
new Upgrade()).commit();
break;
case R.id.privacy_policy_id:
getSupportFragmentManager().beginTransaction().replace(R.id.fragment,
new Privacy_Policy()).commit();
break;
}
drawer.closeDrawer(GravityCompat.START);
return true;
}
@Override
public void onBackPressed() {
if (drawer.isDrawerOpen(GravityCompat.START)) {
drawer.closeDrawer(GravityCompat.START);
} else {
super.onBackPressed();
// Open/close drawer animation
}
}
我认为您应该覆盖 MainActivity
处的 onKeyDown
,它控制整个应用程序的虚拟后退按钮的行为。现在任何点击这个按钮都会退出应用程序,因为你只有一个“页面”包含 fragments
并在它们之间切换,所以如果你从这个页面返回,你会退出应用程序...
我在 MainActivty
处有一个 public String
,它包含 current_fragment,每次切换时我都会更新它 fragment
:
MainActivity(onCreate()
之前)
public String current_page = "";
任意片段
public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
mainActivity.current_fragment = "anyFragment";
首页片段
public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
mainActivity.current_fragment = "home_page";
MainActivity
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) { // you only want to treat the back click, not any click...
if (drawerLayout.isDrawerOpen(GravityCompat.START)) { // if drawer is open, close it
drawerLayout.closeDrawer(GravityCompat.START);
return true;
} else if (!current_page.equals ("home_page") {
setHomePage(); // a method that switch the fragment to homePageFragment
return true;
}
}
return super.onKeyDown(keyCode, event);
}
关于 setHomePage () - 这是一种在 Fragment 之间切换的方法,就像您已经在您的应用程序中所做的那样...
您可以尝试在 mobile_navigation.xml 的根布局中添加 app:startDestination="@+id/your default fragment"
属性。
mobile_navigation.xml 是一个文件,您可以在其中管理您的片段,就像在这个例子中一样
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/mobile_navigation"
app:startDestination="@+id/nav_home">
<fragment
android:id="@+id/nav_home"
android:name="com.example.navd.ui.home.HomeFragment"
android:label="@string/menu_home"
tools:layout="@layout/fragment_home" />
<fragment
android:id="@+id/nav_gallery"
android:name="com.example.navd.ui.gallery.GalleryFragment"
android:label="@string/menu_gallery"
tools:layout="@layout/fragment_gallery" />
<fragment
android:id="@+id/nav_slideshow"
android:name="com.example.navd.ui.slideshow.SlideshowFragment"
android:label="@string/menu_slideshow"
tools:layout="@layout/fragment_slideshow" />
if (item.getItemId() == android.R.id.home) {
int backStackCount = fragmentManager.getBackStackEntryCount();//check currently how many frags loaded
if (backStackCount > 0) {
fragmentManager.popBackStack(); //go back to previously loaded fragment
}
}
Currently, clicking back only exits the app.
使用popBackStack()
会弹出backstack让你的应用程序存在,但你只需要return到默认片段。
要解决此问题,您需要更改抽屉汉堡按钮的行为,以便有时可以使用它来打开抽屉 navView 布局,而其他时候则可以返回默认片段;后者是您想要添加顶部后退按钮的时候。
how I can implement the up/top back button for the nav drawer
这需要访问 setToolbarNavigationClickListener
方法,该方法使您可以为汉堡点击添加侦听器。
在这种情况下,您需要根据需要return回到主页片段,在onCreate()
方法中添加:
toggle.setToolbarNavigationClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// Enable the functionality of opening the side drawer, when the burger icon is clicked
toggle.setDrawerIndicatorEnabled(true); // Show the burger icon & enable the drawer funcionality
navController.navigate(R.id.home); // Back to default fragment (replace home with your default fragment id in the navGraph)
}
});
剩下的部分是当你想转到某个片段时显示后退按钮
并在单击 home/burger 图标时使用 toggle.setDrawerIndicatorEnabled()
来 enable/disable opening/closing 抽屉的功能
navController.addOnDestinationChangedListener(new NavController.OnDestinationChangedListener() {
@Override
public void onDestinationChanged(@NonNull NavController controller, @NonNull NavDestination destination, @Nullable Bundle arguments) {
// Repeat this condition for all the Fragments that you want to show the back button
if (destination.getId() == R.id.settings_id) { // replace `settings_id` with your fragment id in the navGraph that you want to show the back button
// Disable the functionality of opening the side drawer, when the burger icon is clicked & show the UP button instead
toggle.setDrawerIndicatorEnabled(false);
}
}
});