41

Is there a way to render a view on top of the action bar? I want to create a small tip box that will point the user to an item in the action bar. I know that a Toast with a set view will be rendered above the action bar. Does anyone know how to do this with a view?

I have attempted using FrameLayout with layout_gravity="top" and inflating a view and then adding it to the running activity's layout.

I appreciate you in advance.

Edit: Here is an image of what I was thinking: enter image description here

Edit: Perhaps some more detail is needed. I am looking for a way, or to find out if it is even possible to add a view to the view hierarchy of the activity so that it is rendered last.

Similar to CSS, I want a higher z-index order for this particular view ( the blue floating box in the image), such that it would be rendered on top of the Action Bar region in the activity. The view is in no way associated with Action Bar, it is simply drawn on top of it.

edwin
  • 7,985
  • 10
  • 51
  • 82
lunaleaps
  • 426
  • 1
  • 5
  • 9
  • Did you find a solution to this issue? I am wanting to perform the same task in my app now, but I don't see any relevant answers below. – MM. Mar 25 '13 at 23:01
  • @MM Take a look at my answer. Just added it. – Sean Jun 05 '13 at 16:42

10 Answers10

34

I was trying to achieve something else but I needed a solution similar to this. I needed to draw an opaque layer covering the whole screen, even the action bar--sort of like a dialog. I did so this way:

ViewGroup vg = (ViewGroup)(getWindow().getDecorView().getRootView());
vg.addView(myNewView, params);

this can be used to draw anything anywhere on the screen.

UPDATE: You really shouldn't be using ActionBar anymore, you wouldn't have this issue in the first place if you were using Toolbar like Android recommends. Toolbar would go inside your activity xml like a regular view and you can can do whatever you want to it. And its fully backwards compatible. https://developer.android.com/training/appbar/setting-up

Siavash
  • 7,583
  • 13
  • 49
  • 69
  • 1
    @ekashking The `params` is a LayoutParams instance. It's used to determine how your view will be laid out. e.g. `new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)` – Mavamaarten Nov 15 '19 at 15:06
21

After struggling with it myself quite some time, here's the solution (tested it - working good):

The general steps are:

  1. Create a wrapper view
  2. Detach the screen view children, place the wrapper, and attach the children
  3. Inflate the content to the children
  4. Controling the wrapper will help you control exactly the action bar and the content below it all together.
  5. Now, using the wrapper, you can add "brothers" to the actionbar/main area. That brother is exactly what you described in your image.

Let's see some code.

First, create a method to help create a wrapper view. the wrapper will be placed between the entire screen and the content of your app. being a ViewGroup you can later on fully control it's content.

private ViewGroup setContentViewWithWrapper(int resContent) {
        ViewGroup decorView = (ViewGroup) this.getWindow().getDecorView();
        ViewGroup decorChild = (ViewGroup) decorView.getChildAt(0);

        // Removing decorChild, we'll add it back soon
        decorView.removeAllViews();

        ViewGroup wrapperView = new FrameLayout(this);

        // You should set some ID, if you'll want to reference this wrapper in that manner later
        //
        // The ID, such as "R.id.ACTIVITY_LAYOUT_WRAPPER" can be set at a resource file, such as:
        //  <resources xmlns:android="http://schemas.android.com/apk/res/android">
        //      <item type="id" name="ACTIVITY_LAYOUT_WRAPPER"/>
        //  </resources>
        //
        wrapperView.setId(R.id.ACTIVITY_LAYOUT_WRAPPER);

        // Now we are rebuilding the DecorView, but this time we 
        // have our wrapper view to stand between the real content and the decor
        decorView.addView(wrapperView, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
        wrapperView.addView(decorChild, decorChild.getLayoutParams());
        LayoutInflater.from(this).inflate(getActivityLayout(), 
                    (ViewGroup)((LinearLayout)wrapperView.getChildAt(0)).getChildAt(1), true);

        return wrapperView;
    }

Now, interfere with the regular Activity creation, and instead of using setContentView, use the method we've created.

    @Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // DON'T CALL `setContentView`, 
    // we are replacing that line with this code:
    ViewGroup wrapperView = setContentViewWithWrapper(R.layout.activity_layout);

    // Now, because the wrapper view contains the entire screen (including the notification bar
    // which is above the ActionBar) I think you'll find it useful to know the exact Y where the 
    // action bar is located.
    // You can use something like that:
    ViewGroup actionBar = (ViewGroup)((LinearLayout)wrapperView.getChildAt(0)).getChildAt(0);
    int topOffset = actionBar.getTop();

    // Now, if you'll want to add a view:
    //  1. Create new view
    //  2. Set padding top - use "topOffset"
    //  3. Add the view to "wrapperView"
    //  4. The view should be set at front. if not - try calling to "bringToFront()"
}

That's about it.

