How do I create a "Search" field in a contextual action bar? - android

I have an activity with an ActionBar. One of the options is to search a document (not necessarily local). I want to use a contextual action bar (CAB) with the following items:
edit field - place for user to enter text they want to search for
Previous Button - moves to the previous item that matches the search
Next Button - moves to the next item that matches the search
I want to use a CAB for several reasons. Primarily, I want the user to pick an option which will bring up the CAB defined above. When the user is done with the search, they select the done button and the ActionBar returns to previous state.
Here's the problem. I can't get the search item to appear in my CAB in an expanded state. NOTE: The activity I'm attempting to modify is NOT the main activity but is an activity launched by the user based on certain events. Once this activity is loaded, the user may or may not decide to use the 'search' functionality I'm describing. I also do not want Android to do the searching. I have an API that will search for the results I want and allow me to navigate to the prev/next search result.
My code for the CAB (which is being called correctly) is:
protected ActionMode mActionModeSearch;
private ActionMode.Callback mActionModeSearchCallback = new ActionMode.Callback()
{
#Override
public boolean onCreateActionMode(ActionMode actionMode, Menu menu)
{
actionMode.getMenuInflater().inflate(R.menu.pdf_menu_search, menu);
// Associate searchable configuration with the SearchView
SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
SearchView searchView = (SearchView) menu.findItem(R.id.pdf_menu_search_item).getActionView();
searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
searchView.setIconifiedByDefault(false);
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener()
{
#Override
public boolean onQueryTextSubmit(String s)
{
return false;
}
#Override
public boolean onQueryTextChange(String s)
{
return false;
}
});
return true;
}
#Override
public boolean onPrepareActionMode(ActionMode actionMode, Menu menu)
{
return false;
}
#Override
public boolean onActionItemClicked(ActionMode actionMode, MenuItem menuItem)
{
switch(menuItem.getItemId())
{
case R.id.pdf_menu_search_prev:
findPrevSearchResult();
return true;
case R.id.pdf_menu_search_next:
findNextSearchResult();
return true;
default:
return false;
}
}
#Override
public void onDestroyActionMode(ActionMode actionMode)
{
//resetHideToolbarsTimer();
}
};
The CAB menu code is:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<group android:id="#+id/group_search_mode">
<item
android:id="#+id/pdf_menu_search_item"
android:title="#string/search"
android:icon="#drawable/ic_pdf_action_search"
app:showAsAction="collapseActionView|ifRoom"
app:actionViewClass="android.support.v7.widget.SearchView"/>
<item
android:id="#+id/pdf_menu_search_prev"
android:title="#string/search_prev"
android:icon="#drawable/ic_pdf_action_search_prev"
app:showAsAction="ifRoom" />
<item
android:id="#+id/pdf_menu_search_next"
android:title="#string/search_next"
android:icon="#drawable/ic_pdf_action_search_next"
app:showAsAction="ifRoom" />
</group>
I also changed my activity definition in AndroidManifest.xml although I'm not sure this is required:
<activity
android:name="com.myapp.myActivity"
android:label="#string/app_name">
<intent-filter>
<action android:name="android.intent.action.SEARCH" />
</intent-filter>
<meta-data
android:name="android.app.default_searchable"
android:value="com.myapp.myActivity" />
<meta-data
android:name="android.app.searchable"
android:resource="#xml/searchable" />
</activity>
When I run the app, the activity load correctly. When the user selects the search option, I load the CAB and it seems to be running fine. I've stepped through the code to make sure the onCreateActionMode is functioning as expected. But, all I see is the icon for search, next and previous, with the 'Done' button. When I touch the 'Search' icon, it doesn't do anything. No text field is created in the CAB for me to enter text. What am I doing wrong????
Screenshot showing the CAB coming up. As you can see, the search icon shows but doesn't do anything when I press it. What I really need is for the search edit box to appear by default.

