30

I am working on android application where I am using ActionBar so there one is navigation drawer icon to open it and title of ActionBar in ActionBar. I want to set a click listener on title of ActionBar such that it start a new Activity and set click listener different on navigation drawer icon to open navigation drawer menu.

I achieved a click on navigation drawer icon but when I click on title of ActionBar title also then it open the navigation drawer menu. Is there any way to set different click listener on title of ActionBar.

Thanks in advance.

N Sharma
  • 33,489
  • 95
  • 256
  • 444

11 Answers11

24

Try adding this code under the onCreate() function. This will grab the resource the action bar title is under, and assign it an id you can use to add an OnClickListener to. Let me know how it goes!

final int abTitleId = getResources().getIdentifier("action_bar_title", "id", "android");
findViewById(abTitleId).setOnClickListener(new View.OnClickListener() {

@Override
public void onClick(View v) {
    //Do something
    }
});
Jake Weso
  • 555
  • 1
  • 6
  • 14
  • Hi, this approach is not working in Android OS 2.3, not sure about 4.0-4.3, I tested it is working in Android 4.4.4. please assist – N Sharma Aug 16 '14 at 15:14
  • @Williams 2.3 doesn't have an Action Bar. You can use: `your.R.id.action_bar_title` instead of `getIdentifier` if you use `appcompat-v7`. You can ask for both IDs and use the one which exists. – TWiStErRob Jan 03 '15 at 15:20
  • R.id.action_bar_title is not working. I am using Toolbar with support v7:21 – Max Feb 12 '15 at 09:34
  • 7
    Error for Android 4.0+ – TheOnlyAnil Apr 12 '15 at 20:00
  • 1
    @Max check my [new answer](http://stackoverflow.com/a/29823008/253468) if you still have the problem. (Toolbar in v21 doesn't set the id, but can be [read via reflection](http://stackoverflow.com/a/28934656/253468)) – TWiStErRob Apr 23 '15 at 12:11
  • 1
    I tried the same.. but it is showing a null pointer exception on title field.. I am getting the id for abtitleId as some numeric value... but still onclick implementation is failed due to null reference – this is yash Dec 19 '18 at 02:14
  • I tried the same.. but it is showing a null pointer exception on title field.. I am getting the id for abtitleId as some numeric value... but still onclick implementation is failed due to null reference – Ivan Jun 10 '19 at 06:49
23

You could use a custom layout for the title and assign a listener to it:

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

    ActionBar actionBar = getActionBar();
    if (actionBar != null) {
        // Disable the default and enable the custom
        actionBar.setDisplayShowTitleEnabled(false);
        actionBar.setDisplayShowCustomEnabled(true);
        View customView = getLayoutInflater().inflate(R.layout.actionbar_title, null);
        // Get the textview of the title
        TextView customTitle = (TextView) customView.findViewById(R.id.actionbarTitle);


        // Change the font family (optional)
        customTitle.setTypeface(Typeface.MONOSPACE);
        // Set the on click listener for the title
        customTitle.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.w("MainActivity", "ActionBar's title clicked.");
            }
        });
        // Apply the custom view
        actionBar.setCustomView(customView);
    }
}

actionbar_title.xml:

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center">

    <TextView
        android:id="@+id/actionbarTitle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="25sp"
        android:text="@string/app_name"/>

</LinearLayout>
ARB
  • 53
  • 1
  • 5