Notes

  • I've used Android's hierarchy-viewer to understand what's the right hierarchy. (didn't guess those 0 and 1 indexes)
  • If you are using some kind of a menu drawer in your activity, you might have to configure it a little bit different since drawers are already creating that wrapper for you
  • I've learned a lot by looking at this great library

EDIT: Refer to @CristopherOyarzúnAltamirano Answer for further support on newer Android versions

Good luck!

Sean
  • 5,176
  • 2
  • 34
  • 50
  • Hey man. I have some problems implementing this solution for android 4.4. I got this exception, any ideas? com.android.internal.widget.ActionBarOverlayLayout cannot be cast to android.widget.LinearLayout. – Dan1one Nov 22 '13 at 06:44
  • 2
    That's work for ICS and Jelly, but Ginger and Kitkat draw hierarchy in a different way. – Cristopher Oyarzún Altamirano Dec 09 '13 at 15:51
  • If I'm using the SlidingUpPanelLayout, how would I configure it differently? I'm getting an java.lang.IllegalStateException: Height must have an exact value or MATCH_PARENT at com.sothree.slidinguppanel.SlidingUpPanelLayout.onMeasure(SlidingUpPanelLayout.java:386) So at some stage in the container view it's not got the MATCH_PARENT value for the height. Interestingly, it doesn't complain about width. – Casey Gibson Dec 11 '13 at 10:52
  • I get ClassCastException when casting wrapperView to LinearLayout. The status bar height can be caught like this: https://stackoverflow.com/questions/3407256/height-of-status-bar-in-android/3410200#3410200 – Shayan_Aryan Jun 17 '14 at 05:50
  • plus, to create a clean BaseActivity we can override the setContentView() and put the last lines of code in that. – Shayan_Aryan Jun 17 '14 at 05:53
  • 1
    I'm getting ClassCastException Caused by: java.lang.ClassCastException: android.view.ViewStub cannot be cast to android.view.ViewGroup – DroidLearner Apr 30 '15 at 06:02
  • @DroidLearner, Refer to CristopherOyarzúnAltamirano Answer for further support on newer Android versions – Sean Apr 30 '15 at 11:52
  • 1
    That answer should be updated.Causes 'Forced close'. – 1111161171159459134 Apr 25 '17 at 16:18
14

There is a much simpler way to achieve this. ActionBar holds its layout in the class ActionBarContainer which simply inherits from FrameLayout. So in order to display something over the ActionBar you need to grab a reference to the ActionBarContainer and add your own custom View into it. Here is the code

    int abContainerViewID = getResources().getIdentifier("action_bar_container", "id", "android");     
    FrameLayout actionBarContainer = (FrameLayout)findViewById(abContainerViewID);      
    LayoutInflater myinflater = getLayoutInflater();        
    View customView = myinflater.inflate(R.layout.yourCustomeViewLayout, null);
    actionBarContainer.addView(customView);
Nouman Hanif
  • 764
  • 9
  • 13
5

I found this workaround based on @Sean answer:

        //This is for Jelly, ICS, Honeycomb
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB && Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2){
            LayoutInflater.from(this).inflate(resContent, (ViewGroup)((LinearLayout)wrapperView.getChildAt(0)).getChildAt(1), true);}
        //This is for KitKat and Jelly 4.3
        else if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2){
            LayoutInflater.from(this).inflate(resContent, (ViewGroup) (((ViewGroup) wrapperView.getChildAt(0)).getChildAt(0)), true);}
        //This is for Ginger
        else{
            LayoutInflater.from(this).inflate(resContent, (ViewGroup) ((LinearLayout)((FrameLayout) wrapperView.getChildAt(0)).getChildAt(0)).getChildAt(1), true);}
  • 1
    I've got error on Gingerbird devices with this method. I edited it like this: LayoutInflater.from(this).inflate(resContent, (ViewGroup) ((FrameLayout) decorChild), true); – Shayan_Aryan Jun 17 '14 at 10:48
5

I found a much simpler way to do this. I just applied android:translationZ="10dp" to the view which I need to be covering the action bar.

I chose 10dp but it can actually be anything you want as long as it is superior to the actionbar's elevation

<ImageView
      android:layout_width="100dp"
      android:layout_height="100dp"
      android:translationZ="10dp"
      android:src="@drawable/potatoe" />

Also, don't worry about Android studio's following warning :

"translationZ can't be used with API<21".

It will be ignored, but you don't really care because the toolbar shouldn't cover your view with APIs inferior to 21.

Jack'
  • 1,722
  • 1
  • 19
  • 27
1

Try using ActionBar.setCustomView(). That's the only way to change the appearance of that area of the screen. You can't stick a View into the area "above" the ActionBar, because that area is basically controlled by the system. On the other hand, you can provide your own layout for it.

If you explain in more detail what you're trying to do, respondents might have some better design ideas.

