I've problem when using ListView in Fragment. When i using MultiChoiceModelListener, it will display ActionMode when i Long click on the item in ListView.
The problem is :
When ActionMode is activated, and user replace that fragment with other fragment the actionmode still there, take a look the picture below.
When I use ActionMode mode = null and instantiate that mode variable onCreateActionMode plus implement it in onDestroyActionMode and onDestroyView, please take a look the snippet code. The Problem number one is solved, but it's open another problem, because mode.finish() is called in onDestroyView when fragment orientation change my checked item in ListView disappear.
When I delete onDestroyView() method, I solved the problem number two, but Problem number one is open again.
This is my snippet code :
//My ActionModeListener
private class MyActionMode implements AbsListView.MultiChoiceModeListener {
#Override
public void onItemCheckedStateChanged(ActionMode actionMode, int i, long l, boolean b) {
String nfile = ((DataFileProvider) adapter.getItem(i)).getNfile();
if (b) {
selections.add(nfile);
} else {
selections.remove(nfile);
}
actionMode.setTitle(selections.size() + " " + getString(R.string.item_selected));
}
#Override
public boolean onCreateActionMode(ActionMode actionMode, Menu menu) {
mode = actionMode;
getActivity().getMenuInflater().inflate(R.menu.list_file_action_mode, menu);
return true;
}
#Override
public boolean onPrepareActionMode(ActionMode actionMode, Menu menu) {
actionMode.setTitle(selections.size() + " " + getString(R.string.item_selected));
return true;
}
#Override
public boolean onActionItemClicked(ActionMode actionMode, MenuItem menuItem) {
return true;
}
#Override
public void onDestroyActionMode(ActionMode actionMode) {
selections.clear();
mode = null;
showMessage("ListDocFileFragment : onDestroyActionMode");
}
}
This is my snippet code when handle fragment livecycle
//My OnResume,OnPause,and OnDestroyView in Fragment
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
default_location = getArguments().getString("default_location");
}
if (getActivity().getIntent().hasExtra("ActionMode")) {
selections = getActivity().getIntent().getStringArrayListExtra("ActionMode");
}
}
#Override
public void onResume() {
super.onResume();
if(selections.isEmpty() && list_doc_file.getCheckedItemCount() > 0){
for (int i = 0; i < adapter.getCount(); i++) {
list_doc_file.setItemChecked(i, false);
}
selections.clear();
}
showMessage("ListDocFileFragment : onResume");
}
#Override
public void onPause() {
if (selections != null) {
getActivity().getIntent().putExtra("ActionMode", selections);
}
showMessage("ListDocFileFragment : onPause");
super.onPause();
}
#Override
public void onDestroyView() {
if(mode != null){
mode.finish();
}
showMessage("ListDocFileFragment : onDestroyView");
super.onDestroyView();
}
My Question is how to solved three problem above, without open another problem like i did ?
I've searching for another solution in this stack, but i got nothing. When i implement their solution it's always open another problem. Any suggestion ?
Related
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 a ListView and a Toolbar above of it in my Activity. I want to replace the Toolbar default icons (search and settings) with delete and edit icons when clicking an item of the ListView to perform some action.
So what I've understood from your question, you're looking for ActionMode which might serve your purpose.
So here's an implementation guideline.
Declare an ActionMode in your Activity and let your Activity to implement ActionMode.Callback.
public class YourActivity extends AppCompatActivity implements ActionMode.Callback {
// Declare ActionMode here
private ActionMode actionMode;
// Now implement the callback functions for ActionMode
#Override
public boolean onPrepareActionMode(ActionMode actionMode, Menu menu) {
return false;
}
#Override
public boolean onCreateActionMode(final ActionMode actionMode, Menu menu) {
MenuInflater inflater = actionMode.getMenuInflater();
// Inflate your menu here
inflater.inflate(R.menu.list_item_click_menu, menu);
return true;
}
#Override
public boolean onActionItemClicked(ActionMode actionMode, MenuItem menuItem) {
switch (menuItem.getItemId()) {
case R.id.action_delete:
// Do something
actionMode.finish();
return true;
case R.id.action_edit:
// Do something
actionMode.finish();
return true;
default:
return false;
}
}
#Override
public void onDestroyActionMode(ActionMode actionMode) {
try {
this.actionMode = null;
// Do something. Reset the views maybe?
} catch (Exception e) {
e.printStackTrace();
}
}
}
Now to initiate your ActionMode you need to have this in your onClick function of the list item.
listItem.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (actionMode != null) {
return true;
}
// Show ActionMode
actionMode = startSupportActionMode(this);
actionMode.invalidate();
}
});
You may need to reset the ActionMode sometimes.
#Override
public void onResume() {
super.onResume();
if (actionMode != null) {
actionMode.finish();
actionMode = null;
}
}
i implemented a contextual action mode bar in a nested fragement. This fragment is part of a view pager and the view pager is also a fragment and part of a navigation drawer.
My Problem: I want to close the contextual action mode bar if the fragment is no more focused. So, if I swipe through the view pager the action mode bar should close. But if I use the onPause() method of the nested fragment, the method is not called directly. Often it waits until i swiped two or three times forward... Here are some pictures:
In the second picture you can see that the action mode bar is still there. So my question is:
In which method should I call my actionModeBar.finish() method, to close directly the action mode bar if i leave the fragment?
Maybe the code of the fragment helps you:
public class EditorFragment extends Fragment {
private static final String KEY_POSITION="position";
ListView listView;
private boolean isMultipleList = false;
private ActionMode acMode;
private int counterChecked = 0;
private ActionMode.Callback modeCallBack = new ActionMode.Callback() {
public boolean onPrepareActionMode(ActionMode mode, Menu menu){
return false;
}
public void onDestroyActionMode(ActionMode mode) {
listView.clearChoices();
for (int i = 0; i < listView.getChildCount(); i++)
listView.setItemChecked(i, false);
listView.post(new Runnable() {
#Override
public void run() {
listView.setChoiceMode(ListView.CHOICE_MODE_NONE);
}
});
isMultipleList = false;
counterChecked = 0;
mode = null;
}
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
mode.setTitle("1 Aufgabe");
mode.getMenuInflater().inflate(R.menu.actionmode, menu);
return true;
}
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
switch (item.getItemId()) {
case R.id.actionmode_delete:
int choiceCount = listView.getCount();
SparseBooleanArray spBoolArray = listView.getCheckedItemPositions();
DBAufgaben db = new DBAufgaben(MainActivity.getMContext());
db.open();
for (int i = 0; i < choiceCount; i++) {
if(spBoolArray.get(i)){
db.deletContact(listView.getItemIdAtPosition(i));
}
}
Cursor cursor = db.getAllRecords();
AdapterEingang adapterE = new AdapterEingang(MainActivity.getMContext(), cursor, 0);
listView.setAdapter(adapterE);
db.close();
mode.finish();
break;
case R.id.actionmode_cancel:
mode.finish();
break;
}
return false;
}
};
//......//
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = null;
int position = getArguments().getInt(KEY_POSITION, -1);
switch(position){
case 0:
rootView = inflater.inflate(R.layout.pager_list, null);
listView = (ListView) rootView.findViewById(R.id.pager_list);
Context context = MainActivity.getMContext();
DBAufgaben db = new DBAufgaben(context);
db.open();
Cursor cursor = db.getAllRecords();
AdapterEingang adapterE = new AdapterEingang(context, cursor, 0);
listView.setAdapter(adapterE);
db.close();
listView.setOnItemLongClickListener(new OnItemLongClickListener(){
#Override
public boolean onItemLongClick(AdapterView<?> adapterView, View view,
int position, long id) {
if(!isMultipleList){
acMode = MainActivity.getInstance().startActionMode(modeCallBack);
listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
listView.setItemChecked(position, true);
isMultipleList = true;
counterChecked++;
setNewTitle();
} else {
listView.setItemChecked(position, true);
counterChecked++;
setNewTitle();
}
return true;
}
});
listView.setOnItemClickListener(new OnItemClickListener(){
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int position,
long id) {
Log.d(getTag(), "Datensatz: "+String.valueOf(id));
if(isMultipleList){
if(listView.isItemChecked(position)){
listView.setItemChecked(position, true);
counterChecked++;
setNewTitle();
} else {
listView.setItemChecked(position, false);
counterChecked--;
setNewTitle();
}
}
}
});
break;
default:
rootView = inflater.inflate(R.layout.frag_dummy, null);
TextView txt = (TextView) rootView.findViewById(R.id.dummy_txt);
txt.setText(String.valueOf(position));
break;
}
return(rootView);
}
public void setNewTitle(){
if(counterChecked == 1){
acMode.setTitle(counterChecked+" Aufgabe");
} else {
acMode.setTitle(counterChecked+" Aufgaben");
}
}
#Override
public void onPause(){
super.onPause();
if(isMultipleList){
acMode.finish();
}
}
}
ViewPagers keep multiple pages active at any one time (by default, the page before and page after the currently shown page), hence why onPause() is not called until you swipe two pages away.
Your best bet would be to use a ViewPager.OnPageChangeListener, and show and hide the ActionMode in onPageSelected(..) (i.e. if the page selected isn't the one with the ActionMode, hide the ActionMode). You'll likely have to implement this in the Activity which hosts your ViewPager.
Here's what worked for me --
Hold a static reference to the action mode in MyFragment:
public static ActionMode mActionMode;
#Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
mActionMode = mode;
return true;
}
#Override
public void onDestroyActionMode(ActionMode mode) {
mActionMode = null;
}
Set ViewPager.OnPageChangeListener in MyViewPagerActivity.
mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
....
#Override
public void onPageScrollStateChanged(int state) {
if(MyFragment.mActionMode != null) MyFragment.mActionMode.finish();
}
});
This worked for me:
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
if (!isVisibleToUser && this.listView != null) {
//HACK this is done in order to finish the contextual action bar
int choiceMode = this.listView.getChoiceMode();
this.listView.setChoiceMode(choiceMode);
}
}
I combined code from the OP with the override suggested by user pomber:
I save the ActionMode to a class field when the action mode is created.
I set the field back to null when the action mode is destroyed.
I override setUserVisibleHint in my fragment and call ActionMode#finish() if the fragment isn't visible in the view pager.
I also call ActionMode#finish() in the onPause() method of the fragment to close the fragment if the user navigates elsewhere.
Code:
#Nullable
ActionMode mActionMode = null;
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
// Start out with a progress indicator.
setListShownNoAnimation(false);
setEmptyText(getText(R.string.no_forms_in_progress));
getListView().setChoiceMode(AbsListView.CHOICE_MODE_MULTIPLE_MODAL);
getListView().setMultiChoiceModeListener(new AbsListView.MultiChoiceModeListener() {
#Override
public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) {
// Here you can do something when items are selected/de-selected,
// such as update the title in the CAB
}
#Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
MenuInflater inflater = mode.getMenuInflater();
inflater.inflate(R.menu.context_saved_item, menu);
inflater.inflate(R.menu.context_instance_list, menu);
mActionMode = mode;
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:
confirmDelete(getListView().getCheckedItemIds());
return true;
case R.id.action_print:
print(getListView().getCheckedItemIds());
return true;
}
return false;
}
#Override
public void onDestroyActionMode(ActionMode mode) {
mActionMode = null;
}
});
}
#Override
public void onPause() {
super.onPause();
if (mActionMode != null) {
mActionMode.finish();
}
}
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (mActionMode != null && !isVisibleToUser) {
mActionMode.finish();
}
}
You can use the onDestroyOptionsMenu() method inside your Fragment:
public void onDestroyOptionsMenu() {
super.onDestroyOptionsMenu();
if (mActionMode != null) {
mActionMode.finish();
mActionMode = null;
}
}
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).
I got a Fragment B in a Fragment A in a Activity. Works as expected.
When clicking an item in Fragment B, I want to display a contextual menu bar.
I am working with ActionbarSherlock.
What i've done this inside my Fragment B:
private ActionMode.Callback mActionModeCallback = new ActionMode.Callback() {
#Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
MenuInflater inflater = mode.getMenuInflater();
inflater.inflate(R.menu.entry_list_context_menu, menu);
return true;
}
#Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
// make sure no item is selected when bar is shown
adapter.clearSelection();
adapter.notifyDataSetChanged();
return false;
}
#Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
Log.d("EntryList", "Item '" + item.getTitle()
+ "' clicked [onActionItemClicked()]");
return true;
}
#Override
public void onDestroyActionMode(ActionMode mode) {
adapter.clearSelection();
adapter.notifyDataSetChanged();
contextualMode = null;
}
};
private ActionMode contextualMode;
#Override
public void onItemClick(AdapterView<?> parentView, View itemView,
int index, long id) {
DocumentEntity entry = (DocumentEntity) itemView.getTag();
// something went wrong
if (entry == null) {
Log.e("EntryList", "Tag-Less item clicked [onItemClick()]");
return;
}
if (contextualMode != null) {
Log.d("EntryList",
"contextualMode is not yet initialized [onItemClick()]");
contextualMode = getSherlockActivity().startActionMode(
mActionModeCallback);
} else {
Log.d("EntryList",
"contextualMode already initialized [onItemClick()]");
}
entry.setSelected(!entry.isSelected());
Log.d("EntryList", "entry.selected set to " + entry.isSelected()
+ " [onItemClick()]");
}
The selection works pretty good, but no contextual Actionbar is shown.
The debug result is:
contextualMode already initialized [onItemClick()] entry.selected set
to 'true' [onItemClick()]
There is no other position where contextualMode is set...
I got a Fragment B in a Fragment A...
Android does not support embedding a fragment within another fragment. Sorry. This leads me to believe that your problem goes beyond the fact that your contextual ActionBar is not being shown. I suggest you clarify your original post.