Passing id of ListView item to ActionMode.Callback object - android

So my problem right now is that right now I am long clicking an item in a ListView which brings up a contextual action bar. The id passed into onItemLongClick is the variable that I would like to use in the mActionModeCallback's on ActionItemClicked() method. This seems like it would be a fairly common procedure since if a user is editing a list of items, you would want to access the id of that row in the database somehow when the user clicked an "edit" or a "delete" action.
listView.setOnItemLongClickListener(new OnItemLongClickListener() {
public boolean onItemLongClick(AdapterView<?> p, View view, int pos, long id) {
//The id of the row in the database
long variableThatIWantToPassToCallback = id;
mActionMode = getActivity().startActionMode(mActionModeCallback);
view.setSelected(true);
return true;
}
});
private ActionMode.Callback mActionModeCallback = new ActionMode.Callback() {
public boolean onCreateActionMode(ActionMode mode, Menu menu) {}
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {}
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
//I would like access to the id of the clicked item here, NOT item.getItemId()
}
public void onDestroyActionMode(ActionMode mode) {}
};

The proper way to do this is to call mActionMode.setTag("1") in onItemCheckedStateChanged and then from the onActionItemClicked function call mode.getTag();

Create your own callback by extending the interface ActionMode.Callback
private interface ActionCallback extends ActionMode.Callback {
public void setClickedView(View view);
}
private ActionCallback mActionModeCallback = new ActionCallback() {
public View mClickedView;
public void setClickedView(View view) {
mClickedView = view;
}
// Called when the action mode is created; startActionMode() was called
#Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
// Inflate a menu resource providing context menu items
MenuInflater inflater = mode.getMenuInflater();
inflater.inflate(R.menu.context_menu, menu);
return true;
}
// Called each time the action mode is shown. Always called after onCreateActionMode, but
// may be called multiple times if the mode is invalidated.
#Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return false; // Return false if nothing is done
}
// Called when the user selects a contextual menu item
#Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item ) {
switch ( item.getItemId() ) {
case R.id.menu_delete:
Log.v( TAG, "#onActionItemClicked ready to delete the item with id: " + mClickedView.getTag() );
mode.finish(); // Action picked, so close the CAB
return true;
default:
return false;
} // end switch
}
// Called when the user exits the action mode
#Override
public void onDestroyActionMode(ActionMode mode) {
mActionMode = null;
}
};
For a view which has OnLongClickListener attached, override the onLongClick callback this way.
#Override
// Called when the user long-clicks on someView
public boolean onLongClick( View view ) {
// proceed only when actionmode is not null
// otherwise overlapping action modes will be
// displayed
if( mActionMode != null ) {
return false;
}
mActionModeCallback.setClickedView(view);
// Start the CAB using the ActionMode.Callback defined above
mActionMode = startActionMode( mActionModeCallback );
view.setSelected(true);
return true;
}

Related

How do you "deflate" menus in android?

I have a contextual action bar, where I have a setting called "DELETE". When I press that button I want the CAB menu to disappear.
class MyActionModeCallBack implements android.view.ActionMode.Callback {
#Override
public boolean onCreateActionMode(android.view.ActionMode actionMode, Menu menu) {
actionMode.getMenuInflater().inflate(R.menu.event_menu, menu);
return true;
}
#Override
public boolean onPrepareActionMode(android.view.ActionMode actionMode, Menu menu) {
return false;
}
#Override
public boolean onActionItemClicked(android.view.ActionMode actionMode, MenuItem menuItem) {
int id = menuItem.getItemId();
if (id == R.id.delete) {
*******
_root.removeView(view);
// This is where I want to remove the CAB menu
*******
}
return false;
}
#Override
public void onDestroyActionMode(android.view.ActionMode actionMode) {
((RelativeLayout)view).removeAllViews();
view.setBackgroundColor(0xFF00FF00);
view.setTag(R.string.viewSelected, "0");
}
}
I was thinking about calling the onDestroyActionMode() right after the _root.removeView(view);, but I don't know what arguments to pass in.
Any suggestions are appreciated, thanks!
If you are trying to hide the item being selected, you can call
menuItem.setVisible(false);
Alternatively, if you want to remove all items from the menu, you can call
optionsMenu.clear();
in the onActionItemClicked() method.
You can save your menu to a field in your onCreateOptionsMenu method as follows:
#Override
public boolean onCreateOptionsMenu(final Menu menu) {
optionsMenu = menu
}
Call actionMode.finish(). This will invoke the onDestroyActionMode() callback.

