I have implemented a navigation drawer and everything functions properly. The navigation drawer contains a listview and when an item is selected, the main fragment is replaced properly.
This is the onClickListener code:
mDrawerListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
HeaderViewListAdapter headerView = (HeaderViewListAdapter) parent.getAdapter();
AccountAdapter adapter = (AccountAdapter) headerView.getWrappedAdapter();
Cursor cursor = adapter.getCursor();
if(cursor != null && cursor.moveToPosition(position)){
((NavigationDrawerCallbacks) getActivity()).onNavigationDrawerItemSelected(cursor.getLong(cursor.getColumnIndex(ID_COLUMN)));
}
}
});
And in my activity, the onNavigationDrawerItemSelected method is like this:
#Override
public void onNavigationDrawerItemSelected(long accountId) {
// update the main content by replacing fragments
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction()
.replace(R.id.mainFragment, AccountDetailFragment.newInstance(accountId))
.commit();
}
As I said, this functions, but the navigation drawer remains open. It's a hassle for the user to have to select an account from the listview and then have to close the drawer to see the main fragment, so how can I have it close automatically, so that when an item is selected the only visible element is the main fragment (and everything inside of it)?
Call closeDrawer() on the DrawerLayout object with either the Drawer View, or the Drawer's Gravity as an argument.
You need to call the closeDrawer inside the onItemClick, when the Drawer Layout is not null.
if (mDrawerLayout != null) {
mDrawerLayout.closeDrawer(mFragmentContainerView);
}
Related
I am trying to implement behavior of opening bottom sheet when clicked on overflow menu. ex: expected behavior
I may do this on an activity using onMenuOpened as suggested here,
But I want to do this on fragment.
How to achieve this behavior on a fragment?
I am using single activity pattern and navigation architecture component.
Create a interface which will be implemented by your Fragment's
ex:
public interface OnMenuOpenListener(){
boolean onMenuOpened();
}
public class MyFragment extends Fragment implements OnMenuOpenListener{
#Override
public boolean onMenuOpened(){
//open bottom sheet here
}
}
public class MyActivity extends Activity{
#Override
public boolean onMenuOpened(int featureId, Menu menu) {
if(featureId == AppCompatDelegate.FEATURE_SUPPORT_ACTION_BAR && menu != null){
//overflow menu clicked, put code here...
// As you are using navigation component
Fragment navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host);
//MyFragment
Fragment fragment=navHostFragment.getChildFragmentManager().getFragments().get(0);
if(fragment instanceof OnMenuOpenListener){
((OnMenuOpenListener)fragment).onMenuOpened()
return false;
}
}
return super.onMenuOpened(featureId, menu);
}
}
As Support Action Bar is attached to Activity All the event's are captured by Activity all you need to do is get the Fragment which need's the event and trigger the call using a call back.If you return false onMenuOpened will not open the overflow menu and will trigger bottom sheet menu from your fragment.
P.S- I have not written the code in Editor so might have some error's but you must have got the idea.
Reference:
https://stackoverflow.com/a/51732378/7972699
As discussed here opening bottom sheet when clicked on the overflow menu is bad UX.
Why?
Quoting from the post
Because user have to reach the top of the screen to click the oveflow
menu, then go back to the bottom to click desired action which is on
the bottom sheet.
-
According to Fitt's Law - The time to acquire a target is a function
of the distance to and size of the target. I agree that I think
distance between the menu and the bottom sheet is substantial. This
solution allows placing a lot options in one place.
-
it also doesn't match the user expectation since people are used to
the overflow menu opening in a different manner.
If you have a top action bar, use usual context menu. If you have a bottom action bar you may use bottom sheet.
**You can try the following steps to open bottom sheet dialog:**
1. Just make a function inside Activity where the fragment is replace
public Fragment getCurrentFragment() {
return getSupportFragmentManager().findFragmentById(R.id.frameContainer);
}
Fragment fragment = getCurrentFragment();
if (fragment != null) {
if (fragment instanceof RequiredFragment) {
RequiredFragment.openBottumSheetDialog();
}
}
2. In Side RequiredFragment get your function from activity:
private BottomSheetDialog mBottomSheetDialogFragment;
private void showBottomSheetFilter() {
if (mBottomSheetDialogFragment == null) {
mBottomSheetDialogFragment = mBottomSheetDialogFragment.newInstance(feedSection);
mBottomSheetDialogFragment.setCallBackListener(new OnFeedsTypeSelectedListener() {
#Override
public void onFeedsTypeSelected(int contentType) {
filterByContentTypeId(contentType);
}
}
mBottomSheetDialogFragment.show(getChildFragmentManager(),
mBottomSheetDialogFragment.getTag());
}
3. Create a BottomSheetDialog Dialog fragment.
public class BottomSheetDialog extends BottomSheetDialogFragment {
private String[] feedsFilter;
private ListView listView;
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
feedsFilter = getResources().getStringArray(R.array.ideas_filter);
}
#Override
public void setupDialog(final Dialog dialog, int style) {
super.setupDialog(dialog, style);
View contentView = View.inflate(getContext(), R.layout.dialog_idea_filter_bottom_sheet, null);
dialog.setContentView(contentView);
listView = (ListView) contentView.findViewById(R.id.listView);
ArrayAdapter < String > adapter = new ArrayAdapter < String > (getActivity(),
android.R.layout.simple_list_item_1, feedsFilter);
listView.setAdapter(adapter);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView < ? > parent, View view,
int position, long id) {
if (onFeedsTypeSelected != null) {
onIdeaTypeSelectedListenonFeedsTypeSelecteder.onFeedsTypeSelected(feedsFilter[position]);
}
dismiss();
}
});
}
public void setCallBackListener(onFeedsTypeSelected SelectedListener onFeedsTypeSelected) {
this.onIdeaTypeSelectedLionFeedsTypeSelectedstener = onFeedsTypeSelected;
}
}
mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
Data data = mListDatas.get(i);
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
ThirdFragment thirdFragment = new ThirdFragment();
Bundle bundle = new Bundle();
bundle.putSerializable("detail", data);
thirdFragment.setArguments(bundle);
transaction.replace(R.id.content,ThirdFragment).addToBackStack(null).commit();
BottomNavigationView navigation = (BottomNavigationView)view.findViewById(R.id.navigation_bottomview);
navigation.setSelectedItemId(R.id.navigation_third_tab);
}
});
In the above code i need to open a new fragment on the bottom tab from a list item click,
i am able to open a new fragment but the current tab remains unchanged i.e, after redirecting to the third fragment on third bottom tab ,the current tab i.e, Second Tab is active.
How can i change the active state of bottom tab from second tab to third tab on the list item click ??
Your activity needs to implement the BottomNavigationView.OnNavigationItemSelectedListener. This can be done simply by adding implements BottomNavigationView.OnNavigationItemSelectedListener after your class name, this is done so you can get the click callback whenever one of the bottom menu items are clicked on.
Then you need to override the onNavigationItemSelected function so add the following code to your activity.
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.tab_1:
handleFragmentChange(Fragment_1.newInstance());
return true;
}
return false;
}
Also add navigation.setOnNavigationItemSelectedListener(this); in the same activity once you have found the view using findViewById. This is done so that the system knows that your class is handling the event when a bottom menu is clicked so it calls the above function in your class.
The return true is what notifies the android system that it needs to switch the tab.
if this code locates in fragment then you need to replace
BottomNavigationView navigation = view.findViewById(R.id.navigation_bottomview);
navigation.setSelectedItemId(R.id.navigation_third_tab);
before
...commit();
and add getRootView() because your BottomNavigationView locate inside main container - root activity
BottomNavigationView navigation = view.getRootView().findViewById(R.id.navigation_bottomview);
navigation.setSelectedItemId(R.id.navigation_third_tab);
In my ActionBarActivity I use a ViewPager for different Fragments. I use a Spinner in the SupportActionBar to populate the Fragments in ViewPager.
If I write this code in just one Fragment the application works as intended. However if I add the same method in the second Fragment as well, the OnItemSelectedListener from the first Fragment is not called any more.
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
Spinner spinner = (Spinner) ((ActionBarActivity) getActivity()).getSupportActionBar().getCustomView().findViewById(R.id.actionbar_spinner_names);
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
onContentChanged(position);
}
Is it possible to share the setOnItemSelectedListener of an MenuItem amonst different Fragments? Or is there a better way to share the selection of the ActionBarSpinner amongst the Fragments of my Application?
Edit:
I have a TabLayout with two Fragments.
The Spinner is used for a query to filter the results in both Fragments.
I want to update both Fragments as soon as the user makes a selection. However I can't register in OnItemSelectedListener for both Fragments. Hope this makes it a little clearer. Sadly I can't upload pictures yet.
You can only set one Listener to the Spinner. You can achieve what you need if the Activity that hosts your fragments has access to them.
You could create a nested class in your Activity that hosts the ViewPager and thus the Fragments. Attach the listener to Spinner with a custom constructor that takes the Fragments as parameters
class MyListener implements AdapterView.OnItemSelectedListener
{
Fragment frag1;
Fragment frag2;
public MyListener(Fragment f1, Fragment f2)
{
frag1 = f1;
frag2 = f2;
}
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
if (f1 != null)
{
}
....
}
}
Then in your activity create the listener with
Spinner spinner = (Spinner) ((ActionBarActivity) getActivity()).getSupportActionBar().getCustomView().findViewById(R.id.actionbar_spinner_names);
spinner.setOnItemSelectedListener(new MyListener(fragment1, fragment2));
How well this will work and how easy it will be to create the listener will depend on your implementation of the TabLayout.
In navigation drawer i have a child fragment with a list view. By clicking the drawer item first time, the list view is displayed but when closing and reopening the drawer by clicking on the same drawer item, the list view in child fragment is updated i.e. new list item is added in the list view of child fragment with the previous values of list view ..please help..i am stuck
int select=0;
select=position-2;
String sendto =list.get(position-2).getIMEI();
String sendtoname=list.get(position-2).getName();
System.out.println(deviceIMEI+"--"+sendto+sendtoname);
Fragment fragment = new ChatFrag();
Bundle args1 = new Bundle();
args1.putString(ChatFrag.MYIEMI, deviceIMEI);
args1.putString(ChatFrag.SENDERIEMI, sendto);
args1.putString(ChatFrag.SENDERNAME,sendtoname);
args1.putInt(ChatFrag.SELECT, select);
fragment.setArguments(args1);
fm.beginTransaction()
.replace(R.id.content_frame, fragment)
.commit();
this is the code..please help how to remember the index..
Reason is that the Drawer ListView onItemClick will be called when ever an item in the List is clicked. This is the behavior of ListView.
One way to fix this is to keep remembering the last selected index and avoid adding the Fragment again on next time if the selected index is same as last selected.
Another way is to check the FragmentManager if the Fragment is already added
List<Fragment> fragmentList = getSupportFragmentManager().getFragments();
if(fragmentList != null) {
for (Fragment fragment : fragmentList) {
if (fragment != null && fragment instanceof CustomFragment) {
// don't add the fragment again
}else{
// add the fragment here
}
}
I'm working on an app that uses a navigation drawer, with each menu items intended to launch a particular fragment. And each fragment must be in a list view form. Is there a way to launch the listfragments as normal fragment can be launched?
For instance:
new MyListFragment();
Is there a similar way to launch listfragments from the main activity?
Fragments are never "launched," they are added to Activities. Either add the fragment to the current activity using a FragmentTransaction (perhaps by replacing the content area), or start another activity with that fragment in it.
private void goToRadar(FragmentManager fm){
FragmentTransaction ft = fm.beginTransaction();
ft.replace(R.id.flMain, new ChildRadarFragment());
ft.addToBackStack("radar");
ft.commit();
}
You must have a FrameLayout inside the xml-layout of your Activity (here the id = flMain).
You should do the FragmentTransactions when a menu listitem is clicked (=drawer ListView).
mDrawerList.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> arg0, View v, int index,
long arg3) {
// Here the FragmentTransaction
}
});