Simas
  • 43,548
  • 10
  • 88
  • 116
  • 2
    Sadly this way you lose the ability to `getActionBar().setTitle()` and you need to implement subtitle handilng yourself as well, but otherwise this is the proper solution. It would be nice if someone published a gist of an `ActionBarTitles` as a drop-in replacement custom view supporting title/subtitle and reading the theme. – TWiStErRob Apr 23 '15 at 12:16
  • Is there a way to set the handler from Xml? Edit: I found how to do this [here](https://stackoverflow.com/a/16079425/812013) – Chucky Jan 31 '22 at 16:42
10

I think Simas's answer is the best one, but here's a hacky version in case you prefer that.

ViewTools.findActionBarTitle(getWindow().getDecorView()).setOnClickListener(...);

This one should be universal in that it works with:

  • stock Android ActionBar
  • Theme.AppCompat support ActionBar
  • v21-style setActionBar
    use <Toolbar android:id="@+id/action_bar"
    or pass in the inflated Toolbar as root
  • v21-style setSupportActionBar
    use <android.support.v7.widget.Toolbar android:id="@id/action_bar"
    or pass in the inflated Toolbar as root
  • custom Toolbar implementations may need a little adjustment,
    but then you could encapsulate this in that custom class.

Though I only tested with support:v22.

/** @param root usually Activity.getWindow().getDecorView() or your custom Toolbar */
public static @Nullable View findActionBarTitle(@NonNull View root) {
    return findActionBarItem(root, "action_bar_title", "mTitleTextView");
}
/** @param root usually Activity.getWindow().getDecorView() or your custom Toolbar */
public static @Nullable View findActionBarSubTitle(@NonNull View root) {
    return findActionBarItem(root, "action_bar_subtitle", "mSubtitleTextView");
}

private static @Nullable View findActionBarItem(@NonNull View root,
        @NonNull String resourceName, @NonNull String toolbarFieldName) {
    View result = findViewSupportOrAndroid(root, resourceName);

    if (result == null) {
        View actionBar = findViewSupportOrAndroid(root, "action_bar");
        if (actionBar != null) {
            result = reflectiveRead(actionBar, toolbarFieldName);
        }
    }
    if (result == null && root.getClass().getName().endsWith("widget.Toolbar")) {
        result = reflectiveRead(root, toolbarFieldName);
    }
    return result;
}

@SuppressWarnings("ConstantConditions")
private static @Nullable View findViewSupportOrAndroid(@NonNull View root, @NonNull String resourceName) {
    Context context = root.getContext();
    View result = null;
    if (result == null) {
        int supportID = context.getResources().getIdentifier(resourceName, "id", context.getPackageName());
        result = root.findViewById(supportID);
    }
    if (result == null) {
        int androidID = context.getResources().getIdentifier(resourceName, "id", "android");
        result = root.findViewById(androidID);
    }
    return result;
}

@SuppressWarnings("unchecked")
public static <T> @Nullable T reflectiveRead(@NonNull Object object, @NonNull String fieldName) {
    try {
        Field field = object.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        return (T)field.get(object);
    } catch (Exception ex) {
        Log.w("HACK", "Cannot read " + fieldName + " in " + object, ex);
    }
    return null;
}
Community
  • 1
  • 1
TWiStErRob
  • 44,762
  • 26
  • 170
  • 254
  • Is it possible that the `resourceName` would change in future versions of the support library? – B W Aug 24 '16 at 16:06
  • @bwicks Very unlikely, but yes. It probably doesn't matter, because the resource names are locked in at the time of compile. So even though they release a new support lib version, your app should be safe, because it contains the old one. That is, until you update your dependencies, build and republish. Also notice that `R.id.action_bar_title` is a public resource, while the `android.R.id.action_bar_title` it is mirroring, is not (this is why `getIdentifier` is used). You should be more concerned that the Android platform changes the name where the compile-time protection doesn't apply. – TWiStErRob Aug 24 '16 at 17:14
7

If you are using Toolbar with support v7:21. Check out the following code:

Field titleField = Toolbar.class.getDeclaredField("mTitleTextView");
        titleField.setAccessible(true);
        TextView barTitleView = (TextView) titleField.get(mToolbar);
        barTitleView.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub

            }
        });
Dejavu
  • 71
  • 1
  • 1
7

You can do this easily using Toolbar. Define toolbar in layout xml file as given below:

 <android.support.v7.widget.Toolbar
    android:id="@+id/toolbar"
    android:layout_width="match_parent"
    android:layout_height="?actionBarSize"
    android:background="?colorPrimary"
    app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
    app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">

    <TextView
        android:id="@+id/toolbarTitle"
        style="@style/TextAppearance.Widget.AppCompat.Toolbar.Title"
        android:background="?attr/selectableItemBackground"
        android:layout_width="wrap_content"
        android:gravity="center_vertical"
        android:layout_height="match_parent" />
</android.support.v7.widget.Toolbar>

Then you can set the listener in Activity using this code:

setSupportActionBar((Toolbar) findViewById(R.id.toolbar));

TextView toolbarTitle= (TextView) findViewById(R.id.toolbarTitle);
toolbarTitle.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            // DO SOMETHING HERE
        }
    });
jimmy0251
  • 16,293
  • 10
  • 36
  • 39
4

If you want to use the currently existing ActionBar and not the Toolbar, use the following:

ActionBar actBar = getSupportActionBar();
if(actBar != null) {
    actBar.setTitle(R.string.your_ab_title);
 }

//Set actions to take when the AB is clicked
Toolbar ab = findViewById(R.id.action_bar);
if(ab != null){
    for (int i= 0; i < ab.getChildCount(); i++){

        View child = ab.getChildAt(i);

        if(child instanceof TextView || child instanceof ImageView) {
            child.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    String url = "http://www.HoverDroids.com";

                    Intent i = new Intent(Intent.ACTION_VIEW);
                    i.setData(Uri.parse(url));
                    startActivity(i);
                }
            });
        }
     }
}    
Chris Sprague
  • 3,158
  • 33
  • 24
