I have created an ActionDrawer for my Wear OS App and want to change the icon and text on runtime. This code is working and my app continues to run without errors, but I am not able to slide up the ActionDrawer anymore. Also, when the ActionDrawerMenu is already opened and I press the button there, it disappears and my UI of the app seems frozen.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Top navigation drawer
WearableNavigationDrawerView wearableNavigationDrawer = (WearableNavigationDrawerView) findViewById(R.id.top_navigation_drawer);
wearableNavigationDrawer.setAdapter(new NavigationDrawerAdapter());
wearableNavigationDrawer.getController().peekDrawer();
//Bottom action drawer
WearableActionDrawerView wearableActionDrawer = (WearableActionDrawerView) findViewById(R.id.bottom_action_drawer);
wearableActionDrawer.getController().peekDrawer();
wearableActionDrawer.setOnMenuItemClickListener(this);
}
#Override
public boolean onMenuItemClick(MenuItem menuItem) {
final int itemId = menuItem.getItemId();
switch(itemId) {
case R.id.menuItem_pause:
if(intent == null) {
menuItem.setIcon(getDrawable(R.drawable.ic_pause));
menuItem.setTitle(R.string.actionDrawerMenuPause);
} else {
menuItem.setIcon(getDrawable(R.drawable.ic_play));
menuItem.setTitle(R.string.actionDrawerMenuStart);
}
invalidateOptionsMenu();
return true;
}
return false;
}
I have tried some solutions with invalidateOptionsMenu(), onPrepareOptionsMenu() and onCreateOptionsMenu() but nothing worked for me. I think this just works with standard Android mobile apps but not with Wear OS. So how can I change the text and icon of my WearableActionDrawer-MenuItems when pressing a MenuItem?
I found the solution by accident. It's just this one line of code that programmatically implements the menu.
//Bottom action drawer
WearableActionDrawerView wearableActionDrawer = findViewById(R.id.bottom_action_drawer);
wearableActionDrawer.getController().peekDrawer();
wearableActionDrawer.setOnMenuItemClickListener(this);
getMenuInflater().inflate(R.menu.action_drawer_menu, wearableActionDrawer.getMenu());
Related
I am trying to use this code to show and hide menu icon in toolbar with Fragment in Android Studio:
#Override
public void onPrepareOptionsMenu(Menu menu) {
MenuItem IconBTON = menu.findItem(R.id.myIcon);
if (statusBlueTooth == true){
IconBTON.setVisible(true);
}
if (statusBlueTooth == false){
IconBTON.setVisible(false);
}
}
but the icon is not shown even though the value of the flag statusBlueTooth = true, but just when I touch the toolbar the icon is shown, what do I need to do so that this is shown according to the value of flag statusBlueTooth
Try adding this after in your onCreate of your Fragment:
setHasOptionsMenu(true);
and check IconBTON != null before setting the visibility.
Currently, I'm following suggestion from https://stackoverflow.com/a/29748810/72437 , in order to change status bar color, when entering contextual action mode.
However, from the following video, https://www.youtube.com/watch?v=2Ra56_eh7uk , we can observe that color change always happen at status bar, only then color change on toolbar will follow.
Is there any way, to make color change happens simultaneously, on both status bar and toolbar.
#SuppressLint("NewApi")
private class ModeCallback implements ListView.MultiChoiceModeListener {
public boolean onCreateActionMode(android.view.ActionMode mode,
android.view.Menu menu) {
...
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Activity activity = getActivity();
if (activity != null) {
activity.getWindow().setStatusBarColor(actionModeStatusBarColor);
}
}
return true;
}
public boolean onPrepareActionMode(android.view.ActionMode mode, android.view.Menu menu) {
return true;
}
public boolean onActionItemClicked(android.view.ActionMode mode, android.view.MenuItem item) {
switch (item.getItemId()) {
....
}
return false;
}
public void onDestroyActionMode(android.view.ActionMode mode) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Activity activity = getActivity();
if (activity != null) {
activity.getWindow().setStatusBarColor(colorPrimaryDark);
}
}
}
}
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB) {
this.getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
this.getListView().setMultiChoiceModeListener(new ModeCallback());
...
This is caused because there is animation for switching views on the Toolbar for the normal display and contextual display. You see it because there is no animation applied when switching status bar colors.
I dig around in the android source ActionBar implementation uses 500 seconds to switch between contextual and normal view. See this for animation duration in android source.
Probably you need to perform ValueAnimator with ArgbEvaluatior that will animate color changes to the status bar. Run this animation when you destroy the ActionMode. Duration should be 500 seconds or config_longAnimTime
I hope this helps. :)
I have implemented the action bar in my application. I have implemented the action bar menu using a support library. I want to show the same menu while clicking a menu button on an android device. Can anybody tell me how to do this?
I can get you as far as detecting the menu key. Depending on device there may be overflow of actionbar menu that appears when the menu key is pressed. There are a lot more questions for a complete answer it might be as simple as building the menu options programmatically versus using a res/menu/menu.xml file.
Below I force full screen in an app when the menu key is pressed.
#Override
public boolean onKeyDown(int keycode, KeyEvent e) {
switch (keycode) {
// show the bar if the menu button is pressed
case KeyEvent.KEYCODE_MENU:
isFullScreen = false;
this.setFullScreen(isFullScreen);
return false;
}
return super.onKeyDown(keycode, e);
}
#Override
public void setFullScreen(boolean isChecked) {
if (!isChecked) {
if (Build.VERSION.SDK_INT < 16) {
getWindow().clearFlags(
WindowManager.LayoutParams.FLAG_FULLSCREEN);
}
bar.show();
} else {
if (Build.VERSION.SDK_INT < 16) {
getWindow()
.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
}
bar.hide();
}
}
I have used this guide from Google and this tutorial to produce my own contextual action bar.
private ActionMode.Callback mActionModeCallback = new ActionMode.Callback() {
// 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.annotation_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.custom_button:
// do some stuff
break;
case R.id.custom_button2:
// do some other stuff
break;
default:
// This essentially acts as a catch statement
// If none of the other cases are true, return false
// because the action was not handled
return false;
}
finish(); // An action was handled, so close the CAB
return true;
}
// Called when the user exits the action mode
#Override
public void onDestroyActionMode(ActionMode mode) {
mActionMode = null;
}
};
This menu is designed to appear when the user selects text, so it overrides the native copy/paste menu. Now I get to my issue.
Because I am overriding functions for text selection, I also added a LongClickListener to a WebView and implemented the onLongClick(View v) method so I can detect when users make the selection.
myWebView.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
if (mActionMode != null) {
return false;
}
mActionMode = startActionMode(mActionModeCallback);
v.setSelected(true);
return true;
}
});
When I long click, I see my custom menu appear, but no text is highlighted.I need to have the text selection functionality; without it, my menu is pointless.
How do I override onLongClick(View v), but maintain the text selection provided by Android?If that is not possible, can I make the call to startActionMode(mActionModeCallback) somewhere else so that text will be selected as normal, but my custom menu will also appear?If neither of those are possible... help.
THERE IS AN EASIER WAY! See update below :D
For the sake of completeness, here is how I fixed the problem:
I followed the suggestion according to this answer, with a little more tweaking to more closely match the overridden code:
public class MyWebView extends WebView {
private ActionMode mActionMode;
private mActionMode.Callback mActionModeCallback;
#Override
public ActionMode startActionMode(Callback callback) {
ViewParent parent = getParent();
if (parent == null) {
return null;
}
mActionModeCallback = new CustomActionModeCallback();
return parent.startActionModeForChild(this, mActionModeCallback);
}
}
Essentially, this forces your customized CAB to appear instead of the Android CAB. Now you have to modify your callback so that the text highlight will go away along with the CAB:
public class MyWebView extends WebView {
...
private class CustomActionModeCallback implements ActionMode.Callback {
...
// Everything up to this point is the same as in the question
// Called when the user exits the action mode
#Override
public void onDestroyActionMode(ActionMode mode) {
clearFocus(); // This is the new code to remove the text highlight
mActionMode = null;
}
}
}
That's all there is to it. Be aware that as long as you are using MyWebView with the overridden startActionMode there is NO WAY to get the native CAB (the copy/paste menu, in the case of a WebView). It may be possible to implement that sort of behavior, but that is not the way this code works.
UPDATE: There is a much easier way to do this! The above solution works well, but here is an alternative, easier way.
This solution provides less control over the ActionMode, but it requires far less code than the above solution.
public class MyActivity extends Activity {
private ActionMode mActionMode = null;
#Override
public void onActionModeStarted(ActionMode mode) {
if (mActionMode == null) {
mActionMode = mode;
Menu menu = mode.getMenu();
// Remove the default menu items (select all, copy, paste, search)
menu.clear();
// If you want to keep any of the defaults,
// remove the items you don't want individually:
// menu.removeItem(android.R.id.[id_of_item_to_remove])
// Inflate your own menu items
mode.getMenuInflater().inflate(R.menu.my_custom_menu, menu);
}
super.onActionModeStarted(mode);
}
// This method is what you should set as your item's onClick
// <item android:onClick="onContextualMenuItemClicked" />
public void onContextualMenuItemClicked(MenuItem item) {
switch (item.getItemId()) {
case R.id.example_item_1:
// do some stuff
break;
case R.id.example_item_2:
// do some different stuff
break;
default:
// ...
break;
}
// This will likely always be true, but check it anyway, just in case
if (mActionMode != null) {
mActionMode.finish();
}
}
#Override
public void onActionModeFinished(ActionMode mode) {
mActionMode = null;
super.onActionModeFinished(mode);
}
}
Here is an example Menu to get you started:
<!-- my_custom_menu.xml -->
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="#+id/example_item_1"
android:icon="#drawable/ic_menu_example_1"
android:showAsAction="always"
android:onClick="onContextualMenuItemClicked"
android:title="#string/example_1">
</item>
<item
android:id="#+id/example_item_2"
android:icon="#drawable/ic_menu_example_2"
android:showAsAction="ifRoom"
android:onClick="onContextualMenuItemClicked"
android:title="#string/example_2">
</item>
</menu>
That's it! You're done! Now your custom menu will show up, you don't have to worry about the selection, and you barely have to concern yourself with the ActionMode lifecycle.
This works nearly flawlessly with a WebView that occupies its entire parent Activity. I am not sure how well it will work if there are multiple Views within your Activity at one time. It will likely require some tweaking in that case.
The way I did something similar was to only override the onTouchListener and to invoke a GestureDetector to detect when the WebView was long-pressed and do what I wanted from there. Here's some sample code that allows you to catch long-press events without sacrificing text-selection in the WebView. Hopefully this helps.
#Override
protected void onCreate(Bundle savedInstanceState) {
WebView mWebView = (WebView) findViewById(R.id.myWebView);
GestureDetector mGestureDetector = new GestureDetector(this, new CustomGestureListener());
mWebView.setOnTouchListener(new OnTouchListener(){
#Override
public boolean onTouch(View view, MotionEvent arg1) {
//Suggestion #1 - this just lets the touch to be handled by the system but allows you to detect long presses
mGestureDetector.onTouchEvent(arg1);
return false;
//Suggestion #2 - this code will only let the touch be handled by the system if you don't detect a long press
return mGestureDetector.onTouchEvent(arg1);
}
});
}
private class CustomGestureListener extends SimpleOnGestureListener {
#Override
public void onLongPress(MotionEvent e) {
//do stuff
}
}
I use ActionMode to select items in a grid. The problem is that I cannot recognize whether exactly the Done button is clicked. The only I can is to know that ActionMode is finished. But pressing Back finishes the ActionMode too.
The desired behavior is to accept selection on Done click, and exit ActionMode on Back press.
I tried to use ActionMode.setCustomView() but it doesn't affect the Done button. The Activity.onBackPressed() is not called when ActionMode is started.
The one solution I've found is to use ActionBarSherlock and get the Done button manually:
View closeButton = findViewById(R.id.abs__action_mode_close_button);
But it works on Android 2.x-3.x only, because on 4.x a native action bar is used.
Please don't do that as it's implementation specific and extremely non-standard.
You can use the onDestroyActionMode callback for when an action mode is dismissed.
Here is the solution:
ActionMode mMode = MyActivityClass.this.startActionMode(some implementation);
int doneButtonId = Resources.getSystem().getIdentifier("action_mode_close_button", "id", "android");
View doneButton = MyActivityClass.this.findViewById(doneButtonId);
doneButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// do whatever you want
// in android source code it's calling mMode.finish();
}
});
Here is my implementation, and it's a proper hack but it works and I can't really find an alternative to doing something specific when the ActionMode DONE is clicked. I find it really weird that you can't capture this event more elegantly.
Any suggestions to making this slightly less ugly would be greatly appreciated...
In my activity..
boolean mActionModeIsActive = false;
boolean mBackWasPressedInActionMode = false;
#Override
public boolean dispatchKeyEvent(KeyEvent event)
{
mBackWasPressedInActionMode = mActionModeIsActive && event.getKeyCode() == KeyEvent.KEYCODE_BACK;
return super.dispatchKeyEvent(event);
}
#Override
public boolean onCreateActionMode(ActionMode mode, Menu menu)
{
mActionModeIsActive = true;
return true;
}
#Override
public void onDestroyActionMode(ActionMode mode)
{
mActionModeIsActive = false;
if (!mBackWasPressedInActionMode)
onActionModeDoneClick();
mBackWasPressedInActionMode = false;
}
public void onActionModeDoneClick();
{
// Do something here.
}
If you are using Fragments with your Activity then some of this code will probably need to be in the Fragment, and the other bits in the Activity.
#JakeWharton (and other ActionBarSherlock users) if you see this on your travels. I'd be interested to know if the above is compatible with ABS as I have yet to integrate ABS with my current project.