First, we should set always value for app:showAsAction of all menu items:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<group android:id="#+id/group_search_mode">
<item
android:id="#+id/pdf_menu_search_item"
android:icon="#drawable/ic_pdf_action_search"
android:title="#string/search"
app:actionViewClass="android.support.v7.widget.SearchView"
app:showAsAction="always"/>
<item
android:id="#+id/pdf_menu_search_prev"
android:icon="#drawable/ic_pdf_action_search_prev"
android:title="#string/search_prev"
app:showAsAction="always"/>
<item
android:id="#+id/pdf_menu_search_next"
android:icon="#drawable/ic_pdf_action_search_next"
android:title="#string/search_next"
app:showAsAction="always"/>
</group>
</menu>
Secondary, in this case we don't need to set intent filter for our Activity and searchable info for our SearchView.
Definition of this Activity in AndroidManifest.xml:
<activity
android:name="com.myapp.myActivity"
android:label="#string/app_name" />
ActionMode.Callback implementation:
private ActionMode.Callback mActionModeSearchCallback = new ActionMode.Callback() {
private SearchView mSearchView;
#Override
public boolean onCreateActionMode(ActionMode actionMode, Menu menu) {
actionMode.getMenuInflater().inflate(R.menu.home, menu);
mSearchView = (SearchView) MenuItemCompat.getActionView(menu.findItem(R.id.pdf_menu_search_item));
mSearchView.setIconifiedByDefault(false);
mSearchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
#Override
public boolean onQueryTextSubmit(String s) {
return false;
}
#Override
public boolean onQueryTextChange(String s) {
return false;
}
});
return true;
}
#Override
public boolean onPrepareActionMode(ActionMode actionMode, Menu menu) {
mSearchView.requestFocus();
return true;
}
#Override
public boolean onActionItemClicked(ActionMode actionMode, MenuItem menuItem) {
switch (menuItem.getItemId()) {
case R.id.pdf_menu_search_prev:
findPrevSearchResult();
return true;
case R.id.pdf_menu_search_next:
findNextSearchResult();
return true;
default:
return false;
}
}
#Override
public void onDestroyActionMode(ActionMode actionMode) {
}
};
I just tried this code on three 4.0+ devices and it was fully working. But I didn't test on devises with lower OS versions.
Hope it will be helpful for you.

i have problem searchView in CAB
so i set layout(with edittext) in manu item, it give me same view as searchview
try this i hope it works for you
menu/search.xml
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:id="#+id/edt_mySearch"
android:actionLayout="#layout/search_bar"
android:enabled="true"
android:showAsAction="always"
android:visible="true"/>
</menu>
layout/search _bar.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<EditText
android:layout_alignParentLeft="true"
android:id="#+id/edt_search"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:hint=" Search..."
/>
</RelativeLayout>
and myAction mode
mActionModeCallback = new ActionMode.Callback() {
#Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
// mode.setTitle("Demo");
getSupportMenuInflater().inflate(R.menu.search, menu);
RelativeLayout m = (RelativeLayout) menu.findItem(
R.id.edt_mySearch).getActionView();
EditText mSearchView = (EditText) m
.findViewById(R.id.edt_search);
mSearchView.addTextChangedListener(new TextWatcher() {
#Override
public void onTextChanged(CharSequence s, int start,
int before, int count) {
// TODO Auto-generated method stub
}
#Override
public void beforeTextChanged(CharSequence s, int start,
int count, int after) {
}
#Override
public void afterTextChanged(Editable s) {
// search here
}
});
return true;
}
#Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
// TODO Auto-generated method stub
return false;
}
#Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
return false;
}
#Override
public void onDestroyActionMode(ActionMode mode) {
}
};

Related

How to set toggle for Android MenuItems. How to show one when the other is clicked?

