I have an EditText and I want the user to be able to select some text and apply some basic formatting to the selected text (bold, italic, etc). I still want the standard copy, cut, paste options to show, though. I read somewhere in the Android documentation that to do this, you should call setCustomSelectionActionModeCallback() on the EditText and pass it an ActionModeCallback(), so that's what I did. Here's my code:
In my activity's onCreate() method:
myEditText.setCustomSelectionActionModeCallback(new TextSelectionActionMode());
Callback declaration:
private class TextSelectionActionMode implements ActionMode.Callback {
#Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
return false;
}
#Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
menu.add("Bold");
return true;
}
#Override
public void onDestroyActionMode(ActionMode mode) {
}
#Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return false;
}
}
The problem I'm having is that when I click on the overflow button (to access my "Bold" menu item), the ActionMode gets closed immediately. If I set it to always show as an action, using this:
MenuItem bold = menu.add("Bold");
bold.setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_ALWAYS);
It works fine and I can click on it (though it obviously does nothing). What am I missing here?
Edit: Just wanted to add that I run into the exact same problem if I actually inflate a menu instead of adding menu items programmatically. Once again, though, the problem goes away if I force it to always show as an action.
It's frameworks issue. If textview receive 'focus changed' event, then textview stop the action mode. When overflow popup is shown, textview miss focus.
This issue has been solved in Android 6.0. However you should use ActionMode.Callback2 as described here in Android 6.0.
For Android 5.x and below, I recommend this workaround: add a button to Toolbar or ActionBar which records the current selection and then open another context menu.
this.inputText_selectionStart = inputText.getSelectionStart();
this.inputText_selectionEnd = inputText.getSelectionEnd();
registerForContextMenu(inputText);
openContextMenu(inputText);
unregisterForContextMenu(inputText);
It is a filed Android bug: https://code.google.com/p/android/issues/detail?id=82640.
That link contains a workaround. Fortunately this has been fixed in Android 6.0.
Related
I am writing here about an issue that was introduced when we migrated from the AppCompat library to the AndroidX library. While doing so, we switched from android.support.design.widget.NavigationView to com.google.android.material.navigation.NavigationView and that’s when the following issue started.
In our NavigationView design, in order to save space, we implemented an expandable menu, so that when users clicks on the “more” button, the menu expands to show more options. It starts off with only some options visible, and the rest are not visible, as follows;
Option 1
Option 2
More…
Upon clicking on the “More...” button, the menu expands to;
Option 1
Option 2
Option 3
Option 4
Option 5
Option 6
To do this we used following code;
#Override
public boolean onNavigationItemSelected(MenuItem item)
{
....
if (item.getItemId() == R.id.nav_more)
{
item.setVisible(false); // hide the “More” item
getMenu().findItem(R.id.nav_option_3).setVisible(true);
getMenu().findItem(R.id.nav_option_4).setVisible(true);
getMenu().findItem(R.id.nav_option_5).setVisible(true);
getMenu().findItem(R.id.nav_option_6).setVisible(true);
return true;
}
.......
return false;
}
Well, this code has worked in the past, but when we migrated to using the androidx library, poof, it stopped working. Well, it did work a bit. The “More...” button got hidden, but the previously hidden options, were not being displayed.
As, it took me many hours to solve this issue, and to save others this headache, I will explain the issue and the solution.
The first thing to do in such cases, is to look at the source code. As the code is open source, I was able to get it at github. At first glance I didn’t get smarter. I found that the NavigationView has a NavigationMenuPresenter object field (called presenter), that has a method called updateMenuView() which calls adapter.update(), which calls prepareMenuItems() and notifyDataSetChanged(). This sounded like the needed fix, so using reflection, we accessed and called the updateMenuView() method, but surprisingly, it did not help!
So, I decided to take it to the extreme, and see what happens if I call getMenu().clear(), and believe it or not, nothing happened. It seems that any changes made to Menu after the NavigationView is shown, are ignored. But a quick look through source code, I could not see any reason for that.
So how do I solve this issue? I tried using the latest alpha version of the library, but I still have the same issue.
Well, after much work, I found the solution. It's actually simple. Just hold on for the answer.
Lionscribe
So I was back to the source code, searching for some clue, when I fell upon a method called setUpdateSuspended(boolean updateSuspended). Well, that sounded suspicious! I searched for usage of this method, and found it being called in the onClick callback. Here is a minimized version of the code;
#Override
public void onClick(View view) {
NavigationMenuItemView itemView = (NavigationMenuItemView) view;
setUpdateSuspended(true);
MenuItemImpl item = itemView.getItemData();
boolean result = menu.performItemAction(item, NavigationMenuPresenter.this, 0);
setUpdateSuspended(false);
}
Bingo! It seems that while handling clicks, the NavigationView suspends and will not recognize any changes done to menu. I am not sure the reason for this, but as we were updating the menu in the onNavigationItemSelected callback, which is called by the onClick method, the menu updates are ignored.
Well, once I understood the issue, the solution was simple and clean. I just wrapped the code in a Runnable, and posted it, so that it runs after the onClick method returns, and setUpdateSuspended is set back to false. Here is the updated code;
#Override
public boolean onNavigationItemSelected(MenuItem item)
{
....
if (item.getItemId() == R.id.nav_more)
{
final MenuItem itemFinal = item;
post(new Runnable()
{
#Override
public void run()
{
getMenu().findItem(R.id.nav_option_3).setVisible(true);
getMenu().findItem(R.id.nav_option_4).setVisible(true);
getMenu().findItem(R.id.nav_option_5).setVisible(true);
getMenu().findItem(R.id.nav_option_6).setVisible(true);
itemFinal.setVisible(false); // hide the “More” item
}
});
return true;
}
.......
return false;
}
Viola! The expandable menu now works like it used to, the hidden items are now being shown!
I hope this will be of help to others with same issue.
Lionscribe
I have been trying for 2 days to add actions to the title/action bar in my android app. I have started the app to learn android and familiarize myself
I have read a couple of question on stackoverflow and some other tutorials on google to try and find a answer, I couldn't however find one for a custom title bar but rather a title bar in general.
What I have tried is adding the following in my activity.
#Override
public boolean onCreateOptionsMenu(Menu menu) {
//boolean result = super.onCreateOptionsMenu(menu);
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.mainmenu, menu);
return true;
#Override
public boolean onMenuItemSelected(int featureId, MenuItem item) {
switch (item.getItemId()) {
case ADD_NEW_FRIEND_ID: {
Intent i = new Intent(FriendList.this, AddFriend.class);
startActivity(i);
But nothing happens. Any suggestions on where to go from here or what to do?
Since there is no onMenuItemSelected() in Android, perhaps try onOptionsItemSelected(), to line up with your onCreateOptionsMenu().
Also, since you are trying to "learn android and familiarize myself", I would recommend getting rid of the "class that was created to style the titlebar/actionbar to be dynamic per user that's logged in to the app". While that may be a nice feature, it will add unnecessary complexity to your app and may interfere with your learning experience. Focus on learning Android first, then fret about customizable title bars later.
I am working on to avoid cut/copy/paste in smart phone (for tablet its fine).
Its fine in port mode but coming in land mode EditText shows a Button Next.
after selecting the text, next button converts into Edit Button which has copy,cut and paste option.
So is there any way to disable cut/copy after rotation when edit button appears.
i am following this link.
How to disable copy/paste from/to EditText
I think you can use this:
http://developer.android.com/reference/android/widget/TextView.html#attr_android:imeOptions
With the flag:
flagNoAccessoryAction
Used in conjunction with a custom action, this indicates that the action should not be available as an accessory button when the input method is full-screen. Note that by setting this flag, there can be cases where the action is simply never available to the user. Setting this generally means that you think showing text being edited is more important than the action you have supplied.
Corresponds to IME_FLAG_NO_ACCESSORY_ACTION.
I would insist you to disable the long click on the EditText as that Edit button appears when you long press on the selected text in the EditText.
et_text.setLongClickable(false);
Also, you can clear the ClipboardManager, so that if you already have something that is already copied to ClipBoard will be cleared using,
ClipboardManager manager = (ClipboardManager)
getSystemService(Context.CLIPBOARD_SERVICE);
manager.setText("");
Try this ..
your_edit_text.setCustomSelectionActionModeCallback(new Callback() {
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return false;
}
public void onDestroyActionMode(ActionMode mode) {
}
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
return false;
}
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
return false;
}
});
Try using these two lines. It may help you:
EditText.setFocusable(false);
EditText.setFocusableInTouchMode(false);
as we don't know what u tried so far..and what you actually doing in your code...beside that..here is a link ...similar to your question , i think this will help you out..
Disabling the fullscreen editing view for soft keyboard input in landscape?
#Cobra - set EditText.setEditable(false); in your code, or you set this property in your xml as well.
this property done well, but if any case this won't work ..set setFocusable also false..
this solution work for me..
I'm facing a problem when actions intended to be shown in overflow menu in action bar are not there on Galaxy S3. As a consequence the UX is somewhat confusing - my action bar on Galaxy S3 is there to only display app logo and name but offering no extra functionality.
I'd like to have an identical UX on all devices running on Android 4.x with actions in the overflow menu. Is this possible without using third-party components such as ActionBarSherlock?
Thanls
This is a decision made by some manufacturers that requires some "bad" solutions if you really want to do this. The overflow menu is just the "regular" old menu button that all android devices used to have. When the menu button got removed by Google in Honeycomb and ICS some manufacturers decided to keep the menu button. This has lead to great confusion about what the menu button is and does.
You should keep in mind though that the user using a S3 would expect to have a functional menu button as they would not be used to seeing a 3-dot menu. All apps using the built in menu system should appear in a way to the user that they expect. Therefor I would strongly recommend against the urge to have your app look exactly the same on all devices in this matter since it would most likely confuse users more then help them. It should be possible, to both implement the "proper" menu system and a "custom/fake" 3-dot menu if you wish however.
This post seems to have some good guidelines:
https://stackoverflow.com/a/10713860/1068167
There is a quick and dirty way to fake the absence of a hardware menu button using reflection to set a field in your app's ViewConfiguration instance.
The following code snip can be added to your activity and called during onCreate().
private void enableActionBarOverflow() {
try {
ViewConfiguration config = ViewConfiguration.get(this);
Field menuKeyField = ViewConfiguration.class
.getDeclaredField("sHasPermanentMenuKey");
if(menuKeyField != null) {
menuKeyField.setAccessible(true);
menuKeyField.setBoolean(config, false);
}
} catch (Exception e) {
e.printStackTrace();
}
}
Not a clean solution as the implementation of ViewConfiguration could change at some point in the future, and since the sHasPermanentMenuKey field is private, there's no guarantee that the field will always be there.
However, I would only use this as a last resort if you absolutely must have an overflow menu on devices that have a menu key.
Assuming you're minimum API is 11 (Honeycomb) or greater, a better solution would be to make your own overflow menu like so:
Add a menu item for the overflow in your menu.xml, setting it to always show and inflate in your onCreateOptionsMenu()
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
...
<item
android:id="#+id/action_overflow"
android:icon="#drawable/ic_action_settings"
android:title="#string/settings"
android:showAsAction="always">
</item>
</menu>
,
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater mi = getMenuInflater();
mi.inflate(R.menu.menu, menu);
return super.onCreateOptionsMenu(menu);
}
Create a separate overflow_menu.xml resource for your choices you want in the overflow menu
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:id="#+id/overflow_action1"
android:title="#string/overflow_action1">
</item>
<item
android:id="#+id/overflow_action2"
android:title="#string/overflow_action2">
</item>
</menu>
In your onOptionsItemSelected() method, handle the selection of your overflow menu
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
...
case R.id.action_overflow:
PopupMenu popup = new PopupMenu(
this, findViewById(R.id.action_overflow));
MenuInflater inflater = popup.getMenuInflater();
inflater.inflate(R.menu.overflow_menu, popup.getMenu());
popup.setOnMenuItemClickListener(this);
popup.show();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
Implement the PopupMenu.OnMenuItemClickListener interface in your activity to handle the clicks of the overflow items
#Override
public boolean onMenuItemClick(MenuItem item) {
switch (item.getItemId()) {
case R.id.overflow_action1:
//do stuff
return true;
case R.id.overflow_action2:
//do stuff
return true;
default:
return false;
}
}
In trying to follow the Android Design Guidelines, I'm running into a small quandary.
I want to have a list of items that I can long-press several of (multi-select), and then perform bulk actions on them.
The Design Guidelines suggest using the Contextual Action Bar for this, and it sounds perfectly like what I had in mind. Problem is, I'm trying to maintain compatibility backwards to API 7 (due to my phone being 2.3.3 currently).
I'm using ActionBarSherlock to get other actionbar stuff, but I can't seem to figure out how to get it to either fire up a contextual action bar, nor have I figured out how to add buttons arbitrarily to the ActionBar in ABS. I see you can do tabs, so maybe that's the answer, but since I'm trying to allow multi-select, I don't want to have the normal modal context menu.
This is a late answer, but I think would help people stuck.
Opening the contextual action bar is actually pretty simple, at any point in your activity you just have to call:
startActionMode(mActionModeCallback);
If you are not in your main activity, like in fragments, you can get a reference with
getSherlockActivity().startActionMode(mActionModeCallback);
and this is the callback
private ActionMode.Callback mActionModeCallback = new ActionMode.Callback(){
#Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
MenuInflater inflater = mode.getMenuInflater();
inflater.inflate(R.menu.actionbar_context_menu, menu);
return true;
}
#Override
public void onDestroyActionMode(ActionMode mode) {
}
#Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_item1:
return true;
case R.id.menu_item2:
//close the action mode
//mode.finish();
return true;
default:
mode.finish();
return false;
}
}
};
The xml is a simple menu like the actionbar one:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="#+id/menu_item1"
android:icon="#drawable/ic_item1"
android:title="#string/ITEM1"
android:showAsAction="always|withText" />
<item android:id="#+id/menu_item2"
android:icon="#drawable/ic_item2"
android:title="#string/ITEM2"
android:showAsAction="always|withText" />
Setting up contextual actionbar is the same to setting up the 'regular' ActionBar items as far as the XML is concerned. This example in the developer's guide explains it all.
In order to use ActionBarSherlock, replace the default Android-callbacks to the ActionBarSherlock-edited callbacks (e.g. instead of Android.View.ActionMode, use com.actionbarsherlock.view.ActionMode).
ActionBarSherlock has its own implementation of ActionMode, but you'll have to manualy controll its lifesycle, I wrote a tutorial about this.
For long click sample please refer to below links. First one is java code required for sample. And second one is how to define the layout;
Java source
Layout xml
I will answer second part of your question. Here is an example how to add any View instance (button in the code below) actionbar with ActionBarSherlock library:
#Override
public boolean onCreateOptionsMenu(Menu menu) {
refreshButton = (RotatingButton) LayoutInflater.from(this).inflate(R.layout.actionbar_customview_refresh, null);
refreshButton.setOnClickListener(refreshButtonListener);
MenuItem item = menu.add(0, android.R.id.copy, 0, getString(R.string.actionbar_refresh));
item.setActionView(refreshButton);
item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.main_activity_action_bar, menu);
}
I was facing the same issue. It was solved when I found this link. Basically, you have to create a callback class that implements ActionMode.Callback. In this class, you inflate the Action Bar with your contextual Action Bar. At each selection (or long click), you start the callback using the startActionMode method. See the link for an working code =]
EDIT: There is also an example on Sherlock's samples under /samples/demos/src/com/actionbarsherlock/sample/demos/ActionModes.java