AppCompat v7:21 Split Action Bar Broken? - android

I am currently developing an application in which I use a heavily modified Split Action Bar. Here is a link to the app's current state:
You'll notice a transparent action bar up top, with a custom view inflated into it, with a hacked together split action bar on bottom. The bottom view is actually a single action item with a custom view inflated into it and showAlways=true.
Currently I only support SDK v15+ and I don't really plan on changing that, but with the Lollipop AppCompat library that just released, I decided to implement it, so I could get some of that awesomeness in my app.
I've changed my theme to Theme.AppCompat.Light, and my MainActivity now extends ActionBarActivity instead of Activity.
All references to getActionBar have now been switched to getSupportActionBar, and with only those changes, this is what my activity now looks like:
You'll notice I got a UI dump from the Device Monitor, and it's shoving the bottom action bar into a weird space and calling that the action bar, and getting rid of my top custom view.
Here is my code for setting up my action bar:
public void initializeActionBar(){
View customNav = LayoutInflater.from(this).inflate(R.layout.action_bar_top, null);
actionBar = getSupportActionBar();
actionBar.setBackgroundDrawable(getResources().getDrawable(R.drawable.transparent_fifty_percent));
final PopupWindow window = addPopupWindow();
actionBarOptions = (ImageView)customNav.findViewById(R.id.options);
actionBarOptions.setVisibility(View.GONE);
actionBarOptions.setImageDrawable(app.svgToBitmapDrawable(getResources(), R.raw.vertical_ellipsis, app.scaleByDensity(48)));
actionBarOptions.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
window.showAsDropDown(actionBarOptions, 0, 0);
}
});
TextView title = (TextView) customNav.findViewById(R.id.screen_title);
Typeface font1 = Typeface.createFromAsset(getAssets(), "Merriweather-Italic.ttf");
title.setText("Parsley");
title.setTypeface(font1);
actionBar.setCustomView(customNav);
actionBar.setDisplayShowCustomEnabled(true);
actionBar.setDisplayShowTitleEnabled(false);
actionBar.setDisplayShowHomeEnabled(false);
actionBar.setDisplayUseLogoEnabled(false);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
new MenuInflater(this).inflate(R.menu.test, menu);
LinearLayout fullMenu = (LinearLayout) menu.findItem(R.id.full_menu).getActionView();
ViewGroup.LayoutParams params;
icon1 = (ImageView) fullMenu.findViewById(R.id.action_item1);
params = icon1.getLayoutParams();
params.width = getResources().getDisplayMetrics().widthPixels / 4;
params.height = (int) (48 * getResources().getDisplayMetrics().density);
icon1.setImageDrawable(app.svgToBitmapDrawable(getResources(), R.raw.shopping_list_icon, app.scaleByDensity(32)));
icon2 = (ImageView) fullMenu.findViewById(R.id.action_item2);
icon3 = (ImageView) fullMenu.findViewById(R.id.action_item3);
icon4 = (ImageView) fullMenu.findViewById(R.id.action_item4);
icon2.setImageDrawable(app.svgToBitmapDrawable(getResources(), R.raw.recipe_box_icon, app.scaleByDensity(32)));
icon3.setImageDrawable(app.svgToBitmapDrawable(getResources(), R.raw.icon_search, app.scaleByDensity(32)));
icon4.setImageDrawable(app.svgToBitmapDrawable(getResources(), R.raw.icon_add, app.scaleByDensity(32)));
params = icon2.getLayoutParams();
params.width = getResources().getDisplayMetrics().widthPixels / 4;
params.height = (int) (48 * getResources().getDisplayMetrics().density);
params = icon3.getLayoutParams();
params.width = getResources().getDisplayMetrics().widthPixels / 4;
params.height = (int) (48 * getResources().getDisplayMetrics().density);
params = icon4.getLayoutParams();
params.width = getResources().getDisplayMetrics().widthPixels / 4;
params.height = (int) (48 * getResources().getDisplayMetrics().density);
if (!firstLoad) {
setBottomActionBarActive();
setActiveTab(0);
}
optionsLoaded = true;
return true;
}
initializeActionBar() is called from onCreate in my activity. Any ideas what I'm doing wrong?