i have a note taking activity where i would like to be able to edit and save (basically overwrite) the same note.
Once i click the edit MenuItem, I would like it to hide and then show the save MenuItem.
I can get the edit MenuItem to be invisible but i cant get the save MenuItem to show. i keep getting a null pointer exception.
here's my edit_question_menu.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="#+id/edit_question"
android:icon="#drawable/ic_edit_black_24dp"
android:title="Edit"
app:showAsAction="ifRoom"
android:visible="true">
</item>
<item
android:id="#+id/save_question"
android:icon="#drawable/ic_save_black_24dp"
android:title="Edit"
app:showAsAction="ifRoom"
android:visible="false">
</item>
</menu>
activity.java file
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater menuInflater = getMenuInflater();
menuInflater.inflate(R.menu.edit_question_menu, menu);
MenuItem itemSave = menu.findItem(R.id.save_question);
itemSave.setVisible(false);
return super.onCreateOptionsMenu(menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.save_question:
saveQuestion();
return true;
case R.id.edit_question:
item.setVisible(false);
// MenuItem save_Question_MenuItem = findViewById(R.id.save_question);
// save_Question_MenuItem.setVisible(true);
enableEditMode();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
private void enableEditMode(){
MenuItem saveButton = findViewById(R.id.save_question);
getSupportActionBar().setHomeAsUpIndicator(R.drawable.ic_close_black_24dp);
questionEditText = findViewById(R.id.questionEditTextID);
mPostAnswerButton.setEnabled(false);
mPostAnswerButton.setBackgroundColor(getResources().getColor(android.R.color.darker_gray));
mCommentButton.setEnabled(false);
mCommentButton.setBackgroundColor(getResources().getColor(android.R.color.darker_gray));
saveButton.setVisible(true); **ERROR HAPPENS HERE**
}
Any and all help is appreciated. This seems fairly simple but I cant find a way to get it to work.
Add flag to your activity that indicates where the save MenuItem should be visible or not:
private boolean mShowSaveIcon
Override onPrepareOptionsMenu (UPDATE):
#Override
public boolean onPrepareOptionsMenu(Menu menu) {
MenuItem item = menu.findItem(R.id.save_question);
item.setVisible(mShowSaveIcon);
menu.findItem(R.id.edit_question).setVisible(!mShowSaveIcon); // you can use negation of the same flag if one and only one of two menu items is visible; or create more complex logic
return true;
}
In the click handler, change flag value and request invalidation:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.save_question:
mShowSaveIcon = false;
break;
case R.id.edit_question:
item.setVisible(false);
enableEditMode();
mShowSaveIcon = true;
break;
}
invalidateOptionsMenu();
return true;
}
Change handler:
private void enableEditMode(){
/// MenuItem saveButton = findViewById(R.id.save_question); // <--- NO THIS LINE
getSupportActionBar().setHomeAsUpIndicator(R.drawable.ic_close_black_24dp);
questionEditText = findViewById(R.id.questionEditTextID);
mPostAnswerButton.setEnabled(false);
mPostAnswerButton.setBackgroundColor(getResources().getColor(android.R.color.darker_gray));
mCommentButton.setEnabled(false);
mCommentButton.setBackgroundColor(getResources().getColor(android.R.color.darker_gray));
/// saveButton.setVisible(true); **ERROR HAPPENS HERE** // <--- NO THIS LINE
}

How to override "Copy", "Share" and "Select All" options while selecting a text in a TextView

I have a TextView where a user is able to select a text. By default the following options appear: "Copy", "Share" and "Select All".
I need to override them with custom options. But I can't find how to do that. I went through the documentation and this nice article but no lack. The article explains how to extend the menu when a user presses three-dots-button which is not what I need.
Question: How can I override default "Copy", "Share" and "Select All" options in text section menu?
Here is how my view looks like:
<TextView
android:id="#+id/transcript"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbars="vertical" />
And in java code I have:
transcript.setTextIsSelectable(true);
transcript.setFocusable(true);
transcript.setFocusableInTouchMode(true);
You can use TextView.setCustomSelectionActionModeCallback() to do this.
Documentation: https://developer.android.com/reference/android/widget/TextView.html#setCustomSelectionActionModeCallback(android.view.ActionMode.Callback)
I put together a very simple app to demonstrate how to use this feature.
MainActivity.java
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView text = (TextView) findViewById(R.id.text);
CustomActionModeCallback callback = new CustomActionModeCallback(this);
text.setCustomSelectionActionModeCallback(callback);
}
}
activity_main.xml
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/text"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="16dp"
android:text="#string/lorem_ipsum"
android:textIsSelectable="true"/>
</FrameLayout>
CustomActionModeCallback.java
public class CustomActionModeCallback implements ActionMode.Callback {
private final Context context;
public CustomActionModeCallback(Context context) {
this.context = context;
}
#Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
menu.clear();
mode.getMenuInflater().inflate(R.menu.menu_custom, menu);
return true;
}
#Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return false;
}
#Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
if (item.getItemId() == R.id.custom_one) {
Toast.makeText(context, "One!", Toast.LENGTH_SHORT).show();
mode.finish();
return true;
}
else if (item.getItemId() == R.id.custom_two) {
Toast.makeText(context, "Two!", Toast.LENGTH_SHORT).show();
mode.finish();
return true;
}
else if (item.getItemId() == R.id.custom_three) {
Toast.makeText(context, "Three!", Toast.LENGTH_SHORT).show();
mode.finish();
return true;
}
return false;
}
#Override
public void onDestroyActionMode(ActionMode mode) {
}
}
menu_custom.xml
<menu
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="#+id/custom_one"
android:title="One"
app:showAsAction="never"/>
<item
android:id="#+id/custom_two"
android:title="Two"
app:showAsAction="never"/>
<item
android:id="#+id/custom_three"
android:title="Three"
app:showAsAction="never"/>
</menu>
Nothing much to comment on in MainActivity or either xml file. All the magic happens in CustomActionModeCallback.
Both onCreateActionMode() and onPrepareActionMode() can be used to add your custom menu items to the menu. If you use onCreateActionMode(), the system will add some extra options into an overflow menu, like this:
If you use onPrepareActionMode(), the extra items won't be added.
Note that you must return true from onCreateActionMode() no matter what (returning false causes the menu to not be displayed), but you only have to return true from onPrepareActionMode() if you've actually modified the menu.
You can handle the user's clicks on your custom items inside onActionItemClicked(). In my example, I simply show a Toast and then close the contextual menu (using ActionMode.finish()). In this method, you should return true only on menu items that you handle yourself; returning false allows the system default action to happen (such as if you want to give the user the option to select all text).
Finally, onDestroyActionMode() is called when the menu is closed. Perhaps you have some use for this; I did not.

