Right now I have an activity using a viewpager to display a several fragments. each of those fragments has implemented a Context Action Bar in the fragment. I am trying to make it where if the user scrolls or tabs between the fragments, the ActionBar for the previously selected fragment is dismissed.
I think I need to make some sort of call to the CAB .finish()in the fragment from my OnPageChangeListener() however I am not entirely sure how or where to do that.
Create should a public function in each fragment which can be called to finish the ActionMode. Could look like this
public void finishActionMode(){
[YOUR_ACTIONMODE_VARIABLE].finish();
}
Then in the activity you keep your ViewPager you should somehow keep or get a reference to the fragment in the ViewPager which should close its ActionMode, and then in your onPageSelected you can call `[YOUR_FRAGMENT_REFERENCE].finishActionMode();
Like this:
mPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageSelected(int position) {
[YOUR_FRAGMENT_REFERENCE].finishActionMode();
}
...
});
For keeping a reference to a ViewPagers fragments look here: Retrieve a Fragment from a ViewPager
You don't have to create individual Contextual Action Bar(CAB) for every fragments. You can simply create a single STATIC CAB in the TabbedActivity(Activity with pager) and use them anytime when needed.
Here is my code snippet of TabbedActivity.java
public class TabbedActivity extends AppCompatActivity {
public static ActionMode uniActionMode=null;
private ViewPager mViewPager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int
positionOffsetPixels) {
if(uniActionMode!=null)
uniActionMode.finish();
}
#Override
public void onPageSelected(int position)
{
}
#Override
public void onPageScrollStateChanged(int state) {
}
});
}
The mViewPager.addOnPageChangeListener is used to check if the user moves through the fragments. Now all you have to do is create an ActionMode from anywhere and then pass the value to the static uniActionMode variable.
mActionModeCallback = new ActionMode.Callback() {
#Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
// Inflate a menu resource providing context menu items
MenuInflater inflater = mode.getMenuInflater();
inflater.inflate(R.menu.menu_selection, menu);
return true;
}
#Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return false;
}
#Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
switch (item.getItemId()) {
case R.id.action_delete:
mode.finish();
return true;
case R.id.action_mark_read:
mode.finish();
return true;
}
return false;
}
#Override
public void onDestroyActionMode(ActionMode mode) {
}
};
TabbedActivity.uniActionMode= v.startActionMode(mActionModeCallback);
Here the TabbedActivity.uniActionMode references the ActionMode Object of the Tabbed Activity. Thus the callbacks are passed to the TabbedActivity.uniActionMode.
Related
My Fragment class:
Toolbar toolbar;
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
toolbar = getView().findViewById(R.id.toolbar3);
toolbar.inflateMenu(R.menu.menufragmentmain);
setHasOptionsMenu(true); //i also tried putting this function in oncreate function
}
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
Toast.makeText(getActivity(), "i never enter this function also" , Toast.LENGTH_LONG).show();
super.onCreateOptionsMenu(menu,inflater);
inflater.inflate(R.menu.menufragmentmain, menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
Toast.makeText(getActivity(), "i never enter this function" , Toast.LENGTH_LONG).show();
//some switch cases.....
return super.onOptionsItemSelected(item);
}
I've got a stupid error which I cant find unfourtantly. I've got a simple toolbar for my fragment. In onViewCreated I inflate my actionbar menu with my toolbar.
The issue is that the functions onCreateOptionsMenu and 'onOptionsItemSelected' are never called. I've got no clue why.
Things i checked in other similar questions with the same issue:
I checked if my main activity has a onCreateOptionsMenu or'onOptionsItemSelected'. It doesn*t
Checked if my style class has NOT: android:theme="#android:style/Theme.Black.NoTitleBar
None of the points unfourtantly work. What did I miss. Do I need to check something else?
As per the Fragment-owned app bar guide which explains how to use a Fragment-owned Toolbar, you do not use any of the setHasOptionsMenu(), onCreateOptionsMenu(), or onOptionsItemSelected() APIs - those are only used for activity owned app bars.
Instead, you would follow the guide for handling menu click events by using the Toolbar APIs:
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
toolbar = getView().findViewById(R.id.toolbar3);
toolbar.inflateMenu(R.menu.menufragmentmain);
toolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() {
#Override
public boolean onMenuItemClick(MenuItem item) {
Toast.makeText(getActivity(), "onMenuItemClick called" , Toast.LENGTH_LONG).show();
//some switch cases.....
return true;
}
});
}
Set up the toolbar in the host Activity. Then override the menu handling in each Fragment wherever necessary.
Here's some generic scaffolding (with comments):
public class MyFragment extends Fragment {
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Report that this Fragment has an options menu
setHasOptionsMenu(true);
}
#Override
public void onCreateOptionsMenu(#NonNull Menu menu, #NonNull MenuInflater inflater) {
// Inflate then menu, and THEN call super()
// Note the order of the invocations
inflater.inflate(R.menu.my_menu, menu);
super.onCreateOptionsMenu(menu, inflater);
}
#Override
public boolean onOptionsItemSelected(#NonNull MenuItem item) {
// Handle menu item selections
final int itemId = item.getItemId();
switch(itemId) {
case R.id.my_menu_option_x: ...; return true;
case R.id.my_menu_option_y: ...; return true;
case R.id.my_menu_option_z: ...; return true;
default: return super.onOptionsItemSelected(item);
}
}
}
I am using a listview in my application where have implemented setChoiceMode with ListView.CHOICE_MODE_MULTIPLE_MODAL. When long press on the listview, items can be selected but after that, if the screen orientation is changed the action bar goes annoyingly.
My code looks like this
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
adapter = new ItemListAdapter();
listView.setAdapter(adapter);
listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
listView.setMultiChoiceModeListener(new AbsListView.MultiChoiceModeListener() {
#Override
public void onItemCheckedStateChanged(ActionMode actionMode, int position, long l, boolean b) {
final int checkedItemCount = listView.getCheckedItemCount();
actionMode.setTitle(checkedItemCount + " Selected");
/*get the selected items*/
}
#Override
public boolean onCreateActionMode(ActionMode actionMode, Menu menu) {
actionMode.getMenuInflater().inflate(R.menu.menu_listview_delete, menu);
return true;
}
#Override
public boolean onPrepareActionMode(ActionMode actionMode, Menu menu) {
return false;
}
#Override
public boolean onActionItemClicked(final ActionMode actionMode, MenuItem menuItem) {
switch (menuItem.getItemId()){
case R.id.delete_item:
/*delete selected items*/
return true;
default:
return false;
}
}
#Override
public void onDestroyActionMode(ActionMode actionMode) {
adapter.removeSelection();
}
});
}
Image in portrait mode & landscape mode ,It shows selected items as 0
& when trying to delete the respective selected items will not get deleted.then if back button is pressed (on landscape mode which is implemented in my application activity) the action bar color will turn to white.
(Same happens when trying in other way select items in landscape mode and rotate to delete the same in portrait)
One solution is using below in the manifest
android:configChanges="orientation|screenSize"
If this is used the activity is not recreated on orientation change.As it has different layouts for portrait and landscape, activity recreation is must on orientation change.
I know the issue is because of the activity is recreated on orientation change that makes setMultiChoiceModeListener to called every time.
can someone help to tackle the issue? Thanks!
Use these methods to store and restore the values of your selected items.
#Override
public void onSaveInstanceState(Bundle outState) {
outState.putInt("count",checkedItemCount);
super.onSaveInstanceState(outState);
}
then restore it onCreate method
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
//will return value of selecteditems
int countedItems = savedInstanceState.getInt("count");
}
}
Here's a link for more information:
How to use onSavedInstanceState example please
I have an ActionBarActivity that has two tabs. I have implemented a ActionMode.Callback in tab1(fragment1). Now i want to implement it in tab2(fragment2) as well. The problem is that the tab1 Contextual action bar is active even after i swipe to tab2. This means a CAB is meant to be implemented for an activity, not for specific fragment.
So what is the approach to take to have different CABs in two fragments hosted in the same activity?
In the onCreateActionMode callback save a reference of the ActionMode in your fragment, and then in your fragment's onPause you can finish your action. Example:
protected class MultiChoiceModeListener implements AbsListView.MultiChoiceModeListener {
#Override
public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) {
}
#Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
ConversationFragment.this.actionMode = mode;
MenuInflater menuInflater = getActivity().getMenuInflater();
menuInflater.inflate(R.menu.menu_delete_conversation, menu);
return true;
}
#Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return false;
}
#Override
public boolean onActionItemClicked(final ActionMode mode, MenuItem item) {
return false;
}
#Override
public void onDestroyActionMode(ActionMode mode) {
ConversationFragment.this.actionMode = null;
}
}
#Override
public void onPause() {
super.onPause();
if (actionMode != null) {
actionMode.finish();
}
}
I'm using a viewpager to swipe between fragments and would like the back button to navigate to the previously viewed fragment rather than ending the activity. Sorry if this is a duplicate of this question however I didn't find the answer very helpful. Obviously onBackPressed needs to be overridden, but I don't know how to get and display the correct fragment. I assume that I should use the fragmentmanager's backstack, but getSupportFragmentManager().getBackStackEntryCount() always returns 0. Do I need to manually add fragments to the backstack using FragmentTransaction.addToBackStack()? If so, where would I add this in my adapter?
Here is the code for my activity,
public class PagerActivity extends FragmentActivity {
ArrayList<Sale> sales;
MyAdapter mAdapter;
ViewPager mPager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent it = getIntent();
this.sales = (ArrayList<Sale>) it.getExtras().get("sales");
int position = it.getExtras().getInt("position");
ActionBar actionBar = getActionBar();
actionBar.setDisplayHomeAsUpEnabled(true);
setContentView(R.layout.fragment_pager);
mAdapter = new MyAdapter(getSupportFragmentManager());
mPager = (ViewPager) findViewById(R.id.pager);
mPager.setAdapter(mAdapter);
mPager.setCurrentItem(position);
}
public class MyAdapter extends FragmentPagerAdapter {
public MyAdapter(FragmentManager fragmentManager) {
super(fragmentManager);
}
#Override
public int getCount() {
return sales.size();
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
super.destroyItem(container, position, object);
}
#Override
public Fragment getItem(int position) {
SalesThumbFragment frag = new SalesThumbFragment();
return frag.newInstance(sales.get(position));
}
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
finish();
return true;
}
return super.onOptionsItemSelected(item);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.sales_controller, menu);
return true;
}
#Override
public void onBackPressed() {
if(getSupportFragmentManager().getBackStackEntryCount() != 0) {
getSupportFragmentManager().popBackStack();
} else {
super.onBackPressed();
}
}
}
In new design support library, i use this
I have same issue and i follow this step
In the main activity where there are 3 fragment in viewpager i create stack
and push and pop data.
//private Stack<Integer> stackkk; ==> As i get edit suggestion
private Stack<Integer> stackkk = new Stack<>(); // Edited
private ViewPager mPager;
private int tabPosition = 0;
mTabLayout.setupWithViewPager(mPager);
mPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(mTabLayout));
mTabLayout.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
#Override
public void onTabSelected(TabLayout.Tab tab) {
tabPosition = tab.getPosition();
mPager.setCurrentItem(tab.getPosition());
if (stackkk.empty())
stackkk.push(0);
if (stackkk.contains(tabPosition)) {
stackkk.remove(stackkk.indexOf(tabPosition));
stackkk.push(tabPosition);
} else {
stackkk.push(tabPosition);
}
}
#Override
public void onTabUnselected(TabLayout.Tab tab) {
tabPositionUnselected = tab.getPosition();
}
#Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
}
and in the onBackPressed in activity,
#Override
public void onBackPressed() {
if (stackkk.size() > 1) {
stackkk.pop();
mPager.setCurrentItem(stackkk.lastElement());
} else {
}
}
Use this code in Fragment Activity class. Don't forget to add return true;
public boolean onKeyDown(int keyCode, KeyEvent event) {
// TODO Auto-generated method stub
if ((keyCode == KeyEvent.KEYCODE_BACK)) {
mViewPager.setCurrentItem(viewPageSelected - 1);
return true;
}
return super.onKeyDown(keyCode, event);
}
I had a similar problem, this is how I solved it. I think you can adapt the code to your problem, if I understood what you problem is. I had a ViewPager with 6 fragments and wanted to keep track of the page history and to be able to use the back button to navigate backwards in the history. I create a java.util.Stack<Integer> object, add fragment numbers to it (except when you use the back button, see below), and override onBackPressed() to make it pop the last viewed fragment instead of using the back stack, when my history stack is not empty.
You want to avoid pushing elements on the Stack when you press the back button, otherwise you will get stuck between two fragments if you keep using the back button, instead of eventually exiting.
My code:
MyAdapter mAdapter;
ViewPager mPager;
Stack<Integer> pageHistory;
int currentPage;
boolean saveToHistory;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mAdapter = new MyAdapter(getSupportFragmentManager());
mPager = (ViewPager)findViewById(R.id.container);
mPager.setAdapter(mAdapter);
mPager.setOffscreenPageLimit(5);
pageHistory = new Stack<Integer>();
mPager.setOnPageChangeListener(new OnPageChangeListener() {
#Override
public void onPageSelected(int arg0) {
if(saveToHistory)
pageHistory.push(Integer.valueOf(currentPage));
}
#Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
}
#Override
public void onPageScrollStateChanged(int arg0) {
}
});
saveToHistory = true;
}
#Override
public void onBackPressed() {
if(pageHistory.empty())
super.onBackPressed();
else {
saveToHistory = false;
mPager.setCurrentItem(pageHistory.pop().intValue());
saveToHistory = true;
}
};
If you use a field to keep track of the index of the previous page using mPager.getCurrentItem() after each time the user navigates to a new fragment, then in the onBackPressed() method, you should be able to call mPager.setCurrentItem(previousPage)
Or, if the user can only page in order, then you don't need a field at all, and you could just do mPager.setCurrentItem(mPager.getCurrentItem()-1)
I've made custom ViewPager and implement stack functionality in it.
public class CustomViewPager extends ViewPager {
private Stack<Integer> stack = new Stack<>();
#Override
public void setCurrentItem(int item, boolean smoothScroll) {
stack.push(getCurrentItem());
super.setCurrentItem(item, smoothScroll);
}
public int popFromBackStack(boolean smoothScroll) {
if (stack.size()>0) {
super.setCurrentItem(stack.pop(), smoothScroll);
return getCurrentItem();
} else return -1;
}
I am currently working on android project and making use of fragments and ListViews/ListFragments. I have contextual action bars working on standard activities such as a ListActivity.
Now I am trying to do the same sort of thing but on a fragment layout. I have a MainActivity which extends Activity which inflates the XML for the layout that contains the 2 fragments, fragment A and fragment B.
Fragment A extends ListFragment and contains a ListView which is populated from data within an SQLite Database. When I have got a contextual action bar working on a standard ListActivity I have a class that Extends ListView.MultiChoiceModeListener but this isn't available for a ListFragment class or a standard activity so how would I go about implementing this.
The basic thing I want to achieve is when someone long presses the item within a ListView within FragmentA which extends ListFragment, the action bar contextually changes and the user can then select multiple items from within the ListView.
Thanks for any help you can provide.
When I have got a contextual action bar working on a standard
ListActivity I have a class that Extends
ListView.MultiChoiceModeListener but this isn't available for a
ListFragment class or a standard activity so how would I go about
implementing this.
I don't see how MultiChoiceModeListener isn't available (maybe I didn't understand what you try to do). From your comment I assumed you use the fragments from the compatibility package.
Below is an example with a FragmentActivity with two static fragments and each of those fragments triggers the contextual action bar with their own menus options.
The FragmentActivity is very simple, it just holds the two fragments below:
// the list fragment
public class ListCABFragment extends ListFragment {
private String[] mCountries = { "Romania", "Germany", "England", "USA",
"Japan", "France" };
private static final boolean POST_HONEYCOMB = Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB;
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (POST_HONEYCOMB) {
// make sure we are on a version above Honeycomb otherwise will
// access things that aren't available
postHoneycombCAB();
} else {
// probably do nothing and implement the normal context menu?!?
}
setListAdapter(new ArrayAdapter<String>(getActivity(),
android.R.layout.simple_list_item_1, mCountries));
}
#SuppressLint({ "NewApi", "NewApi" })
private void postHoneycombCAB() {
getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
getListView().setOnItemLongClickListener(new OnItemLongClickListener() {
#Override
public boolean onItemLongClick(AdapterView<?> parent, View view,
int position, long id) {
((ListView) parent).setItemChecked(position,
((ListView) parent).isItemChecked(position));
return false;
}
});
getListView().setMultiChoiceModeListener(new MultiChoiceModeListener() {
private int nr = 0;
#Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
getActivity().getMenuInflater().inflate(R.menu.listcab_menu,
menu);
return true;
}
#Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return false;
}
#Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
switch (item.getItemId()) {
case R.id.item1:
Toast.makeText(getActivity(), "Option1 clicked",
Toast.LENGTH_SHORT).show();
break;
case R.id.item2:
Toast.makeText(getActivity(), "Option2 clicked",
Toast.LENGTH_SHORT).show();
break;
}
return false;
}
#Override
public void onDestroyActionMode(ActionMode mode) {
nr = 0;
}
#Override
public void onItemCheckedStateChanged(ActionMode mode,
int position, long id, boolean checked) {
if (checked) {
nr++;
} else {
nr--;
}
mode.setTitle(nr + " rows selected!");
}
});
}
}
and the other fragment for a Fragment which has a layout composed from a RadioGroup which triggers the CAB when a RadioButton is selected:
public class SimpleCABFragment extends Fragment implements Callback {
private static final boolean POST_HONEYCOMB = Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.frag_simplecabfragment, container,
false);
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
RadioGroup rg = (RadioGroup) getView().findViewById(R.id.radioGroup1);
rg.setOnCheckedChangeListener(new OnCheckedChangeListener() {
#Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
if (POST_HONEYCOMB) {
// this could be improved so we don't need to create the
// option
// menu if it is already available
getActivity().startActionMode(SimpleCABFragment.this);
} else {
// something else
}
}
});
}
#Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
if (item.getItemId() == R.id.itemradio) {
Toast.makeText(getActivity(), "CAB for Radiogroup!",
Toast.LENGTH_SHORT).show();
}
return false;
}
#Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
getActivity().getMenuInflater().inflate(R.menu.simplecab_menu, menu);
return true;
}
#Override
public void onDestroyActionMode(ActionMode mode) {
}
#Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return false;
}
}
See if this is what you're looking for (you can find a full sample including the layouts and menus files in my github project).