Toolbar should be used. In your case it's one toolbar at the top, and one at the bottom. Check android team blog, they have nice integration guide.

If you just want your bottom action bar back, you can simply change back to appcompat v7:20 ,and it works for me. The problem is split action bar is no longer being supported in appcomat v7:21.

While user482277's solution may work for instances with a more traditional split action bar, utilizing action items, navigation drawer, etc, it didn't quite work for me. What I ended up doing was building a pair of custom (compound really) views to emulate both the top and bottom action bar. I found this situation to work much better, especially with backwards compatibility. I don't have to worry about earlier versions supporting action bar, because at the end of the day, it's just a pair of classes that extend LinearLayout. In addition, I don't have to worry about different screen sizes (particularly tablets) not supporting the split version.

Related

Showing refreshing message in Action Bar

I'm using an Action Bar (a regular one, not sherlock) in my android app, and when the app opens I want to show a refreshing message in the action bar. This means I want to hide the menu items and title (similar to how the GMail app appears when it's refreshing).
What is the best approach for this? Is it using a contextual action bar?
Is it possible to show the refreshing animation just below the action bar, like in the GMail app (ie, the blue lines sliding over).
I know I can use a 3rd party pull-to-refresh, but I'd prefer not to use this (as I don't need the pull-to-refresh capability).
I'm targeting Jelly Bean and newer devices.
Thanks!
I want to hide the menu items and title (similar to how the GMail app
appears when it's refreshing).
This can be done by using WindowManager.addView(View, LayoutParams). Here's an example of displaying a message on top of the ActionBar that should give you a pretty solid idea about how to proceed.
The layout
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#android:id/message"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:textColor="#android:color/white"
android:textSize="18sp" />
Implementation
/** The attribute depicting the size of the {#link ActionBar} */
private static final int[] ACTION_BAR_SIZE = new int[] {
android.R.attr.actionBarSize
};
/** The notification layout */
private TextView mMessage;
private void showLoadingMessage() {
// Remove any previous notifications
removeLoadingMessage();
// Initialize the layout
if (mMessage == null) {
final LayoutInflater inflater = getLayoutInflater();
mMessage = (TextView) inflater.inflate(R.layout.your_layout, null);
mMessage.setBackgroundColor(getResources().getColor(android.R.color.holo_blue_dark));
mMessage.setText("Loading...");
}
// Add the View to the Window
getWindowManager().addView(mMessage, getActionBarLayoutParams());
}
private void removeLoadingMessage() {
if (mMessage != null && mMessage.getWindowToken() != null) {
getWindowManager().removeViewImmediate(mMessage);
mMessage = null;
}
}
/**
* To use, #see {#link WindowManager#addView(View, LayoutParams)}
*
* #return The {#link WindowManager.LayoutParams} to assign to a
* {#link View} that can be placed on top of the {#link ActionBar}
*/
private WindowManager.LayoutParams getActionBarLayoutParams() {
// Retrieve the height of the status bar
final Rect rect = new Rect();
getWindow().getDecorView().getWindowVisibleDisplayFrame(rect);
final int statusBarHeight = rect.top;
// Retrieve the height of the ActionBar
final TypedArray actionBarSize = obtainStyledAttributes(ACTION_BAR_SIZE);
final int actionBarHeight = actionBarSize.getDimensionPixelSize(0, 0);
actionBarSize.recycle();
// Create the LayoutParams for the View
final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
LayoutParams.MATCH_PARENT, actionBarHeight,
WindowManager.LayoutParams.TYPE_APPLICATION_PANEL,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSLUCENT);
params.gravity = Gravity.TOP;
params.x = 0;
params.y = statusBarHeight;
return params;
}
Results
Conclusion
This implementation is very similar to Gmail and other apps, minus the pull-to-refresh pattern.
When you call showLoadingMessage, post a Runnable or use a View.OnClickListener. You don't want to call WindowManager.addView too early or you'll throw a WindowManager.BadTokenException. Also, it's important to call removeLoadingMessage in Activity.onDestroy, otherwise you run the risk of leaking the View you add to the Window.

