I have been looking for ways to implement a searchview in the activity toolbar (actionbar) as per the material design guidelines.
On clicking on the search icon, the entire toolbar animates to have only the search EditText with white background with suggestions appearing in the main view instead of a drop down.
Here is a screenshot from the guidelines:
Here is a gif from the Gmail Inbox implementation:
I have been looking for code examples and tutorials but so far I have been unsuccesful. How do I go about doing this?
I tried several material SearchView libraries, but none of them worked good as the one from the support library, so I decided to redesign it, after a lot of work, I am pleased with the result:
Here is how you can do it:
1) Add SearchView item to your menu
<item
android:id="#+id/m_search"
android:icon="#drawable/ic_action_search"
android:title="#string/search_title"
app:actionLayout="#layout/search_view_layout"
app:showAsAction="ifRoom|collapseActionView" />
Notice that I'm declaring actionLayout instead of actionViewClass, I figured that this is the only way to set SearchView theme separately from Toolbar theme.
search_view_layout.xml:
<android.support.v7.widget.SearchView
android:id="#+id/search_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="#style/SearchViewTheme" />
2) Add the custom SearchView theme to your styles, declare SearchView theme in your Toolbar theme as well:
<style name="SearchViewTheme" parent="Widget.AppCompat.SearchView.ActionBar">
<item name="layout">#layout/toolbar_search_view</item>
<item name="commitIcon">#drawable/ic_search_commit</item>
<item name="colorControlNormal">#color/material_light_active_icon</item>
<item name="colorControlHighlight">#color/material_ripple_light</item>
<item name="autoCompleteTextViewStyle">#style/AutoCompleteTextViewStyle</item>
<item name="suggestionRowLayout">#layout/search_view_suggestion_row</item>
<item name="android:maxWidth">9999dp</item>
</style>
<style name="AutoCompleteTextViewStyle" parent="Widget.AppCompat.Light.AutoCompleteTextView">
<item name="android:popupBackground">#drawable/search_suggestions_bg</item>
<item name="android:popupElevation">0dp</item>
</style>
<style name="ToolbarTheme" parent="ThemeOverlay.AppCompat.Dark.ActionBar">
<item name="searchViewStyle">#style/SearchViewTheme</item>
</style>
toolbar_search_view.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/search_bar"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:paddingEnd="8dp">
<!-- This is actually used for the badge icon *or* the badge label (or neither) -->
<TextView
android:id="#+id/search_badge"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginBottom="2dp"
android:drawablePadding="0dp"
android:gravity="center_vertical"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="?android:attr/textColorPrimary"
android:visibility="gone" />
<ImageView
android:id="#+id/search_button"
style="?attr/actionButtonStyle"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:contentDescription="#string/abc_searchview_description_search"
android:focusable="true" />
<LinearLayout
android:id="#+id/search_edit_frame"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:layoutDirection="locale"
android:orientation="horizontal">
<ImageView
android:id="#+id/search_mag_icon"
style="#style/RtlOverlay.Widget.AppCompat.SearchView.MagIcon"
android:layout_width="#dimen/abc_dropdownitem_icon_width"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:scaleType="centerInside"
android:visibility="gone" />
<!-- Inner layout contains the app icon, button(s) and EditText -->
<LinearLayout
android:id="#+id/search_plate"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:layout_weight="1"
android:orientation="horizontal">
<view
android:id="#+id/search_src_text"
class="android.support.v7.widget.SearchView$SearchAutoComplete"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:layout_marginEnd="#dimen/item_list_horizontal_margin"
android:layout_marginStart="#dimen/item_list_horizontal_margin"
android:layout_weight="1"
android:background="#null"
android:dropDownAnchor="#id/anchor_dropdown"
android:dropDownHeight="wrap_content"
android:dropDownHorizontalOffset="0dp"
android:dropDownVerticalOffset="0dp"
android:ellipsize="end"
android:imeOptions="actionSearch"
android:inputType="text|textAutoComplete|textNoSuggestions"
android:maxLines="1"
android:paddingEnd="8dp"
android:textColor="#android:color/black"
android:textColorHint="#color/material_light_hint_text"
android:textSize="20sp" />
<ImageView
android:id="#+id/search_close_btn"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="#string/abc_searchview_description_clear"
android:focusable="true"
android:paddingEnd="8dp"
android:paddingStart="8dp" />
</LinearLayout>
<LinearLayout
android:id="#+id/submit_area"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal">
<ImageView
android:id="#+id/search_go_btn"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="#string/abc_searchview_description_submit"
android:focusable="true"
android:paddingEnd="8dp"
android:paddingStart="8dp"
android:visibility="gone" />
<ImageView
android:id="#+id/search_voice_btn"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="#string/abc_searchview_description_voice"
android:focusable="true"
android:paddingEnd="8dp"
android:paddingStart="8dp"
android:visibility="gone" />
</LinearLayout>
</LinearLayout>
Notice that I added anchor dropdown view under the Toolbar view, so suggestions will get full screen width.
<android.support.design.widget.AppBarLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/appBar"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:collapseIcon="#drawable/ic_search_collapse"
app:popupTheme="#style/AppTheme.PopupOverlay"
app:theme="#style/ToolbarTheme" />
<View
android:id="#+id/anchor_dropdown"
android:layout_width="match_parent"
android:layout_height="0dp" />
</android.support.design.widget.AppBarLayout>
search_view_suggestion_row.xml:
(change suggestion_divider visibility if you want divider between suggestions):
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="58dp"
android:theme="#style/Theme.AppCompat.DayNight">
<!-- Icons come first in the layout, since their placement doesn't depend on
the placement of the text views. -->
<ImageView
android:id="#android:id/icon1"
style="#style/RtlOverlay.Widget.AppCompat.Search.DropDown.Icon1"
android:layout_width="56dp"
android:layout_height="56dp"
android:layout_alignParentBottom="true"
android:layout_alignParentTop="true"
android:scaleType="centerInside"
android:visibility="invisible" />
<ImageView
android:id="#+id/edit_query"
style="#style/RtlOverlay.Widget.AppCompat.Search.DropDown.Query"
android:layout_width="56dp"
android:layout_height="56dp"
android:layout_alignParentBottom="true"
android:layout_alignParentTop="true"
android:background="?attr/selectableItemBackground"
android:scaleType="centerInside"
android:visibility="gone" />
<ImageView
android:id="#id/android:icon2"
style="#style/RtlOverlay.Widget.AppCompat.Search.DropDown.Icon2"
android:layout_width="56dp"
android:layout_height="56dp"
android:layout_alignParentBottom="true"
android:layout_alignParentTop="true"
android:layout_alignWithParentIfMissing="true"
android:scaleType="centerInside"
android:visibility="gone" />
<!-- The subtitle comes before the title, since the height of the title depends on whether the
subtitle is visible or gone. -->
<TextView
android:id="#android:id/text2"
style="?android:attr/dropDownItemStyle"
android:layout_width="match_parent"
android:layout_height="29dp"
android:layout_alignParentBottom="true"
android:layout_alignWithParentIfMissing="true"
android:gravity="top"
android:maxLines="1"
android:paddingBottom="4dp"
android:textColor="?android:textColorSecondary"
android:textSize="12sp"
android:visibility="gone" />
<!-- The title is placed above the subtitle, if there is one. If there is no
subtitle, it fills the parent. -->
<TextView
android:id="#android:id/text1"
style="?android:attr/dropDownItemStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="#android:id/text2"
android:layout_centerVertical="true"
android:ellipsize="end"
android:maxLines="1"
android:scrollHorizontally="false"
android:textColor="?android:textColorPrimary"
android:textSize="16sp" />
<View
android:id="#+id/suggestion_divider"
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:layout_alignParentBottom="true"
android:layout_alignStart="#android:id/text1"
android:layout_marginStart="8dp"
android:background="#color/divider_color"
android:visibility="gone" />
The suggestions background and the commit icon are custom made, the rest of the icons I used can be found at: https://material.io/icons/
ic_search_commit.xml:
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:autoMirrored="true"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#color/active_icon_color"
android:pathData="m18.364,16.95l-8.605,-8.605l7.905,-0l-0.007,-2.001l-11.314,0l0,11.314l1.994,-0l0.007,-7.898l8.605,8.605l1.414,-1.414z" />
search_suggestions_bg.xml:
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="rectangle">
<padding android:top="0.5dp" />
<stroke
android:width="0.5dp"
android:color="#color/divider_color" />
</shape>
</item>
<item>
<shape android:shape="rectangle">
<solid android:color="#color/cards_and_dialogs_color" />
</shape>
</item>
</layer-list>
Add following values to your colors.xml (add values-night only if you are using DayNight theme):
values/colors.xml
<color name="material_light_primary_text">#DE000000</color>
<color name="material_light_hint_text">#61000000</color>
<color name="material_light_active_icon">#8A000000</color>
<color name="material_ripple_light">#1F000000</color>
<color name="divider_color">#1F000000</color>
<color name="active_icon_color">#8A000000</color>
<color name="cards_and_dialogs_color">#android:color/white</color>
<color name="quantum_grey_600">#757575</color>
values-night/colors.xml:
<color name="divider_color">#1FFFFFFF</color>
<color name="active_icon_color">#android:color/white</color>
<color name="cards_and_dialogs_color">#424242</color>
3) Last part, make the magic happen in code:
Setup and initialize SearchView in your desired activity
private MenuItem mSearchItem;
private Toolbar mToolbar;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
mToolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(mToolbar);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
mSearchItem = menu.findItem(R.id.m_search);
MenuItemCompat.setOnActionExpandListener(mSearchItem, new MenuItemCompat.OnActionExpandListener() {
#Override
public boolean onMenuItemActionCollapse(MenuItem item) {
// Called when SearchView is collapsing
if (mSearchItem.isActionViewExpanded()) {
animateSearchToolbar(1, false, false);
}
return true;
}
#Override
public boolean onMenuItemActionExpand(MenuItem item) {
// Called when SearchView is expanding
animateSearchToolbar(1, true, true);
return true;
}
});
return true;
}
public void animateSearchToolbar(int numberOfMenuIcon, boolean containsOverflow, boolean show) {
mToolbar.setBackgroundColor(ContextCompat.getColor(this, android.R.color.white));
mDrawerLayout.setStatusBarBackgroundColor(ContextCompat.getColor(this, R.color.quantum_grey_600));
if (show) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
int width = mToolbar.getWidth() -
(containsOverflow ? getResources().getDimensionPixelSize(R.dimen.abc_action_button_min_width_overflow_material) : 0) -
((getResources().getDimensionPixelSize(R.dimen.abc_action_button_min_width_material) * numberOfMenuIcon) / 2);
Animator createCircularReveal = ViewAnimationUtils.createCircularReveal(mToolbar,
isRtl(getResources()) ? mToolbar.getWidth() - width : width, mToolbar.getHeight() / 2, 0.0f, (float) width);
createCircularReveal.setDuration(250);
createCircularReveal.start();
} else {
TranslateAnimation translateAnimation = new TranslateAnimation(0.0f, 0.0f, (float) (-mToolbar.getHeight()), 0.0f);
translateAnimation.setDuration(220);
mToolbar.clearAnimation();
mToolbar.startAnimation(translateAnimation);
}
} else {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
int width = mToolbar.getWidth() -
(containsOverflow ? getResources().getDimensionPixelSize(R.dimen.abc_action_button_min_width_overflow_material) : 0) -
((getResources().getDimensionPixelSize(R.dimen.abc_action_button_min_width_material) * numberOfMenuIcon) / 2);
Animator createCircularReveal = ViewAnimationUtils.createCircularReveal(mToolbar,
isRtl(getResources()) ? mToolbar.getWidth() - width : width, mToolbar.getHeight() / 2, (float) width, 0.0f);
createCircularReveal.setDuration(250);
createCircularReveal.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
mToolbar.setBackgroundColor(getThemeColor(MainActivity.this, R.attr.colorPrimary));
mDrawerLayout.setStatusBarBackgroundColor(getThemeColor(MainActivity.this, R.attr.colorPrimaryDark));
}
});
createCircularReveal.start();
} else {
AlphaAnimation alphaAnimation = new AlphaAnimation(1.0f, 0.0f);
Animation translateAnimation = new TranslateAnimation(0.0f, 0.0f, 0.0f, (float) (-mToolbar.getHeight()));
AnimationSet animationSet = new AnimationSet(true);
animationSet.addAnimation(alphaAnimation);
animationSet.addAnimation(translateAnimation);
animationSet.setDuration(220);
animationSet.setAnimationListener(new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
}
#Override
public void onAnimationEnd(Animation animation) {
mToolbar.setBackgroundColor(getThemeColor(MainActivity.this, R.attr.colorPrimary));
}
#Override
public void onAnimationRepeat(Animation animation) {
}
});
mToolbar.startAnimation(animationSet);
}
mDrawerLayout.setStatusBarBackgroundColor(getThemeColor(MainActivity.this, R.attr.colorPrimaryDark));
}
}
private boolean isRtl(Resources resources) {
return resources.getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
}
private static int getThemeColor(Context context, int id) {
Resources.Theme theme = context.getTheme();
TypedArray a = theme.obtainStyledAttributes(new int[]{id});
int result = a.getColor(0, 0);
a.recycle();
return result;
}
Few things to notice about the code:
1) The animation will adjust it's start point based on your set of number of menu items and if the toolbar has overflow icon, it will detect if layout is LTR or RTL automatically.
2) I'm using navigation drawer activity, so I set StatusBar color to mDrawerLayout, if you are using regular activity, you can set StatusBar color this way:
getWindow().setStatusBarColor(ContextCompat.getColor(this, R.color.quantum_grey_600));
3) The circular reveal animation will only work on KitKat and above.
It is actually quite easy to do this, if you are using android.support.v7 library.
Step - 1
Declare a menu item
<item android:id="#+id/action_search"
android:title="Search"
android:icon="#drawable/abc_ic_search_api_mtrl_alpha"
app:showAsAction="ifRoom|collapseActionView"
app:actionViewClass="android.support.v7.widget.SearchView" />
Step - 2
Extend AppCompatActivity and in the onCreateOptionsMenu setup the SearchView.
import android.support.v7.widget.SearchView;
public class YourActivity extends AppCompatActivity {
...
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_home, menu);
// Retrieve the SearchView and plug it into SearchManager
final SearchView searchView = (SearchView) MenuItemCompat.getActionView(menu.findItem(R.id.action_search));
SearchManager searchManager = (SearchManager) getSystemService(SEARCH_SERVICE);
searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
return true;
}
...
}
The idea is very simple - you have to write your own AutoCompleteTextView using EditText, TextWatcher and RecyclerView with Filterable adapter.
EditText gives you a text field with ability to input characters
TextWatcher allows you to watch for text changes
RecyclerView can be placed anywhere, so you can show the search results just like on your screenshot
Filterable adapter helps to present data filtered with the entered text
So:
make a layout with EditText on the top, with RecyclerView filling the remaining space. Add the icon, shadow, etc.
add a TextWatcher and update the adapter on each text change
If you'd like to see my solution in action, check out my project on github:
https://github.com/ZieIony/Carbon
The Auto complete demo can be sound in the sample app in 'Demos' section.
Taking a hint from #Zielony's answer I did the following:
1) Instead if using an ActionBar or ToolBar I built my own layout (basically a RelativeLayout with burger menu, search and other menu buttons and a EditText for search)
2) Used a theme without an ActionBar, placed my custom layout at the top of the activity so that it appeared like an ActionBar.
3) In the search button's OnClickListener I do 2 things:
Hide the menu buttons and show the 'search' EditText.
Add a fragment to display search suggestions and search
Show the soft keyboard input
3) Added OnClickListeners for the other menu buttons.
4) Added a TextWatcher on the 'search' EditText to display search hints and results from the server.
This is how it appears now:
I think I've figured it out.
I'm now using just an EditText inside of the Toolbar.
I now have this:
First inside onCreate() of my activity I added the EditText with an image view on the right hand side to the Toolbar like this:
// Setup search container view
searchContainer = new LinearLayout(this);
Toolbar.LayoutParams containerParams = new Toolbar.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
containerParams.gravity = Gravity.CENTER_VERTICAL;
searchContainer.setLayoutParams(containerParams);
// Setup search view
toolbarSearchView = new EditText(this);
// Set width / height / gravity
int[] textSizeAttr = new int[]{android.R.attr.actionBarSize};
int indexOfAttrTextSize = 0;
TypedArray a = obtainStyledAttributes(new TypedValue().data, textSizeAttr);
int actionBarHeight = a.getDimensionPixelSize(indexOfAttrTextSize, -1);
a.recycle();
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(0, actionBarHeight);
params.gravity = Gravity.CENTER_VERTICAL;
params.weight = 1;
toolbarSearchView.setLayoutParams(params);
// Setup display
toolbarSearchView.setBackgroundColor(Color.TRANSPARENT);
toolbarSearchView.setPadding(2, 0, 0, 0);
toolbarSearchView.setTextColor(Color.WHITE);
toolbarSearchView.setGravity(Gravity.CENTER_VERTICAL);
toolbarSearchView.setSingleLine(true);
toolbarSearchView.setImeActionLabel("Search", EditorInfo.IME_ACTION_UNSPECIFIED);
toolbarSearchView.setHint("Search");
toolbarSearchView.setHintTextColor(Color.parseColor("#b3ffffff"));
try {
// Set cursor colour to white
// http://stackoverflow.com/a/26544231/1692770
// https://github.com/android/platform_frameworks_base/blob/kitkat-release/core/java/android/widget/TextView.java#L562-564
Field f = TextView.class.getDeclaredField("mCursorDrawableRes");
f.setAccessible(true);
f.set(toolbarSearchView, R.drawable.edittext_whitecursor);
} catch (Exception ignored) {
}
// Search text changed listener
toolbarSearchView.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
Fragment mainFragment = getFragmentManager().findFragmentById(R.id.container);
if (mainFragment != null && mainFragment instanceof MainListFragment) {
((MainListFragment) mainFragment).search(s.toString());
}
}
#Override
public void afterTextChanged(Editable s) {
// http://stackoverflow.com/a/6438918/1692770
if (s.toString().length() <= 0) {
toolbarSearchView.setHintTextColor(Color.parseColor("#b3ffffff"));
}
}
});
((LinearLayout) searchContainer).addView(toolbarSearchView);
// Setup the clear button
searchClearButton = new ImageView(this);
Resources r = getResources();
int px = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 16, r.getDisplayMetrics());
LinearLayout.LayoutParams clearParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
clearParams.gravity = Gravity.CENTER;
searchClearButton.setLayoutParams(clearParams);
searchClearButton.setImageResource(R.drawable.ic_close_white_24dp); // TODO: Get this image from here: https://github.com/google/material-design-icons
searchClearButton.setPadding(px, 0, px, 0);
searchClearButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
toolbarSearchView.setText("");
}
});
((LinearLayout) searchContainer).addView(searchClearButton);
// Add search view to toolbar and hide it
searchContainer.setVisibility(View.GONE);
toolbar.addView(searchContainer);
This worked, but then I came across an issue where onOptionsItemSelected() wasn't being called when I tapped on the home button. So I wasn't able to cancel the search by pressing the home button. I tried a few different ways of registering the click listener on the home button but they didn't work.
Eventually I found out that the ActionBarDrawerToggle I had was interfering with things, so I removed it. This listener then started working:
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// toolbarHomeButtonAnimating is a boolean that is initialized as false. It's used to stop the user pressing the home button while it is animating and breaking things.
if (!toolbarHomeButtonAnimating) {
// Here you'll want to check if you have a search query set, if you don't then hide the search box.
// My main fragment handles this stuff, so I call its methods.
FragmentManager fragmentManager = getFragmentManager();
final Fragment fragment = fragmentManager.findFragmentById(R.id.container);
if (fragment != null && fragment instanceof MainListFragment) {
if (((MainListFragment) fragment).hasSearchQuery() || searchContainer.getVisibility() == View.VISIBLE) {
displaySearchView(false);
return;
}
}
}
if (mDrawerLayout.isDrawerOpen(findViewById(R.id.navigation_drawer)))
mDrawerLayout.closeDrawer(findViewById(R.id.navigation_drawer));
else
mDrawerLayout.openDrawer(findViewById(R.id.navigation_drawer));
}
});
So I can now cancel the search with the home button, but I can't press the back button to cancel it yet. So I added this to onBackPressed():
FragmentManager fragmentManager = getFragmentManager();
final Fragment mainFragment = fragmentManager.findFragmentById(R.id.container);
if (mainFragment != null && mainFragment instanceof MainListFragment) {
if (((MainListFragment) mainFragment).hasSearchQuery() || searchContainer.getVisibility() == View.VISIBLE) {
displaySearchView(false);
return;
}
}
I created this method to toggle visibility of the EditText and menu item:
public void displaySearchView(boolean visible) {
if (visible) {
// Stops user from being able to open drawer while searching
mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
// Hide search button, display EditText
menu.findItem(R.id.action_search).setVisible(false);
searchContainer.setVisibility(View.VISIBLE);
// Animate the home icon to the back arrow
toggleActionBarIcon(ActionDrawableState.ARROW, mDrawerToggle, true);
// Shift focus to the search EditText
toolbarSearchView.requestFocus();
// Pop up the soft keyboard
new Handler().postDelayed(new Runnable() {
public void run() {
toolbarSearchView.dispatchTouchEvent(MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), MotionEvent.ACTION_DOWN, 0, 0, 0));
toolbarSearchView.dispatchTouchEvent(MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, 0, 0, 0));
}
}, 200);
} else {
// Allows user to open drawer again
mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED);
// Hide the EditText and put the search button back on the Toolbar.
// This sometimes fails when it isn't postDelayed(), don't know why.
toolbarSearchView.postDelayed(new Runnable() {
#Override
public void run() {
toolbarSearchView.setText("");
searchContainer.setVisibility(View.GONE);
menu.findItem(R.id.action_search).setVisible(true);
}
}, 200);
// Turn the home button back into a drawer icon
toggleActionBarIcon(ActionDrawableState.BURGER, mDrawerToggle, true);
// Hide the keyboard because the search box has been hidden
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(toolbarSearchView.getWindowToken(), 0);
}
}
I needed a way to toggle the home button on the toolbar between the drawer icon and the back button. I eventually found the method below in this SO answer. Though I modified it slightly to made more sense to me:
private enum ActionDrawableState
{
BURGER, ARROW
}
private void toggleActionBarIcon(final ActionDrawableState state, final ActionBarDrawerToggle toggle, boolean animate) {
if (animate) {
float start = state == ActionDrawableState.BURGER ? 1.0f : 0f;
float end = Math.abs(start - 1);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
ValueAnimator offsetAnimator = ValueAnimator.ofFloat(start, end);
offsetAnimator.setDuration(300);
offsetAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
offsetAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
float offset = (Float) animation.getAnimatedValue();
toggle.onDrawerSlide(null, offset);
}
});
offsetAnimator.addListener(new Animator.AnimatorListener() {
#Override
public void onAnimationStart(Animator animation) {
}
#Override
public void onAnimationEnd(Animator animation) {
toolbarHomeButtonAnimating = false;
}
#Override
public void onAnimationCancel(Animator animation) {
}
#Override
public void onAnimationRepeat(Animator animation) {
}
});
toolbarHomeButtonAnimating = true;
offsetAnimator.start();
}
} else {
if (state == ActionDrawableState.BURGER) {
toggle.onDrawerClosed(null);
} else {
toggle.onDrawerOpened(null);
}
}
}
This works, I've managed to work out a few bugs that I found along the way. I don't think it's 100% but it works well enough for me.
EDIT: If you want to add the search view in XML instead of Java do this:
toolbar.xml:
<android.support.v7.widget.Toolbar
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/toolbar"
contentInsetLeft="72dp"
contentInsetStart="72dp"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:elevation="4dp"
android:minHeight="?attr/actionBarSize"
app:contentInsetLeft="72dp"
app:contentInsetStart="72dp"
app:popupTheme="#style/ActionBarPopupThemeOverlay"
app:theme="#style/ActionBarThemeOverlay">
<LinearLayout
android:id="#+id/search_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:orientation="horizontal">
<EditText
android:id="#+id/search_view"
android:layout_width="0dp"
android:layout_height="?attr/actionBarSize"
android:layout_weight="1"
android:background="#android:color/transparent"
android:gravity="center_vertical"
android:hint="Search"
android:imeOptions="actionSearch"
android:inputType="text"
android:maxLines="1"
android:paddingLeft="2dp"
android:singleLine="true"
android:textColor="#ffffff"
android:textColorHint="#b3ffffff" />
<ImageView
android:id="#+id/search_clear"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:src="#drawable/ic_close_white_24dp" />
</LinearLayout>
onCreate() of your Activity:
searchContainer = findViewById(R.id.search_container);
toolbarSearchView = (EditText) findViewById(R.id.search_view);
searchClearButton = (ImageView) findViewById(R.id.search_clear);
// Setup search container view
try {
// Set cursor colour to white
// http://stackoverflow.com/a/26544231/1692770
// https://github.com/android/platform_frameworks_base/blob/kitkat-release/core/java/android/widget/TextView.java#L562-564
Field f = TextView.class.getDeclaredField("mCursorDrawableRes");
f.setAccessible(true);
f.set(toolbarSearchView, R.drawable.edittext_whitecursor);
} catch (Exception ignored) {
}
// Search text changed listener
toolbarSearchView.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
Fragment mainFragment = getFragmentManager().findFragmentById(R.id.container);
if (mainFragment != null && mainFragment instanceof MainListFragment) {
((MainListFragment) mainFragment).search(s.toString());
}
}
#Override
public void afterTextChanged(Editable s) {
}
});
// Clear search text when clear button is tapped
searchClearButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
toolbarSearchView.setText("");
}
});
// Hide the search view
searchContainer.setVisibility(View.GONE);
Here is how I tried to implement it, please check this out.
https://github.com/Shahroz16/material-searchview
You can use AutoCompleteTextView to achieve this, Follow the link below
How to build Gmail like search box in the action bar?
Related
I have bottomsheet fragment what shows to user answears to his comment. At the bottom of bottom sheet we have edit text, where user can add new comment. So, when soft keyboard opened the bottomsheet staes above keyboard and its top moved far beyond the screen. But bottom sheet should to resize when keyboard is open.
Here is my code:
<android.support.design.widget.CoordinatorLayout
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"
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<include android:id="#+id/toolbar_layout"
layout="#layout/partial_toolbar" />
<android.support.v7.widget.RecyclerView
android:id="#+id/list_comments"
android:layout_below="#+id/toolbar_layout"
android:layout_width="match_parent"
android:overScrollMode="always"
android:layout_height="match_parent"/>
<View
android:layout_width="match_parent"
android:layout_height="3dp"
android:layout_above="#+id/comment_container"
android:layout_alignBaseline="#+id/list_comments"
android:background="#drawable/elevation_bottom"/>
<LinearLayout
android:id="#+id/comment_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#ebffffff"
android:layout_alignParentBottom="true"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingBottom="#dimen/vertical_spacing"
android:paddingEnd="#dimen/horizontal_spacing"
android:paddingStart="8dp"
android:paddingTop="#dimen/vertical_spacing">
<android.support.v7.widget.AppCompatImageButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#color/transparent"
android:src="#drawable/ic_add_blue_small"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:background="#drawable/bg_message_field"
android:gravity="center_vertical"
android:orientation="horizontal">
<android.support.v7.widget.AppCompatEditText
android:id="#+id/edit_comment"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="#color/transparent"
android:hint="#string/comments_add_comment_hint"
android:inputType="textMultiLine"
android:maxLength="200"
android:maxLines="3"
android:minLines="1"
android:paddingBottom="8dp"
android:paddingEnd="0dp"
android:paddingStart="8dp"
android:paddingTop="8dp"
android:textSize="14sp"
app:fontFamily="#font/lora_regular"/>
<android.support.v7.widget.AppCompatImageButton
android:id="#+id/button_send"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:background="#color/transparent"
app:srcCompat="#drawable/ic_send_message"/>
</LinearLayout>
</LinearLayout>
</RelativeLayout>
</android.support.design.widget.CoordinatorLayout>
Activity at manifest (this activity opens dialog)
<activity
android:name=".presentation.ui.category.CategoryDetailsActivity"
android:configChanges="locale|orientation|screenSize|keyboard"
android:windowSoftInputMode="adjustResize"
android:windowTranslucentNavigation="true"
android:windowTranslucentStatus="true"
android:launchMode="singleTop"
android:screenOrientation="portrait"
android:theme="#style/AppTheme.Main.NoActionBar" />
Also I put this code to method setupDialog
dialog.getWindow().setSoftInputMode(
WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
And screenshots
Got this answer from here
As I too had the same issue in one of my projects,
Use this in onCreateDialog in BottomSheetFragment
new KeyboardUtil(getActivity(), rootView);
By using the below class
public class KeyboardUtil {
private View decorView;
private View contentView;
//a small helper to allow showing the editText focus
ViewTreeObserver.OnGlobalLayoutListener onGlobalLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
Rect r = new Rect();
//r will be populated with the coordinates of your view that area still visible.
decorView.getWindowVisibleDisplayFrame(r);
//get screen height and calculate the difference with the useable area from the r
int height = decorView.getContext().getResources().getDisplayMetrics().heightPixels;
int diff = height - r.bottom;
//if it could be a keyboard add the padding to the view
if (diff != 0) {
// if the use-able screen height differs from the total screen height we assume that it shows a keyboard now
//check if the padding is 0 (if yes set the padding for the keyboard)
if (contentView.getPaddingBottom() != diff) {
//set the padding of the contentView for the keyboard
contentView.setPadding(0, 0, 0, diff);
}
} else {
//check if the padding is != 0 (if yes reset the padding)
if (contentView.getPaddingBottom() != 0) {
//reset the padding of the contentView
contentView.setPadding(0, 0, 0, 0);
}
}
}
};
public KeyboardUtil(Activity act, View contentView) {
this.decorView = act.getWindow().getDecorView();
this.contentView = contentView;
//only required on newer android versions. it was working on API level 19
if (Build.VERSION.SDK_INT >= 19) {
decorView.getViewTreeObserver().addOnGlobalLayoutListener(onGlobalLayoutListener);
}
}
/**
* Helper to hide the keyboard
*
* #param act
*/
public static void hideKeyboard(Activity act) {
if (act != null && act.getCurrentFocus() != null) {
InputMethodManager inputMethodManager = (InputMethodManager) act.getSystemService(Activity.INPUT_METHOD_SERVICE);
inputMethodManager.hideSoftInputFromWindow(act.getCurrentFocus().getWindowToken(), 0);
}
}
public void enable() {
if (Build.VERSION.SDK_INT >= 19) {
decorView.getViewTreeObserver().addOnGlobalLayoutListener(onGlobalLayoutListener);
}
}
public void disable() {
if (Build.VERSION.SDK_INT >= 19) {
decorView.getViewTreeObserver().removeOnGlobalLayoutListener(onGlobalLayoutListener);
}
}
}
I ended up getting a satisfactory result with the following code:
use this in setupDialog(dialog: Dialog, style: Int) method in BottomSheetFragment
dialog.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)
dialog.setOnShowListener {
val bottomSheetDialog = dialog as BottomSheetDialog
val bottomSheet = bottomSheetDialog.findViewById<View>(android.support.design.R.id.design_bottom_sheet) as FrameLayout?
BottomSheetBehavior.from(bottomSheet!!).state = BottomSheetBehavior.STATE_EXPANDED
}
I have created a DrawerLayout with a right side drawer. So far, so good. Now, there's one menu item which, when clicked opens a pop-out menu with 5 different options (see screenshot). When one of the options is clicked the icon in the main menu should get swapped for the icon that I just clicked and then the main menu should close. However, I have not been able to create that behaviour. Currently when I try to click one of the options in the pop-out menu the following happens:
1. The main menu (the drawer) closes.
2. Only if I click the desired option again the icon in the main menu gets swapped.
As far as I understand this it is the default behaviour of a drawer within a DrawerView to be closed as soon as I click somewhere in the screen. I have tried to work around it by setting the lock mode:
myDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_OPEN, Gravity.END)
... but no luck. The drawer closes as soon as I try to click one of the items in the pop-out.
Is it even possible to achieve what I want in a DrawerLayout?
Here's my code:
activity_main.xml
<android.support.v4.widget.DrawerLayout
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"
android:id="#+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="net.videosc2.activities.VideOSCMainActivity">
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/camera_preview"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ImageView
android:id="#+id/camera_downscaled"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:antialias="false"
android:contentDescription="#string/preview_image_content_description"
android:scaleType="fitXY"/>
</FrameLayout>
<!-- The navigation drawer that comes from the right (layout_gravity:end) -->
<ListView
android:id="#+id/drawer"
android:layout_width="50dp"
android:layout_height="match_parent"
android:layout_gravity="center_vertical|end"
android:background="#99000000"
android:choiceMode="singleChoice"
android:divider="#android:color/transparent"
android:dividerHeight="10dp"
app:itemTextColor="#android:color/white"/>
</android.support.v4.widget.DrawerLayout>
drawer_item.xml
<?xml version="1.0" encoding="utf-8"?>
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/tool"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/activatedBackgroundIndicator"
android:contentDescription="#string/a_tools_menu_item"
android:gravity="center_vertical"
android:paddingBottom="8dp"
android:paddingLeft="8dp"
android:paddingRight="8dp"
android:paddingTop="8dp"/>
color_mode_panel.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/color_mode_panel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:layout_marginEnd="60dp"
android:layout_marginRight="60dp"
android:layout_marginTop="100dp"
android:background="#99000000"
android:clickable="true"
android:orientation="horizontal">
<ImageButton
android:id="#+id/mode_rgb"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#00000000"
android:contentDescription="#string/color_mode_rgb"
android:padding="8dp"
android:src="#drawable/rgb"/>
<ImageButton
android:id="#+id/mode_rgb_inv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#00000000"
android:clickable="true"
android:contentDescription="#string/color_mode_rgb_neg"
android:padding="8dp"
android:src="#drawable/rgb_inv"/>
<ImageButton
android:id="#+id/mode_r"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#00000000"
android:contentDescription="#string/color_mode_r"
android:padding="8dp"
android:src="#drawable/r"/>
<ImageButton
android:id="#+id/mode_g"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#00000000"
android:contentDescription="#string/color_mode_g"
android:padding="8dp"
android:src="#drawable/g"/>
<ImageButton
android:id="#+id/mode_b"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#00000000"
android:contentDescription="#string/color_mode_b"
android:padding="8dp"
android:src="#drawable/b"/>
</LinearLayout>
MainActivity.java
public class MainActivity extends AppCompatActivity {
static final String TAG = "MainActivity";
View camView;
public static Point dimensions;
private DrawerLayout toolsDrawerLayout;
private FrameLayout mainFrame;
private ActionBarDrawerToggle drawerToggle;
protected ArrayList<View> uiElements = new ArrayList<>();
// is device currently sending OSC?
public boolean isPlaying = false;
// is flashlight on?
public boolean isTorchOn = false;
// don't create more than one color mode panel
private boolean isColorModePanelOpen = false;
public Fragment cameraPreview;
Camera camera;
/**
* Fragment managing the behaviors, interactions and presentation of the navigation drawer.
*/
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.activity_main);
final FragmentManager fragmentManager = getFragmentManager();
if (findViewById(R.id.camera_preview) != null) {
camView = findViewById(R.id.camera_preview);
if (savedInstanceState != null) return;
cameraPreview = new VideOSCCameraFragment();
fragmentManager.beginTransaction()
.replace(R.id.camera_preview, cameraPreview, "CamPreview")
.commit();
}
TypedArray tools = getResources().obtainTypedArray(drawer_icons_id);
toolsDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
toolsDrawerLayout.setScrimColor(Color.TRANSPARENT);
// FIXME: touches seem to get swallowed by the DrawerLayout first
final ListView toolsDrawerList = (ListView) findViewById(R.id.drawer);
List<BitmapDrawable> toolsList = new ArrayList<>();
for (int i = 0; i < tools.length(); i++) {
toolsList.add((BitmapDrawable) tools.getDrawable(i));
}
// set the drawer menu in a custom Adapter
toolsDrawerList.setAdapter(new ToolsMenuAdapter(this, R.layout.drawer_item, R.id.tool, toolsList));
tools.recycle();
toolsDrawerList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
BitmapDrawable img;
final ImageView imgView = (ImageView) view.findViewById(R.id.tool);
Context context = getApplicationContext();
// we can not use 'cameraPreview' to retrieve the 'mCamera' object
VideOSCCameraFragment camPreview = (VideOSCCameraFragment) fragmentManager.findFragmentByTag("CamPreview");
camera = camPreview.mCamera;
LayoutInflater inflater = getLayoutInflater();
switch (i) {
// ... other cases...
case 2:
if (!isColorModePanelOpen) {
int y = (int) view.getY();
// create pop-out
final View modePanel = inflater.inflate(R.layout.color_mode_panel, (FrameLayout) camView, true);
isColorModePanelOpen = true;
// try to lock the main menu drawer - didn't work for me
toolsDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_OPEN, toolsDrawerList);
final View modePanelInner = modePanel.findViewById(R.id.color_mode_panel);
// set the vertical position of the pop-out
ActivityHelpers.setMargins(modePanelInner, 0, y, 0, 0);
// set actions for each item in the pop-out
for (int k = 0; k < ((ViewGroup) modePanelInner).getChildCount(); k++) {
final Context iContext = context;
View button = ((ViewGroup) modePanelInner).getChildAt(k);
button.setOnClickListener(new View.OnClickListener() {
#Override
// FIXME: touches seem to get swallowed by the DrawerLayout first
public void onClick(View view) {
switch (view.getId()) {
case R.id.mode_rgb:
Log.d(TAG, "rgb");
imgView.setImageDrawable(ContextCompat.getDrawable(iContext, R.drawable.rgb));
break;
case R.id.mode_rgb_inv:
Log.d(TAG, "rgb inverted");
imgView.setImageDrawable(ContextCompat.getDrawable(iContext, R.drawable.rgb_inv));
break;
case R.id.mode_r:
Log.d(TAG, "red");
imgView.setImageDrawable(ContextCompat.getDrawable(iContext, R.drawable.r));
break;
case R.id.mode_g:
Log.d(TAG, "green");
imgView.setImageDrawable(ContextCompat.getDrawable(iContext, R.drawable.g));
break;
case R.id.mode_b:
Log.d(TAG, "blue");
imgView.setImageDrawable(ContextCompat.getDrawable(iContext, R.drawable.b));
break;
default:
imgView.setImageDrawable(ContextCompat.getDrawable(iContext, R.drawable.rgb));
}
// remove the pop-out
((ViewGroup) modePanelInner.getParent()).removeView(modePanelInner);
isColorModePanelOpen = false;
// unlock the drawer again
toolsDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, Gravity.END);
// now the drawer should finally get closed
toolsDrawerLayout.closeDrawer(Gravity.END);
}
});
}
}
break;
// ... other cases ...
}
});
// open drawer on application start
toolsDrawerLayout.openDrawer(Gravity.END);
}
}
}
Edit:
I found this post that offered me a simple way of preventing my pop-out from being closed by adding the following to my MainActivity.java:
#Override
public boolean dispatchTouchEvent(MotionEvent event) {
// We only want to intercept MotionEvent.ACTION_UP if the pop-out is present
return !(isColorModePanelOpen && event.getAction() == MotionEvent.ACTION_UP) && super.dispatchTouchEvent(event);
}
That worked in so far as the drawer is not being closed immediately when trying to click one of the buttons in the pop-out. Unfortunately, the buttons don't accept clicks (resp. touches with an event action MotionEvent.ACTION_DOWN) either. It seems, as long as the drawer isn't closed again, the whole screen is being blocked from any user interaction.
Can anyone confirm my suspicion? (or prove me wrong)
Thanks
Try this code in onResume() of your Activity
if(condition)
{
mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_OPEN);
}
else
{
mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED);
}
I tried to find an answer for myself but couldn't find it.
I need make badge on the MenuItem icon in the Toolbar, like this:
How can I make this?
Here is step by step functionality:
add 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/actionNotifications"
android:icon="#drawable/ic_info_outline_white_24dp"
android:menuCategory="secondary"
android:orderInCategory="1"
android:title="Cart"
app:actionLayout="#layout/notification_layout"
app:showAsAction="always" />
</menu>
Then add notification_layout.xml, this layout will be used as the notification icons layout
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
style="#android:style/Widget.ActionButton"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_gravity="center"
android:background="#android:color/transparent"
android:clickable="true"
android:gravity="center"
android:orientation="vertical">
<ImageView
android:id="#+id/hotlist_bell"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="0dp"
android:contentDescription="Notification Icon"
android:gravity="center"
android:src="#drawable/ic_info_outline_white_24dp" />
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/txtCount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="#dimen/x5dp"
android:layout_marginLeft="#dimen/x10dp"
android:layout_marginRight="0dp"
android:background="#drawable/pointer_"
android:gravity="center"
android:minWidth="17sp"
android:text="0"
android:textColor="#ffffffff"
android:textSize="12sp" />
</RelativeLayout>
now inside Activity
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu, menu);
final View notificaitons = menu.findItem(R.id.actionNotifications).getActionView();
txtViewCount = (TextView) notificaitons.findViewById(R.id.txtCount);
updateHotCount(count++);
txtViewCount.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
updateHotCount(count++);
}
});
notificaitons.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// TODO
}
});
return true;
}
You can put following function (taken from stackoverflow) inside the activity to update counter:
public void updateHotCount(final int new_hot_number) {
count = new_hot_number;
if (count < 0) return;
runOnUiThread(new Runnable() {
#Override
public void run() {
if (count == 0)
txtViewCount.setVisibility(View.GONE);
else {
txtViewCount.setVisibility(View.VISIBLE);
txtViewCount.setText(Integer.toString(count));
// supportInvalidateOptionsMenu();
}
}
});
}
I think it is possible with :
<ImageView
android:id="#+id/counterBackground"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#drawable/unread_background" /> <!-- your icon -->
<TextView
android:id="#+id/count"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="1"
android:textSize="8sp"
android:layout_centerInParent="true"
android:textColor="#FFFFFF" />
And then using it for that icon as background.also, you need to remove/disable the default icon too.
You may want to take a look:
Remove App Icon from Navigation Drawer ActionBar Does not work
Remove the navigation drawer icon and use the app icon to open
https://stackoverflow.com/a/29160904/4409113
Also, in the android-sdk/platforms/android-22/data/res, there should be that icon. You just need to find that and use it for your purpose (for example, adding that ImageView and adding it asbackground)
Take a look: https://stackoverflow.com/a/34999691/4409113
MaterialToolbar has ability to add BadgeDrawable:
For example in your Activity you can add the badge like this:
val toolbar: MaterialToolbar = findViewById(R.id.toolbar)
val badgeDrawable = BadgeDrawable.create(this).apply {
isVisible = true
backgroundColor = neededBadgeColor
number = neededNumber
}
BadgeUtils.attachBadgeDrawable(badgeDrawable, toolbar, R.id.item_in_toolbar_menu)
Actually I am developing an application in android and I want to show the total no. of unread emails on the Action Bar Menu Item (like flipkart and all other applications already have) using latest Appcompat V21. I tried a code which I have found here but its not working with AppCompat V21.
Please help me if you can!
Posting the same code here:
=> layout/menu/menu_actionbar.xml
<menu xmlns:android="http://schemas.android.com/apk/res/android">
...
<item android:id="#+id/menu_hotlist"
android:actionLayout="#layout/action_bar_notifitcation_icon"
android:showAsAction="always"enter code here
android:icon="#drawable/ic_bell"
android:title="#string/hotlist" />
...
</menu>
=> layout/action_bar_notifitcation_icon.xml
Note style and android:clickable properties. these make the layout the size of a button and make the background gray when touched.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:orientation="vertical"
android:gravity="center"
android:layout_gravity="center"
android:clickable="true"
style="#android:style/Widget.ActionButton">
<ImageView
android:id="#+id/hotlist_bell"
android:src="#drawable/ic_bell"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:layout_margin="0dp"
android:contentDescription="bell"
/>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/hotlist_hot"
android:layout_width="wrap_content"
android:minWidth="17sp"
android:textSize="12sp"
android:textColor="#ffffffff"
android:layout_height="wrap_content"
android:gravity="center"
android:text="#null"
android:layout_alignTop="#id/hotlist_bell"
android:layout_alignRight="#id/hotlist_bell"
android:layout_marginRight="0dp"
android:layout_marginTop="3dp"
android:paddingBottom="1dp"
android:paddingRight="4dp"
android:paddingLeft="4dp"
android:background="#drawable/rounded_square"/>
</RelativeLayout>
=> drawable-xhdpi/ic_bell.png
A 64x64 pixel image with 10 pixel wide paddings from all sides. You are supposed to have 8 pixel wide paddings, but I find most default items being slightly smaller than that. Of course, you'll want to use different sizes for different densities.
=> drawable/rounded_square.xml
Here, #ff222222 (color #222222 with alpha #ff (fully visible)) is the background color of my Action Bar.
**<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="2dp" />
<solid android:color="#ffff0000" />
<stroke android:color="#ff222222" android:width="2dp"/>
</shape>**
=> com/ipp/MyAppAndroid/MyActivity.java
private int hot_number = 0;
private TextView ui_hot = null;
#Override public boolean onCreateOptionsMenu(final Menu menu) {
MenuInflater menuInflater = getSupportMenuInflater();
menuInflater.inflate(R.menu.menu_actionbar, menu);
final View menu_hotlist = menu.findItem(R.id.menu_hotlist).getActionView();
ui_hot = (TextView) menu_hotlist.findViewById(R.id.hotlist_hot);
updateHotCount(hot_number);
new MyMenuItemStuffListener(menu_hotlist, "Show hot message") {
#Override
public void onClick(View v) {
onHotlistSelected();
}
};
return super.onCreateOptionsMenu(menu);
}
// call the updating code on the main thread,
// so we can call this asynchronously
public void updateHotCount(final int new_hot_number) {
hot_number = new_hot_number;
if (ui_hot == null) return;
runOnUiThread(new Runnable() {
#Override
public void run() {
if (new_hot_number == 0)
ui_hot.setVisibility(View.INVISIBLE);
else {
ui_hot.setVisibility(View.VISIBLE);
ui_hot.setText(Integer.toString(new_hot_number));
}
}
});
}
static abstract class MyMenuItemStuffListener implements View.OnClickListener, View.OnLongClickListener {
private String hint;
private View view;
MyMenuItemStuffListener(View view, String hint) {
this.view = view;
this.hint = hint;
view.setOnClickListener(this);
view.setOnLongClickListener(this);
}
#Override abstract public void onClick(View v);
#Override public boolean onLongClick(View v) {
final int[] screenPos = new int[2];
final Rect displayFrame = new Rect();
view.getLocationOnScreen(screenPos);
view.getWindowVisibleDisplayFrame(displayFrame);
final Context context = view.getContext();
final int width = view.getWidth();
final int height = view.getHeight();
final int midy = screenPos[1] + height / 2;
final int screenWidth = context.getResources().getDisplayMetrics().widthPixels;
Toast cheatSheet = Toast.makeText(context, hint, Toast.LENGTH_SHORT);
if (midy < displayFrame.height()) {
cheatSheet.setGravity(Gravity.TOP | Gravity.RIGHT,
screenWidth - screenPos[0] - width / 2, height);
} else {
cheatSheet.setGravity(Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL, 0, height);
}
cheatSheet.show();
return true;
}
}
Note: Due to lack to reputation, unable to post pic.
So I'm trying my crack at the infamous slide out menu, like in G+ and Youtube.
In this cause I'm setting an ActionBar UP button that I want to use to open the Side Menu.
I have most everything laid out correctly, but my HorizontalScrollView is not sliding when I ask.
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<include
android:layout_width="wrap_content"
layout="#layout/side_menu" />
<HorizontalScrollView
android:id="#+id/menu_scroll_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true"
android:scrollbars="horizontal" >
<include
layout="#layout/main_content" />
</HorizontalScrollView>
</FrameLayout>
private void toggleSideMenu() {
mMenuScrollView.postDelayed(new Runnable() {
#Override
public void run() {
int menuWidth = mSideMenu.getMeasuredWidth();
if (!mIsMenuVisible) {
// Scroll to 0 to reveal menu
int left = 0;
mScrollView.smoothScrollTo(left, 0);
} else {
// Scroll to menuWidth so menu isn't on screen.
int left = menuWidth;
mScrollView.smoothScrollTo(left, 0);
}
mIsMenuVisible = !mIsMenuVisible;
}
}, 50);
}
My call to smoothScroll doesn't seem to be working.
I tweaked the example some and ended up getting it working. Much more barebones than the other examples I have seen out there.
This works correctly, however I do not have a smooth animation for the MenuSliding. And I didn't need the HorizontalScrollView. As a side effect, users will have to hit the button to pop the menu in and out. This will not have the slide feature.
However, it is a BARE BONES example to get things rolling. Enjoy!
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<!-- Base layout has id/side_menu -->
<include layout="#layout/side_menu" />
<!-- Base layout has id/content -->
<include layout="#layout/content" />
</FrameLayout>
Here is my Activity
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mContentView = findViewById(R.id.content);
mSideMenu = findViewById(R.id.side_menu);
mIsMenuVisible = false;
}
private void toggleSideMenu() {
if (mIsMenuVisible) {
mSideMenu.setVisibility(View.INVISIBLE);
mContent.scrollTo(0, 0);
} else {
int menuWidth = mSideMenu.getMeasuredWidth() * -1;
mSideMenu.setVisibility(View.VISIBLE);
mContentView.scrollTo(menuWidth, 0);
}
mIsMenuVisible = !mIsMenuVisible;
}