Animate Between Visibility Modes For Menu Item

I am trying to animate between the visibility mode for a menu.
By default all menu items are hidden but when the user clicks on the edit button i want to show all the items with an animation.
I have achieved the first part of changing the visibility of the menu items and that works fine but the animation part crashes the app.
Here is my code.
When user clicks on edit this is called.By default edit_mode is false.
if (!edit_mode) {
edit_mode = true;
supportInvalidateOptionsMenu();
}
This is the menu code.
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_add__custom, menu);
return true;
}
#Override
public boolean onPrepareOptionsMenu(Menu menu) {
MenuItem photo = menu.findItem(R.id.photo);
photo.setVisible(edit_mode);
if (edit_mode)
photo.getActionView().animate().alpha(1.0f);
MenuItem date = menu.findItem(R.id.date);
date.setVisible(edit_mode);
if (edit_mode)
date.getActionView().animate().alpha(1.0f);
MenuItem done = menu.findItem(R.id.done);
done.setVisible(edit_mode);
if (edit_mode)
done.getActionView().animate().alpha(1.0f);
return edit_mode;
}
menu.xml
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<item
android:id="#+id/date"
android:icon="#drawable/ic_event_white_24dp"
android:orderInCategory="200"
android:title="Date"
app:showAsAction="ifRoom" />
<item
android:id="#+id/done"
android:icon="#drawable/ic_done_white_24dp"
android:orderInCategory="300"
android:title="Done"
app:showAsAction="ifRoom" />
<item
android:id="#+id/photo"
android:icon="#drawable/ic_photo_white_24dp"
android:orderInCategory="100"
android:title="Done"
app:showAsAction="ifRoom" />
I sure that crash that you are having is there because of NullException thrown by getActionView(). First of all to animate that way you have set the actionView first during onCreateOptionMenu(). That way when you get the actionView in onPrepareOptionsMenu it wont crash because of that and then you can animate it. The onPrepareOptionsMenu executes when you press the menu button so your logic to animate it that time is correct.
If its just the text you want to show in menu item, it should go like this,
final MenuItem photo;
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_my_report, menu);
photo = menu.findItem(R.id.action1);
TextView textView = new TextView(this);
textView.setText("I am menu item");
photo.setActionView(textView);
return true;
}
#Override
public boolean onPrepareOptionsMenu(Menu menu) {
if(someCondition)
{
photo.getActionView().animate().alpha(1.0f);
}
return super.onCreateOptionsMenu(menu);
}
In case you want to have the a complex and customise text you can set it using the layoutInflator service. This could go in your onCreate,
LayoutInflater inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
ImageView view = (ImageView)inflater.inflate(R.layout.some_view, null);
Animation rotation = AnimationUtils.loadAnimation(this, R.anim.fade);
and onCreateOptionMenu,
view.startAnimation(rotation);
photo.setActionView(view);
Its just to get you an idea what needed to be done, you can play around with this and can suit your need.

