Programmatically Modify ActionBarDrawerToggle Drawable - android

Does anyone know a way to get the drawable that is used for the ActionBarDrawerToggle after setting it in the constructor by resource ID? I am using a navigation drawer and I want to apply a color filter to the icon programmatically, but I can't figure out how to access it as a drawable. Any help would be appreciated. Thanks!

As the framework's ID for that View is hidden, the only way I've found to access it is by walking through the hierarchy based on the Home View's position in it.
public ImageView getUpView()
{
ViewGroup parentView = (ViewGroup) findViewById(android.R.id.home).getParent();
return (ImageView) parentView.getChildAt(0);
}

Related

When to set Resource for ImageView in Android NavigationDrawer

I have trouble deciding where to set the image of an ImageView contained in the Android NavigationDrawer.
My Android app has a NavigationDrawer that contains an ImageView nested into a couple of other elements inside the NavigationDrawer header layout (app:headerLayout="#layout/navdrawer_header), and my navdrawer_header.xml has the following structure:
<LinearLayout android:id="#+id/navdrawer_header">
...
<ImageView android:id=#+id/image_view
android:layout_width="match_parent"
android:layout_height="match_parent"/>
...
</LinearLayout>
I am setting the image (img.jpg in my drawable folder) via
ImageView imageView = (ImageView) view.findViewById(R.id.image_view);
imageView.setImageResource(R.drawable.img);
If called from OnCreate inside my MainActivity, the imageView will be null and the app crashes. The same if I move the above lines to OnPostCreate or even OnStart.
My understanding is that the view is not initialized yet at that point in time and therefore findViewById returns null. As a workaround, I have overwritten the OnDrawerOpened method and moved my code from above to that method.
This works just fine, but I am pretty sure it is not the best way to go since the method is called every time the drawer is opened. Instead, I would need a method that is only called once upon the creation of the drawer.
What is the best practice here, i.e. where should I set the resource of an ImageView that is contained in the NavigationDrawer?
I have tried omitting view. and experimented with different values of view, for example by assigning to it the NavigationView or DrawerLayout associated with my drawer, but it does not solve the issue.
Try this:
NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
View view = navigationView.getHeaderView(0);
ImageView imageView = (ImageView) view.findViewById(R.id.image_view);
imageView.setImageResource(R.drawable.img);

Android Toolbar "Up" Arrow in custom implementation

I am making a very custom Toolbar (don't freak, it's for good, not evil, I promise), and I want to put the "Up" arrow (the arrow that appears when there is a parent Activity set or for other reasons and has the nice ripple touch effect on Lollipop) in a custom location.
Is there a way to get this arrow as a View or asset to use somewhere else in the Toolbar?
I could not even find the image asset for the arrow. Any ideas?
For an example, I want top be able to do something like:
mToolbar.addView(arrowView, 0);
mToolbar.addView(titleView, 1);
Maybe this will help, looks like something that you want, or you want completely custom up button with different position and view?
If you don't want to place arrow icon as custom view in Toolbar, alternatively you can set content description to the navigation button and find view reference later. Every View or ViewGroup supports finding views with content description.
public static View getNavigationIcon(Toolbar toolbar){
//check if contentDescription previously was set
boolean hadContentDescription = TextUtils.isEmpty(toolbar.getNavigationContentDescription());
String contentDescription = !hadContentDescription ? toolbar.getNavigationContentDescription() : "navigationIcon";
toolbar.setNavigationContentDescription(contentDescription);
ArrayList<View> potentialViews = new ArrayList<View>();
//find the view based on it's content description, set programatically or with android:contentDescription
toolbar.findViewsWithText(potentialViews,contentDescription, View.FIND_VIEWS_WITH_CONTENT_DESCRIPTION);
//Nav icon is always instantiated at this point because calling setNavigationContentDescription ensures its existence
View navIcon = null;
if(potentialViews.size() > 0){
navIcon = potentialViews.get(0); //navigation icon is ImageButton
}
//Clear content description if not previously present
if(hadContentDescription)
toolbar.setNavigationContentDescription(null);
return navIcon;
}
Toolbar is itself a ViewGroup were you can add different views for eg.
<android.support.v7.widget.Toolbar
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:minHeight="?attr/actionBarSize"
app:theme="#style/ThemeOverlay.AppCompat.ActionBar" >
<TextView ..../>
<ImageView ...../>
</android.support.v7.widget.Toolbar>
Refer your toolbar and its elements from your java code to have control over it as:
Toolbar toolbar = (Toolbar) findViewById(R.id.my_awesome_toolbar);
ImageView img = (ImageView) toolbar.findViewById(R.id.imgv);

display view on top of action bar

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:
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.
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
After struggling with it myself quite some time, here's the solution (tested it - working good):
The general steps are:
Create a wrapper view
Detach the screen view children, place the wrapper, and attach the children
Inflate the content to the children
Controling the wrapper will help you control exactly the action bar and the content below it all together.
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!
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);
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);}
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.
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.
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.
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 :)
(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);
}
}
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.

getResource ID of Drawable shown in View v

I am Trying to do something similar to this and I have Problems, too. I am using dynamic ImageViews on my Screen. Per OnClickListener I am changing the Resource ID's of the Views.
I want to be able to tell what the Resource ID of the Drawable shown in the View that was clicked is.
OnClickListener(View v)
{
ImageView temp=(ImageView)v;
//I Can change Image Resource of the Clicked View with this->
temp.setImageResource(R.drawable.picture_1);
//but how can I find out what Resource ID the Drawable has that is displayed in the View?
//(I'm searching for a method ->temp.getImageResource();)
}
Thanks for your help! Philipp
Something like this should work
temp.setImageResource(R.drawable.picture_1);
temp.setTag(R.drawable.picture_1);
and then to get the resource id
temp.getTag();

Reseting the background color of a View

I'm trying to restore the background Color of a View.
I have several selectable Views. When the user clicks one of those Views, the following code is executed and the View becomes Yellow:
View newSelection, previousSelection;
...
if(previousSelection != null) {
previousSelection.setBackgroundColor(Color.BLACK); // problem here
}
newSelection.setBackgroundColor(Color.YELLOW);
However, I want to reset the color of the previously selected View. However, I do not know which color it was (I'm setting it to Color.BLACK in the above code). I was not able to find a getBackgroundColor or similar method in the View class. If I had it, I could save the previous color and just put it back when the new View is selected.
use View.getBackground(), it returns the current 'Drawable' background of the view which can then be used in View.setBackgroundDrawable()
View theView;
Drawable originalBackground;
...
originalBackground = theView.getBackground();
theView.setBackgroundColor(Color.YELLOW);
...
theView.setBackgroundDrawable(originalBackground);
I'm not sure exactly what you are trying to accomplish but perhaps a ColorStateList would come in handy here.
You can try setting the previous color as a tag of the view.
For example
View newSelection, previousSelection;
newSelection.setTag(Color.Green);
previousSelection.setTag(Color.Black);
if(previousSelection != null) {
previousSelection.setBackgroundColor((int)previousSelection.getTag());
}
newSelection.setBackgroundColor(Color.YELLOW);
I haven't tried the code if there is an error but the flow on how to implement is there.

Categories

Resources