Joe Malin
  • 8,621
  • 1
  • 23
  • 18
  • Cool, I've included an image of what I was thinking. I see that Toasts can have a view set. And I'm curious to know how they render their views a top of the action bar. – lunaleaps Mar 07 '13 at 19:48
1

https://github.com/michaelye/EasyDialogDemo

see the demo above,it may help you

dialog.setLocation(new location[])//point in screen

you could set the location[] yourself.

MichaelYe
  • 386
  • 4
  • 15
0

Use the android:actionLayout in your menu.xml file.

<?xml version="1.0" encoding="utf-8"?><menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/menu_id"
      android:title="@string/menu_string_to_show"
      android:icon="@drawable/ic_menu_icon_name"
      android:showAsAction="always"
      android:actionLayout="@layout/action_button_foo" /></menu>

Then create your action_button_foo.xml layout:

<?xml version="1.0" encoding="utf-8"?><TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@string/menu_string_to_show"
android:drawableLeft="@drawable/ic_menu_icon_name"
android:background="@drawable/bg_btn_action_bar"
android:clickable="true" />

To handle click do the following:

@Overridepublic boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.my_menu, menu);

final MenuItem item = menu.findItem(R.id.menu_id);
item.getActionView().setOnClickListener(new OnClickListener() {
    @Override
    public void onClick(View v) {
        onOptionsItemSelected(item);
    }
});

return super.onCreateOptionsMenu(menu);}

That's if :)

Shajeel Afzal
  • 5,913
  • 6
  • 43
  • 70
  • Thanks for the quick reply! But I believe this is creating a menu item for the action bar? I just want to draw a view on top of the action bar. But this view should not be part of the action bar, it is non-responsive. – lunaleaps Mar 07 '13 at 19:22
  • What do you mean by a view? – Shajeel Afzal Mar 08 '13 at 09:52
  • It would be a TextView in a layout file and image background. If you look at the added image in my OP, it would be the floating blue box – lunaleaps Mar 08 '13 at 14:31
0

(Reference: http://www.vogella.com/articles/AndroidActionBar/article.html)
Custom Views in the ActionBar
You can also add a custom View to the ActionBar. For this you use the setCustomView method for the ActionView class. You also have to enable the display of custom views via the setDisplayOptions() method by passing in the ActionBar.DISPLAY_SHOW_CUSTOM flag.

For example you can define a layout file which contains a EditText element.

<?xml version="1.0" encoding="utf-8"?>
<EditText xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/searchfield"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:inputType="textFilter" >

This layout can be assigned to the ActionBar via the following code. The example code allow attaches a listener to the custom view.

package com.vogella.android.actionbar.customviews;

import android.app.ActionBar;
import android.app.Activity;
import android.os.Bundle;
import android.view.KeyEvent;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
import android.widget.Toast;

public class MainActivity extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

ActionBar actionBar = getActionBar();
// add the custom view to the action bar
actionBar.setCustomView(R.layout.actionbar_view);
EditText search = (EditText) actionBar.getCustomView().findViewById(R.id.searchfield);
search.setOnEditorActionListener(new OnEditorActionListener() {

  @Override
  public boolean onEditorAction(TextView v, int actionId,
      KeyEvent event) {
    Toast.makeText(MainActivity.this, "Search triggered",
        Toast.LENGTH_LONG).show();
    return false;
  }
});
actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM
    | ActionBar.DISPLAY_SHOW_HOME);
}
} 
Vishal Vijay
  • 2,518
  • 2
  • 23
  • 45
  • Ah, okay, from what I understand, you are adding a `Toast` notification when I click on the action bar, specifically the `EditText`. I was wondering more about how to render a view/layout on top of the action bar visually. Like I know how Android creates a view hierarchy. How can I insert a view to be rendered last in the hierarchy so it appears "above" the Action Bar in the sense of z-index ordering or "bring view to front". Or is it even possible? Regardless, thank you for your help Vishal. – lunaleaps Mar 08 '13 at 14:40
  • So you are telling that you have to insert a custom view on action bar with out using setCustomView. I think you have to go-to custom action bar. – Vishal Vijay Mar 08 '13 at 15:55
  • Better you go for custom action bar(Create your own action bar). You can do whatever you want with that. – Vishal Vijay Mar 08 '13 at 16:34
  • Download actionbar sherlock from github and see how they are implementing action bar. – Vishal Vijay Mar 08 '13 at 16:42
  • Surely this will help you http://stackoverflow.com/questions/12933905/create-own-action-bar-in-android – Vishal Vijay Mar 08 '13 at 16:45
0

The explanations here are all too long:

Wrap your main layout file into a top level ViewGroup (e.g. wrap a CoordinatorLayout into a FrameLayout), then inject or declare the view in the new top level layout out. The view will appear above the action bar.

the_prole
  • 8,275
  • 16
  • 78
  • 163