I've been recently developing an Android app, in which i need to have a custom layout and dimension for the tab bar. The way that i did it until now is by using Jake Wharton's ActionBarSherlock library to support pre-HoneyComb Android versions, and by applying a style to the app in which i edit the actionBarSize style item.
Now, I've been testing the app on the Galaxy S3 (with Jellybean on it), and the tab bar height doesn't change anymore according to the actionBarSize value.
So I've started looking through JellyBean's code, and I found this method in the ActionBarPolicy class:
public int getTabContainerHeight() {
TypedArray a = mContext.obtainStyledAttributes(null, R.styleable.ActionBar,
com.android.internal.R.attr.actionBarStyle, 0);
int height = a.getLayoutDimension(R.styleable.ActionBar_height, 0);
Resources r = mContext.getResources();
if (!hasEmbeddedTabs()) {
// Stacked tabs; limit the height
height = Math.min(height,
r.getDimensionPixelSize(R.dimen.action_bar_stacked_max_height));
}
a.recycle();
return height;
}
From what i gather in this method, it seems that JellyBean limits the height of the TabBar, when the app is in portrait mode, by setting the tab bar height to the "action_bar_stacked_max_height" dimension value (which is 48dp, in 4.1's /values/dimen.xml file), even though I've set the action bar height in actionBarSize.
I've tried overriding this dimension value, by setting it to my own value in my own dimen.xml file, but i had no luck. It didn't work.
My question:
Do you guys know of a way in which i can override the"action_bar_stacked_max_height" dimen value?
Thank you in advance!
Try putting android:actionBarSize and actionBarSize under the Theme you are using, like so:
<style name="Theme.white_style" parent="#android:style/Theme.Holo.Light.DarkActionBar">
<item name="android:actionBarSize">55dp</item>
<item name="actionBarSize">55dp</item>
</style>
Try using android:height like the following:
<style name="AppActionBar">
<item name="android:height">50dp</item>
</style>
<style name="MainActivityStyle" parent="#android:style/Theme.Holo">
<item name="android:actionBarStyle">#style/AppActionBar</item>
</style>
it seems to me that it was done on purpose and I don't know why. There are some variables in bools.xml
<bool name="action_bar_embed_tabs">true</bool>
<bool name="action_bar_embed_tabs_pre_jb">false</bool>
The height of embed tabs is limited to 48dip in ActionBarPolicy.java, as you have already mentioned. That's why such behaviour can be seen only in Android JellyBean or higher. I can't find a better solution than to make some java reflection. So here is the code
private void hackJBPolicy() {
View container = findScrollingTabContainer();
if (container == null) return;
try {
int height = getResources().getDimensionPixelSize(R.dimen.action_bar_height);
Method method = container.getClass()
.getDeclaredMethod("setContentHeight", Integer.TYPE);
method.invoke(container, height);
} catch (NoSuchMethodException e) {
Log.e(LOG_TAG, e.getLocalizedMessage(), e);
} catch (IllegalArgumentException e) {
Log.e(LOG_TAG, e.getLocalizedMessage(), e);
} catch (IllegalAccessException e) {
Log.e(LOG_TAG, e.getLocalizedMessage(), e);
} catch (InvocationTargetException e) {
Log.e(LOG_TAG, e.getLocalizedMessage(), e);
}
}
private View findScrollingTabContainer() {
View decor = getWindow().getDecorView();
int containerId = getResources().getIdentifier("action_bar_container", "id", "android");
// check if appcompat library is used
if (containerId == 0) {
containerId = R.id.action_bar_container;
}
FrameLayout container = (FrameLayout) decor.findViewById(containerId);
for (int i = 0; i < container.getChildCount(); i++) {
View scrolling = container.getChildAt(container.getChildCount() - 1);
String simpleName = scrolling.getClass().getSimpleName();
if (simpleName.equals("ScrollingTabContainerView")) return scrolling;
}
return null;
}
Use the method hackJBPolicy() in your onCreate. Notice that I used ActionBar from appcompat library. Here is the link to the sample project with the use of this workaround.
After all, it seems to me that it would be easier in future to create custom view aligned to the top of the screen instead of using ActionBar in tabs mode if your ui design is a bit far from guidlines.
Related
I am currently trying to change the height of the default MasterDetailPage using the material design with FormsAppCompatActivity.
Basically I got the custom renderer to work, but I am trying to resize it dynamically based on toolbar size. The reason being is that different devices have different sized toolbars. Furthermore, I got the resizing to work but the black shadow that comes with the MasterDetailPage stays in place and does not seem to pass through the AddView function.
bool firstDone;
public override void AddView(Android.Views.View child)//Android.Views.View
{
var padding = child.GetType().GetRuntimeProperty("TopPadding").GetValue(child);//tried padding but did not work
if (firstDone)
{
LayoutParams p = (LayoutParams)child.LayoutParameters;
//p.TopMargin = padding;
p.TopMargin = 200;// Need this to be dynamic for different devices
base.AddView(child, p);
}
else
{
firstDone = true;
base.AddView(child);
}
}
In my application manifest I've add android:configChanges to prevent activity reload/restart on rotate
<activity
android:name=".MainActivity"
android:label="#string/app_name"
android:configChanges="orientation|keyboardHidden|screenSize" >
it works, but supportActionBar ( I'm using AppCompat ) preserves his height with small font size.
ActionBar should be bigger in portrait and smaller in landscape, but it keeps the initial value:
if I start in landscape, the actionbar stay thin in portrait
if I start in portrait, the actionbar stay big in landscape
Removing android:configChanges="orientation|keyboardHidden|screenSize" is the only solution I've found, but the app restart on rotate, and I need to preserve application content
Starting in portrait
Starting in landscape
Starting in landscape and rotating screen to portrait (small action bar and small font height)
By setting android:configChanges="orientation|keyboardHidden|screenSize"
You declare that you will handle these config changes by yourself. In normal cases, you should not set that, and let Android recreate your Activity.
Edit:
If you want to keep the line android:configChanges, you have to override onConfigChanged() and change everything needed by yourself, e.g. the size of the ActionBar/ToolBar.
As others have pointed out you should save and restore the instance state instead of handling configuration changes yourself if possible. If you have good reason not to do that you can try to update the toolbar's height and text appearance after the configuration change.
The following code should work for the support library version of Toolbar. The attributes actionBarSize, titleTextAppearance and subtitleTextAppearance are provided by the support library.
The code assumes that you have a custom attribute appToolbarStyle declared in attrs.xml. If you don't need that you can adapt the code to use R.style.Widget_AppCompat_Toolbar directly instead.
import android.support.v7.widget.Toolbar;
...
private Toolbar toolbar;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_activity);
toolbar = findViewById(R.id.toolbar);
}
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
updateToolbar();
}
private void updateToolbar() {
if (toolbar == null)
return;
final Context context = toolbar.getContext();
int[] attr = new int[] { R.attr.actionBarSize, R.attr.appToolbarStyle };
int idxActionBarSize = 0;
int idxAppToolbarStyle = 1;
TypedArray a = context.obtainStyledAttributes(attr);
int actionBarSize = a.getDimensionPixelSize(idxActionBarSize, 0);
int appToolbarStyle = a.getResourceId(idxAppToolbarStyle, R.style.Widget_AppCompat_Toolbar);
a.recycle();
if (actionBarSize != 0) {
ViewGroup.LayoutParams layoutParams = toolbar.getLayoutParams();
if (layoutParams != null) {
layoutParams.height = actionBarSize;
}
toolbar.setMinimumHeight(actionBarSize);
}
attr = new int[] { R.attr.titleTextAppearance, R.attr.subtitleTextAppearance };
int idxTitleTextAppearance = 0;
int idxSubtitleTextAppearance = 1;
a = context.obtainStyledAttributes(appToolbarStyle, attr);
int titleTextAppearance = a.getResourceId(idxTitleTextAppearance, 0);
int subtitleTextAppearance = a.getResourceId(idxSubtitleTextAppearance, 0);
a.recycle();
if (titleTextAppearance != 0) {
toolbar.setTitleTextAppearance(context, titleTextAppearance);
}
if (subtitleTextAppearance != 0) {
toolbar.setSubtitleTextAppearance(context, subtitleTextAppearance);
}
toolbar.requestLayout();
}
If you want to keep android:configChanges, you can use this to force 56dp toolbar height, align icons and fix small text issue:
Toolbar XML:
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="56dp"
android:minHeight="56dp"
android:background="?attr/colorPrimary"
app:popupTheme="#style/AppTheme.PopupOverlay"
app:titleTextAppearance="#style/titleTextAppearance" />
Styles XML:
<style name="titleTextAppearance" parent="#style/TextAppearance.Widget.AppCompat.Toolbar.Title">
<item name="android:textSize">20sp</item>
</style>
I have a custom RelativeLayout and I even have set setLayoutTransition(null);. I add this custom view to the WindowManager with ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).updateViewLayout(this, layoutParams);
I change views in the custom view AND I change the LayoutParams for the WindowManager and afterwards call updateViewLayout...
I think, the chang of the LayoutParams for the WindowManager is animated, but I'm not sure...
How can I disable ALL animations?
This one's a little annoying. Android animates all window changes, and they've made the flag to disable it private. You can disable window animations using reflection
WindowManager.LayoutParams wp = new WindowManager.LayoutParams();
String className = "android.view.WindowManager$LayoutParams";
try {
Class layoutParamsClass = Class.forName(className);
Field privateFlags = layoutParamsClass.getField("privateFlags");
Field noAnim = layoutParamsClass.getField("PRIVATE_FLAG_NO_MOVE_ANIMATION");
int privateFlagsValue = privateFlags.getInt(wp);
int noAnimFlag = noAnim.getInt(wp);
privateFlagsValue |= noAnimFlag;
privateFlags.setInt(wp, privateFlagsValue);
// Dynamically do stuff with this class
// List constructors, fields, methods, etc.
} catch (ClassNotFoundException e) {
Logger.l.e(e.toString());
// Class not found!
} catch (Exception e) {
Logger.l.e(e.toString());
// Unknown exception
}
Now wp will not animate layout changes. Note you'll probably see flicker when you change the window size. I haven't found a way to work around that yet.
I need to position a TextView the way its baseline is 20dp from the bottom of the container.
How can I achieve this?
The layout with bottom margin or padding produces the same result.
I would like to make the text 'sit' on the purple line.
When I write 'sit' I mean, the 'wert' should touch the line, not 'q...y'.
The padding / margin is equal to the purple square size:
If you still need it, I wrote custom method, to not create lots of custom views. It works for me with TextView:
public static void applyExistingBotMarginFromBaseline(View view) {
final int baseline = view.getBaseline();
final int height = view.getHeight();
final ViewGroup.MarginLayoutParams marginLayoutParams;
try {
marginLayoutParams = ((ViewGroup.MarginLayoutParams) view.getLayoutParams());
} catch (ClassCastException e) {
throw new IllegalArgumentException("Applying margins on a view with wrong layout params.");
}
final int baselineMarginValue = baseline + marginLayoutParams.bottomMargin;
marginLayoutParams.bottomMargin = baselineMarginValue - height;
view.setLayoutParams(marginLayoutParams);
}
You can apply it when view is measured already, so like this:
final TextView title = (TextView) findViewById(R.id.title);
title.post(new Runnable() {
#Override public void run() {
Utils.applyExistingBotMarginFromBaseline(title);
}
});
Also you can use databinding framework and write your own custom BindingAdapter with a bit customized method, to use it from xml.
Your problem is not the padding/margin referenced to the parent, I think is about your font, I recommend you to change the fontFamily:"yourStyle"
even worst you have to re-difine your own font style which is explained here Custom fonts and XML layouts (Android) or Set specific font in a styles.xml
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().