Getting error: Unable to Instantiate Activity

I have been following this tutorial: http://developer.android.com/guide/topics/ui/menus.html#CAB. However, when I run the application, I get an error:
java.lang.RuntimeException:
Unable to instantiate activity
ComponentInfo{com.example.ayushi.chaseyourdream/com.example.ayushi.chaseyourdream.MainActivity}:
java.lang.InstantiationException: can't instantiate class
com.example.ayushi.chaseyourdream.MainActivity
Here's my MainActivity. My application was working completely fine before I added code between label 1 and `label 2'.
public abstract class MainActivity extends ActionBarActivity implements AbsListView.MultiChoiceModeListener{
DbHelper db;
ListView myList;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
db = new DbHelper(this);
myList = (ListView) findViewById(R.id.newList);
loadData();
// LABEL 1
myList.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
myList.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 onActionItemClicked(ActionMode mode, MenuItem item) {
// Respond to clicks on the actions in the CAB
switch (item.getItemId()) {
case R.id.menu_delete:
// deleteSelectedItems();
mode.finish(); // Action picked, so close the CAB
return true;
default:
return false;
}
}
#Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
// Inflate the menu for the CAB
MenuInflater inflater = mode.getMenuInflater();
inflater.inflate(R.menu.context_main, menu);
return true;
}
public void onDestroyActionMode(ActionMode mode) {
// Here you can make any necessary updates to the activity when
// the CAB is removed. By default, selected items are deselected/unchecked.
}
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
// Here you can perform updates to the CAB due to
// an invalidate() request
return false;
}
});
// LABEL 2
}
public void onResume()
{
super.onResume();
loadData();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
public void loadData()
{
Cursor cursor = null;
try {
cursor = db.fetchData();
}
catch(NullPointerException e)
{
e.printStackTrace();
}
ListAdapter myAdapter = new SimpleCursorAdapter(this, R.layout.tasks,
cursor,
new String[]{db._ID, db.COLUMN_1, db.COLUMN_2},
new int[]{R.id.idnum, R.id.c1, R.id.c2}, 0);
myList.setAdapter(myAdapter);
}
public void addNew(View v)
{
Intent intent = new Intent(this,AddActivity.class);
startActivity(intent);
}
}
EDIT 1: this line
public class MainActivity extends ActionBarActivity implements AbsListView.MultiChoiceModeListener{
showed me an error in Android Studio, and when I clicked Alt + Enter, it showed me two options: Make the class abstract, or implement its methods.
The first option gave me the initial problem, so now I went for option 2. Here are the additional methods implemented :
#Override
public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) {
}
#Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
return false;
}
#Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return false;
}
#Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
return false;
}
#Override
public void onDestroyActionMode(ActionMode mode) {
}
Now, my doubt is: these are exactly the same methods in between LABEL 1 and LABEL 2, and erasing any one set will lead to errors. What should I do?
You are getting this error: Unable to instantiate activity because you made your activity abstract. Remove that and implement the methods of AbsListView.MultiChoiceModeListenerinterface.
When you're implementing an Interface so you have to implement all the methods you have listed. If you don't know what to do with method you don't use just leave them as they are.
Now, my doubt is: these are exactly the same methods in between LABEL 1 and LABEL 2, and erasing any one set will lead to errors. What should I do?
You shouldn't have two of the same. Try using #Override annotation on the methods you implemented.