2

If you know the actual text that is in your Title, and you are reasonably sure that no other TextView on the screen shares that title, you can use a recursive View tree search to find it.

This is a great solution because it doesn't require reflection of internal knowledge of how to Toolbar is constructed, and gives you direct access to the TextView.

@Nullable
public static TextView findTextViewWithText(@Nullable View toCheck, String toFind) {

    if (toCheck instanceof TextView) {
        String foundText = ((TextView) toCheck).getText().toString();
        if (foundText.equals(toFind)) {
            return (TextView) toCheck;
        }

    } else if (toCheck instanceof ViewGroup) {
        for (int i = 0; i < ((ViewGroup) toCheck).getChildCount(); i++) {
            TextView found = findTextViewWithText(((ViewGroup) toCheck).getChildAt(i), toFind);
            if (found != null) {
                return found;
            }
        }
    }
    return null;
}

The most reliable view to call this on is the decor view but feel free to experiment what works best for your purposes, your mileage may vary.

View found = findTextViewWithText(
    getActivity().getWindow().getDecorView(), "My Title");
if (found != null) {
  // Do something, like set a click listener
}
Kevin Grant
  • 2,311
  • 23
  • 17
2

I know its too late, but for or those who use SupportActionBar like this and still have not found a clean solution:

Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);

For the default configuration without logo and custom views, 1st item (index 0) will be the Home/Back ImageView, 2nd item will be our Title TextView and 3rd item will be the OptionMenu Imageview.

Getting child at index 1 would return title. Adding an OnClickListener to the child will make it work like a chram:

    Toolbar toolbar = findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);
    toolbar.getChildAt(1).setOnClickListener(v -> {
       // title is clicked, call ur function here
       
       // can also verify that the view is title itself by converting it to textview
       try {
            String title  = ((TextView)v).getText().toString();
            // title will be your activity title 
        } catch (Exception e) {
            e.printStackTrace();
            // if you got an exception, the view is not title. 
            // Check changing the index, in case you have custom views in the toolbar.
        }
    });
Vikram Baliga
  • 444
  • 5
  • 9
0

You can do this easily using Toolbar. Define toolbar in layout xml file as given below:

    <android.support.v7.widget.Toolbar
    android:id="@+id/MainActivityToolbar"
    android:layout_width="match_parent"
    android:layout_height="?attr/actionBarSize"
    android:background="@color/colorPrimary"
    app:layout_scrollFlags="scroll|enterAlways"
    app:popupTheme="@style/ThemeOverlay.AppCompat.Light" >
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:text="@string/app_name"
            android:textSize="30sp"
            tools:ignore="RelativeOverlap"

            android:layout_marginTop="10dp"
            android:layout_marginBottom="10dp"
            android:layout_marginRight="10dp"
            android:layout_marginLeft="10dp"
            />
        <Button
            android:id="@+id/LogOutButton"
            android:layout_width="100dp"
            android:layout_height="wrap_content"
            android:layout_alignParentEnd="true"
            android:layout_alignParentRight="true"

            android:layout_marginTop="10dp"
            android:layout_marginBottom="10dp"
            android:layout_marginRight="10dp"
            android:layout_marginLeft="10dp"

            android:text="@string/logout" />
    </RelativeLayout>

</android.support.v7.widget.Toolbar>

Then you can set the listener in Activity using this code:

setSupportActionBar((Toolbar) findViewById(R.id.MainActivityToolbar));

logOutButton =  findViewById(R.id.LogOutButton);
logOutButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
   //define your function for logout or something else 
   LogOut();
 }
});
Wajid khan
  • 842
  • 9
  • 18
0

I know it's very late to comment here but I came across this question when I searched for how to add OnClick for Action bar title. Below is what I found and worked for me, hope it will help someone like me.

I wrote it for a fragment in my app.

        ActionBar actionBar = ((AppCompatActivity) getActivity()).getSupportActionBar();
        actionBar.setTitle(""); 
        ((AppCompatActivity) getActivity()).setSupportActionBar((Toolbar) getActivity().findViewById(R.id.toolbar));
        TextView toolbarTitle = (TextView) getActivity().findViewById(R.id.toolbarTitle);
        toolbarTitle.setText("New title");
        toolbarTitle.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
              // Action bar title clicked
            }
        });
        actionBar.show();
Sijeesh
  • 197
  • 2
  • 9
0

There is a class called ToolbarUtils:

It provides methods getTitleTextView() and getSubtitleTextView(), however you need to add @SuppressLint({"RestrictedApi"}) to your method as it won't compile otherwise.

fm-sys
  • 111
  • 1
  • 6