Indeterminate Horizontal ProgressBar BELOW ActionBar using AppCompat?

I have been looking for answers on how to place the indeterminate horizontal progress bar below the action bar using AppCompat. I'm able to get the horizontal progress bar to appear, but it is at the top of the action bar. I want it under/below the action bar kind of like how gmail does it (except without the pull to refresh).
I used the following code to have the progress bar appear:
supportRequestWindowFeature(Window.FEATURE_PROGRESS);
setContentView(R.layout.main_activity);
setSupportProgressBarIndeterminate(Boolean.TRUE);
setSupportProgressBarVisibility(true);
but this places the horizontal progress bar at the top of the action bar. Anyone know how to place the progress bar below the action bar?
I faced a similar problem recently and solved it by creating my own progressbar and then aligning it by manipulating getTop() of the content view.
So first create your progressbar.
final LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, 20); //Use dp resources
mLoadingProgressBar = new ProgressBar(this, null, android.R.attr.progressBarStyleHorizontal);
mLoadingProgressBar.setIndeterminate(true);
mLoadingProgressBar.setLayoutParams(lp);
Add it to the window (decor view)
final ViewGroup decor = (ViewGroup) getWindow().getDecorView();
decor.addView(mLoadingProgressBar);
And in order to get it to its correct position Im using a ViewTreeObserver that listens until the view has been laid out (aka the View.getTop() isnt 0).
final ViewTreeObserver vto = decor.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
final View content = getView(android.R.id.content);
#Override
public void onGlobalLayout() {
int top = content.getTop();
//Dont do anything until getTop has a value above 0.
if (top == 0)
return;
//I use ActionBar Overlay in some Activities,
//in those cases it's size has to be accounted for
//Otherwise the progressbar will show up at the top of it
//rather than under.
if (getSherlock().hasFeature((int) Window.FEATURE_ACTION_BAR_OVERLAY)) {
top += getSupportActionBar().getHeight();
}
//Remove the listener, we dont need it anymore.
Utils.removeOnGlobalLayoutListener(decor, this);
//View.setY() if you're using API 11+,
//I use NineOldAndroids to support older
ViewHelper.setY(mLoadingProgressBar, top);
}
});
Hope that makes sense for you. Good luck!

What should be action bar's height compatible for all devices?

I am using different layout for action bar and add different items to it that matched to my requirements. and then calling it in java class like this
ActionBar actionBar = getActionBar();
actionBar.setCustomView(R.layout.actionbar);
actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM);
actionBar.setDisplayShowHomeEnabled(false);
It looks fine for 2 or 3 different devices but doesn't look compatible for nexus7 tablet and xhdpi. The height of action bar is small for that and it gives empty space.What should I do to manage this? I am using
android:layout_width="match_parent"
android:layout_height="48dp"
in actionbar layout.I have also used
android:layout_height="wrap_content"
but it doesn't work at all.I don't know how to fix it. Please help.
You can set the height of the action bar at source not in the resources.
so all you need is to calculate the height of the action bar :
TypedValue tv = new TypedValue();
if (getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true))
{
actionBarHeight = TypedValue.complexToDimensionPixelSize(tv.data,getResources().getDisplayMetrics());
}
ActionBar bar = getSupportActionBar();
Display d = getWindowManager().getDefaultDisplay();
DisplayMetrics dm = new DisplayMetrics();
d.getMetrics(dm);
LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)myCustomView.getLayoutParams();
final int ACTION_BAR_HEIGHT = dm.heightPixels/10; // 1/10 from screen height
lp.height = ACTION_BAR_HEIGHT;
myCustomView.setLayoutParams(lp);
bar.setCustomView(myCustomView);