How to override Contextual Action Bar when selecting text in a WebView in Android?

I've googled a lot, and find some tutorials and answers here in stackoverflow, but I am facing some difficults to resolve this issue.
I have a Fragment with a WebView, and I want to show my custom Contextual Action Bar when the user selects some text of my web view. I have two main issues here:
1 Currently, my custom CAB is showed when the user performs long click in any part of the web view.
2 The text is not selected when the user performs long click in some text of my web view.
Some of my current code:
Custom interface:
public class SelectActionModeCallback implements ActionMode.Callback {
#Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
return false;
}
#Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return false;
}
#Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
return false;
}
#Override
public void onDestroyActionMode(ActionMode mode) {
}
}
Custom WebView
public class CustomWebView extends WebView {
private SelectActionModeCallback actionModeCallback;
public CustomWebView(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
public ActionMode startActionMode(Callback callback) {
actionModeCallback = new SelectActionModeCallback();
return super.startActionMode(actionModeCallback);
}
In my fragment, I have this:
#Override
public void onResume() {
super.onResume();
myWebView.setOnLongClickListener(new View.OnLongClickListener() {
public boolean onLongClick(View view) {
if (mActionMode != null) {
Toast.makeText(getActivity(), "test", Toast.LENGTH_SHORT).show();
return false;
}
mActionMode = getActivity().startActionMode(mActionModeCallback);
view.setSelected(true);
return true;
}
});
}
private SelectActionModeCallback mActionModeCallback = new SelectActionModeCallback() {
#Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
MenuInflater inflater = mode.getMenuInflater();
inflater.inflate(R.menu.custom, 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_sustom:
customMethod();
mode.finish();
return true;
default:
return false;
}
}
#Override
public void onDestroyActionMode(ActionMode mode) {
mActionMode = null;
}
};
THERE IS A BETTER WAY! See the following answer: https://stackoverflow.com/a/22391169/2608235
In short, you can't use an OnLongClickListener and keep the selection within a WebView. Android does some weird stuff behind the scenes for selecting text within WebViews. If you override OnLongClickListener in order to call startActionMode, you will lose the selection, as you have found out.
What you should do instead is override startActionMode in your fragment, rather than its parent View (in your case, CustomWebView).
I don't have the mod permission to mark this question as a duplicate, but it is.
See my question for more info: Use a custom contextual action bar for WebView text selection

Batch contextual action mode

I want to enable multiple view selection on longClick(). Should I declare an action mode object and call startActionMode()? Also, how would I change menu list for single item click? I used the documentation as reference, as shown.
ListView listView = getListView();
listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
listView.setMultiChoiceModeListener(new 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 onActionItemClicked(ActionMode mode, MenuItem item) {
// Respond to clicks on the actions in the CAB
switch (item.getItemId()) {
case R.id.menu_delete:
deleteSelectedItems();
mode.finish(); // Action picked, so close the CAB
return true;
default:
return false;
}
}
#Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
// Inflate the menu for the CAB
MenuInflater inflater = mode.getMenuInflater();
inflater.inflate(R.menu.context, menu);
return true;
}
#Override
public void onDestroyActionMode(ActionMode mode) {
// Here you can make any necessary updates to the activity when
// the CAB is removed. By default, selected items are deselected/unchecked.
}
#Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
// Here you can perform updates to the CAB due to
// an invalidate() request
return false;
}
});
To change menu list for single item click following is the code.
int count=0;
#Override
public void onItemCheckedStateChanged(ActionMode mode, int position,
long id, boolean checked) {
if (checked) {
count++;
} else {
count--;
}
mode.invalidate(); // Add this to Invalidate CAB so that it calls onPrepareActionMode
}
Now modify the onPrepareActionMode as follows
#Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
if (selCount == 1){
//show the menu here that you want if only 1 item is selected
} else {
//show the menu here that you want if more than 1 item is selected
}
}

Contextual Menu in a Fragment inside a Fragment (ActionMode)

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.

Categories

Resources