Formatting Menu for Edittext in Android

I trying to create a note apps that have an ability to format the selected text in EditText like the one I usually use in Onenote. But everytime I click my menu icon, the selected text in EditText is resetted and the menu is closed itself.
This is my code in my custom Edit Text
public void init()
{
this.setCustomSelectionActionModeCallback(new ActionMode.Callback() {
#Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
MenuInflater inflater = mode.getMenuInflater();
inflater.inflate(R.menu.edit_text_menu, menu);
return true;
}
#Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return false;
}
#Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
return true;
}
#Override
public void onDestroyActionMode(ActionMode mode) {
}
});
}
Menu Layout
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="#+id/action_bold"
android:title="Bold"/>
<item android:id="#+id/action_underline"
android:title="Underline"/>
<item android:id="#+id/action_italic"
android:title="Italic"/>
</menu>
the screenshot of onenote menu
http://i57.tinypic.com/zlriu9.png
and screenshot of my apps
http://i58.tinypic.com/2yjwlys.jpg
What is the right way to make a menu for EditText to format just the selected text?
Update! Already found a solution that I think work this far
I just need to override the onWindowFocusChanged function, so when I click another menu, it won't lose its selection.

Android: how to detect click on disabled menu item?

I have some items in an ActionBar sub menu which I would like to conditionally disable, because they are not applicable for some contexts. I would like them to appear disabled (greyed out) to discourage users from clicking them; but if clicked, I would like to show a toast informing the user of why it is disabled ("can't do A because there is no B", etc). However, if I call MenuItem.setEnabled(false), it seems all the menu item click events do not occur. So, how do I detect click on the disabled menu item?
public class Temp extends Activity implements OnMenuItemClickListener
{
boolean mConditional = true;
protected void onCreate(Bundle state)
{
super.onCreate(state);
}
public boolean onMenuOpened(int featureId, Menu menu)
{
MenuItem item = (MenuItem) menu.findItem(R.id.item2);
if(item != null && mConditional)
{
item.setEnabled(false);
item.setOnMenuItemClickListener(this);
}
return super.onMenuOpened(featureId, menu);
}
#Override
public boolean onMenuItemClick(MenuItem item)
{
//does not fire if item is disabled
Log.e("", item.getTitle().toString());
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item)
{
//does not fire if item is disabled
Log.e("", item.getTitle().toString());
return super.onOptionsItemSelected(item);
}
#Override
public boolean onMenuItemSelected(int featureId, MenuItem item)
{
//does not fire if item is disabled
Log.e("", item.getTitle().toString());
return super.onMenuItemSelected(featureId, item);
}
#Override
public boolean onCreateOptionsMenu(Menu menu)
{
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.main, menu);
return true;
}
}
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="#+id/menu"
android:icon="#drawable/ic_launcher"
android:showAsAction="always"
android:title="menu">
<menu>
<item
android:id="#+id/item1"
android:showAsAction="always"
android:title="item 1"/>
<item
android:id="#+id/item2"
android:showAsAction="always"
android:title="item 2"/>
<item
android:id="#+id/item3"
android:showAsAction="always"
android:title="item 3"/>
</menu>
</item>
</menu>
You cannot use setEnabled for this. You need to keep menu items enabled all the time and simulate the "disabled" state by changing the drawable (or modifying it using porterduff filter - have a look at this question on how to dim images in android). Separately, you need to keep a flag indicating the state of the item - tag of the menu item is a good option. Ideally, you'd have something like this:
private void setMenuItemEnable(MenuItem item, boolean enabled) {
int curstate = ((Integer)item.getTag()).intValue();
if(curState == 1 && enabled || curstate == 0 && !enabled) {
return;
}
if(enabled) {
... //update image to remove dimming
item.setTag(1);
}
else {
... //update image by dimming it
item.setTag(0);
}
}
Finally, in your onOptionsItemSelected() method, check the tag of the chosen menu item and either perform the action if the tag is 1 or display the toast if the tag is 0. Visually it will be just what you want, and functionally it'll do what you're after.

Categories

Resources