Actionbarsherlock getHeight() returns 0

I'm using Actionbarsherlock and I want to place a PopupWindow right below the action bar. Using the showAtLocation() takes an x and y offset, so ideally the y offset would be the height of the action bar. But when I call
int abHeight = getSupportActionBar().getHeight();
it returns zero. I'm using a SherlockFragmentActivity
Here's the relevant code:
slidingLayout = inflater.inflate(R.layout.sliding_menu, null);
menuDrawer = MenuDrawer.attach(this, MenuDrawer.MENU_DRAG_CONTENT, Position.LEFT);
menuDrawer.setContentView(R.layout.activity_main);
menuDrawer.setMenuView(slidingLayout.findViewById(R.id.sliding_menu));
getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
int abHeight = getSupportActionBar().getHeight();
I've looked all over and can't find a similar question/answer, so has anyone experienced this before? Thanks.
EDIT: Jake's answer was right on. In order to get that attribute value I used this post.
You can read the height of the action bar from the actionBarSize theme attribute. This changes based on the device configuration so make sure you are always reading it when your activity is created or recreated.
in you style.XML add: <item name="#android:attr/actionBarSize">50px</item>
and then in your activity add the following code :
TypedArray actionbarSizeTypedArray = mContext.obtainStyledAttributes(new int[] { android.R.attr.actionBarSize});
int h = (int) actionbarSizeTypedArray.getDimension(0, 0);
this is one kind ,I am trying to get other ways.Good luck!
Yeah!I find a way very simple:
TypedValue tv = new TypedValue();
if (getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true))
{
int h=TypedValue.complexToDimensionPixelSize(tv.data,getResources().getDisplayMetrics());
}
more info,look this link
You can't get the height for views until they have been layed out. Try adding a ViewTreeObserver:
someView.getViewTreeObserver().addGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
// Remember to remove it if you don't want it to fire every time
someView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
int abHeight = getSupportActionBar().getHeight();
// Use the height as desired...
}
});
Refer to the docs starting at View.getViewTreeObserver().

How to customize Android ActionBar to show a custom view before the tabs?

I'm using a custom view for the ActionBar with Tabs. My problem is the ordering of the custom view. Android is displaying it AFTER the tabs - which I do not want.
I want the custom view displayed BEFORE the tabs.
Is there a way to customize the actionBar to show the custom view before the tabs? or is this not possible?
Code:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final ActionBar bar = getActionBar();
bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
View customActionBarView =
getLayoutInflater().inflate(R.layout.home_actionbar, null, true);
ActionBar.LayoutParams lp =
new ActionBar.LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT);
lp.gravity = Gravity.START;
bar.setCustomView(customActionBarView, lp);
bar.setLogo(R.drawable.logo);
bar.setHomeButtonEnabled(true);
bar.setDisplayShowCustomEnabled(true);
bar.addTab(bar.newTab()
.setText("Stuff")
.setTabListener(new TabListener<StuffFragment>(
this, "stuff", StuffFragment.class)));
bar.addTab(bar.newTab()
.setText("Friends")
.setTabListener(new TabListener<ContactsFragment>(
this, "friends", ContactsFragment.class)));
bar.addTab(bar.newTab()
.setText("Messages")
.setTabListener(new TabListener<ConversationsFragment>(
this, "messages", ConversationsFragment.class)));
if (savedInstanceState != null) {
bar.setSelectedNavigationItem(savedInstanceState.getInt("tab", 0));
}
bar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM |
ActionBar.DISPLAY_SHOW_HOME | ActionBar.DISPLAY_USE_LOGO);
bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
}
This seems to be the intended behaviour when using tabs and custom views.
https://code.google.com/p/android/issues/detail?id=36191#c3
If you take a look at ActionBarSherlock - Tabs appearing ABOVE actionbar with custom view many other people are experiencing this as well and some people have offered solutions.
I have been unable to get any of the solutions working, but they may work for you. The trick seems to be to make sure the logo is is not set to be hidden. Call
getActionBar().setDisplayShowHomeEnabled(true) or getSupportActionBar().setDisplayShowHomeEnabled(true) if using ActionBarSherlock.
Then call:
View homeIcon = findViewById(
Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB ?
android.R.id.home : R.id.abs__home);
((View) homeIcon.getParent()).setVisibility(View.GONE);
((View) homeIcon).setVisibility(View.GONE);
This will get a reference to the actionbar view that holds the logo and sets it to gone with enables the custom view to fill the entire parent view, but should keep the tabs underneath...
As I said I was unable to get this working, but some people have had success.
Good luck.
I had the same problem and i figured out a way to solve it.
It's not an "elegant" solution but it was the best i could find.
As first thing since you want to modify the standard ActionBar behaviour you have to force ActionBar Sherlok to always use the non native implementation.
To do that open ActionBarSherlok.java and comment this line of code:
registerImplementation(ActionBarSherlockNative.class);
then remove all the values-v11 values-v14 etc and be sure to always extend Theme.Sherlock and never Theme.Holo
At this point you are sure that the ActionBar implementation is always the one written by Jake Wharton.
The only thing left to do is make the ActionBar view layout the way you want.
Open ActionBarView.java and in the onLayout() method move this piece of code
if (mExpandedActionView == null) {
final boolean showTitle = mTitleLayout != null && mTitleLayout.getVisibility() != GONE &&
(mDisplayOptions & ActionBar.DISPLAY_SHOW_TITLE) != 0;
if (showTitle) {
x += positionChild(mTitleLayout, x, y, contentHeight);
}
switch (mNavigationMode) {
case ActionBar.NAVIGATION_MODE_STANDARD:
break;
case ActionBar.NAVIGATION_MODE_LIST:
if (mListNavLayout != null) {
if (showTitle) x += mItemPadding;
x += positionChild(mListNavLayout, x, y, contentHeight) + mItemPadding;
}
break;
case ActionBar.NAVIGATION_MODE_TABS:
if (mTabScrollView != null) {
if (showTitle) x += mItemPadding;
x += positionChild(mTabScrollView, x, y, contentHeight) + mItemPadding;
}
break;
}
}
right before this piece
if (mProgressView != null) {
mProgressView.bringToFront();
final int halfProgressHeight = mProgressView.getMeasuredHeight() / 2;
mProgressView.layout(mProgressBarPadding, -halfProgressHeight,
mProgressBarPadding + mProgressView.getMeasuredWidth(), halfProgressHeight);
}
You're done!
Hope this helps
When you add your custom view to the ActionBar you can specify the gravity also.
ActionBar.LayoutParams lp = new ActionBar.LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT);
lp.gravity = Gravity.LEFT;
customView.setLayoutParams(lp);
use ActionBarSherlock, which is very good implementation of Custom ActionBar for all android versions and very easy to use.
or you can create your own ActionBar using custom title bar and and its fair enough easy to implement. You can see this and this examples are very good examples of custom title bars.
Are you making an app for the tablet? As far as I know, the tabs bar of actionbar basically appears below the it on cellphone.
If on tablet, I'm afraid you can't adjust the positions of tabs. what I can think of is that you need to quit using navigation mode and make a custom view which look like tabs to replace the actionBar tabs. Of course this causes a lot of extra effort to deal with the navigation stuff.
Just create a custom view for your home button. In this view you can combine both logo and the custom view you're trying to position on left. Then add tabs as normal
The only downside of this solution is that you'll need to maintain the state of the back button yourself (if you use it at all)
First set "DisplayShowHomeEnabled" property of actionbar to "true":
actionBar.setDisplayShowHomeEnabled(true);
and then:
View homeIcon = findViewById(android.R.id.home);
((View) homeIcon.getParent()).setVisibility(View.GONE);
((View) homeIcon).setVisibility(View.GONE);
I hope it helps :)

Categories

Resources