remove padding around action bar left icon on Android 4.0+ - android

I want to remove the padding around the icon on the left in the standard android 4.0+ action bar. I'm setting the icon with:
getActionBar().setIcon(getResources().getDrawable(R.drawable.ic_action_myapp));
And I would like the icon to fill vertically the space, touching both top and bottom, similar to what soundcloud app does:

Digging into AOSP sources, it seems the code involved is in com.android.internal.widget.ActionBarView.java. In particular the relevant part is the onLayout() method of the inner class ActionBarView$HomeView, partially reported below (lines 1433-1478):
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
...
final LayoutParams iconLp = (LayoutParams) mIconView.getLayoutParams();
final int iconHeight = mIconView.getMeasuredHeight();
final int iconWidth = mIconView.getMeasuredWidth();
final int hCenter = (r - l) / 2;
final int iconTop = Math.max(iconLp.topMargin, vCenter - iconHeight / 2);
final int iconBottom = iconTop + iconHeight;
final int iconLeft;
final int iconRight;
int marginStart = iconLp.getMarginStart();
final int delta = Math.max(marginStart, hCenter - iconWidth / 2);
if (isLayoutRtl) {
iconRight = width - upOffset - delta;
iconLeft = iconRight - iconWidth;
} else {
iconLeft = upOffset + delta;
iconRight = iconLeft + iconWidth;
}
mIconView.layout(iconLeft, iconTop, iconRight, iconBottom);
}
the same widget use this layout, defined in res/layout/action_bar_home.xml:
<view xmlns:android="http://schemas.android.com/apk/res/android"
class="com.android.internal.widget.ActionBarView$HomeView"
android:layout_width="wrap_content"
android:layout_height="match_parent">
<ImageView android:id="#android:id/up"
android:src="?android:attr/homeAsUpIndicator"
android:layout_gravity="center_vertical|start"
android:visibility="gone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="-8dip" />
<ImageView android:id="#android:id/home"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dip"
android:layout_marginTop="#android:dimen/action_bar_icon_vertical_padding"
android:layout_marginBottom="#android:dimen/action_bar_icon_vertical_padding"
android:layout_gravity="center"
android:adjustViewBounds="true"
android:scaleType="fitCenter" />
</view>
According to sources the icon is shown in the Imageview with id=android.R.id.home. The onLayout() method reported above takes account of the ImageView margins defined in the layout, which can't be set via theme/style override because they use the value #android:dimen/action_bar_icon_vertical_padding.
All you can do is to get rid of those values at runtime, and set them according your needs:
simply retrieve the ImageView and set its top and bottom margins to 0. Something like this:
ImageView icon = (ImageView) findViewById(android.R.id.home);
FrameLayout.LayoutParams iconLp = (FrameLayout.LayoutParams) icon.getLayoutParams();
iconLp.topMargin = iconLp.bottomMargin = 0;
icon.setLayoutParams( iconLp );
EDIT: I've just realized I didn't cover how to get rid of left padding. Solution below.
Left padding on actionbar is affected by the Navigating Up behavior of actionbar icon. When that is disabled (via ActionBar.setDisplayHomeAsUpEnabled(false)) the left/up indicator is gone, but a left padding is used as well. A simple solution:
enable the actionbar up navigation using ActionBar.setDisplayHomeAsUpEnabled(true) in order to consider the indicator view in the layout process
force the drawable used as up indicator in your res/values-v14/styles.xml to null
eg:
<style name="AppBaseTheme" parent="android:Theme.Holo.Light.DarkActionBar">
<!-- API 14 theme customizations can go here. -->
<item name="android:homeAsUpIndicator">#null</item>
</style>

I found an other resolution (reference appcompat-v7 ) that change the toolbarStyle ,following code:
<item name="toolbarStyle">#style/Widget.Toolbar</item>
<style name="Widget.Toolbar" parent="#style/Widget.AppCompat.Toolbar">
<item name="contentInsetStart">0dp</item>
</style>

use custom layout for ActionBar
public class TestActivity extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final ActionBar actionBar = getActionBar();
actionBar.setCustomView(R.layout.actionbar_custom_view_home);
actionBar.setDisplayShowTitleEnabled(false);
actionBar.setDisplayShowCustomEnabled(true);
actionBar.setDisplayUseLogoEnabled(false);
actionBar.setDisplayShowHomeEnabled(false);
setContentView(R.layout.main);
}
public void Click(View v) {
if (v.getId() == R.id.imageIcon) {
Log.e("click on--> ", "Action icon");
}
}
}
actionbar_custom_view_home.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/linearLayout1"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center" >
<ImageView
android:id="#+id/imageIcon"
android:onClick="Click"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:src="#drawable/ic_launcher" />
<TextView
android:id="#+id/textView1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Large Icon With Title"
android:textAppearance="?android:attr/textAppearanceLarge" />
</LinearLayout>

Enhanced parrzhang reply in remove padding around action bar left icon on Android 4.0+
private void adjustHomeButtonLayout(){
ImageView view = (ImageView)findViewById(android.R.id.home);
if(view.getParent() instanceof ViewGroup){
ViewGroup viewGroup = (ViewGroup)view.getParent();
View upView = viewGroup.getChildAt(0);
if(upView != null && upView.getLayoutParams() instanceof FrameLayout.LayoutParams){
FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) upView.getLayoutParams();
layoutParams.width = 20;// **can give your own width**
upView.setLayoutParams(layoutParams);
}
}
}

To set the height of ActionBar you can create new Theme like this one:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Theme.BarSize" parent="Theme.Sherlock.Light.DarkActionBar">
<item name="actionBarSize">48dip</item>
<item name="android:actionBarSize">48dip</item>
</style>
</resources>
and set this Theme to your Activity:
android:theme="#style/Theme.BarSize"
Now, set the height of the icons to "match_parent".
That would remove the top and bottom padding.
Now, the arrow at the left is inbuilt into the framework, so you have two options for a workaround:
Use ActionBarSherlock. It uses it's own drwables and resources, so you can modify the arrow icon to an emty png, so that your up icon would move to extreme left.
The up/back icon arises from:
public boolean onOptionsItemSelected(MenuItem item)
{
switch (item.getItemId())
{
case android.R.id.home:
NavUtils.navigateUpFromSameTask(this);
return true;
}
}
So, instead of using this option for up button, you can make another actionbar option, which has an intent for the previous activity, and then place that icon on your action bar.
It will be a bigger workaround though.
Hope that helps.. :)

you can find homeview in actionbarview define like this:
<view xmlns:android="http://schemas.android.com/apk/res/android"
class="com.android.internal.widget.ActionBarView$HomeView"
android:layout_width="wrap_content"
android:layout_height="match_parent">
<ImageView android:id="#android:id/up"
android:src="?android:attr/homeAsUpIndicator"
android:layout_gravity="center_vertical|start"
android:visibility="gone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="-8dip" />
<ImageView android:id="#android:id/home"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dip"
android:layout_marginTop="#android:dimen/action_bar_icon_vertical_padding"
android:layout_marginBottom="#android:dimen/action_bar_icon_vertical_padding"
android:layout_gravity="center"
android:adjustViewBounds="true"
android:scaleType="fitCenter" />
but you cannot get upView by findViewById(android.R.id.up).
so you can get homeView and get its parent view ,set upview width 0
ImageView view = (ImageView)findViewById(android.R.id.home);
if(view.getParent() instanceof ViewGroup){
ViewGroup viewGroup = (ViewGroup)view.getParent();
View upView = viewGroup.getChildAt(0);
FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) upView.getLayoutParams();
layoutParams.width = 0;
upView.setLayoutParams(layoutParams);
}

Related

How to set max width for BottomSheetDialogFragment

Question
I'm using the BottomSheetDialogFragment for my modal bottom sheet and would like to set a maximum width so that on tablets/large screens the BottomSheet doesn't occupy the entire width of the screen. How do I approach solving this? Thanks!
Relevant code & resources
fragment_bottomsheet.xml:
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
style="#style/BottomSheetStyle">
<GridLayout
android:id="#+id/bottom_sheet"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:alignmentMode="alignBounds"
android:columnOrderPreserved="false"
android:columnCount="3"
android:paddingTop="16dp"
android:paddingBottom="8dp"
android:paddingRight="8dp"
android:paddingLeft="8dp"
app:layout_behavior="android.support.design.widget.BottomSheetBehavior">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawableTop="#drawable/image1"
android:text="Open"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawableTop="#drawable/image2"
android:text="Save"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawableTop="#drawable/image3"
android:text="Send"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawableTop="#drawable/image4"
android:text="Upload"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawableTop="#drawable/image5"
android:text="Share"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawableTop="#drawable/iamge6"
android:text="More"/>
</GridLayout>
</android.support.design.widget.CoordinatorLayout>
res/values/styles.xml:
<style name="BottomSheetStyle">
<item name="android:layout_height">match_parent</item>
<item name="android:layout_width">match_parent</item>
<item name="android:layout_gravity">center_horizontal</item>
</style>
res/values-w600dp/styles.xml:
<style name="BottomSheetStyle">
<item name="android:layout_height">match_parent</item>
<item name="android:layout_width">640dp</item>
<item name="android:layout_gravity">center_horizontal</item>
</style>
I found a solution which works for me:
#Override
public void onResume() {
super.onResume();
// Resize bottom sheet dialog so it doesn't span the entire width past a particular measurement
WindowManager wm = (WindowManager) getActivity().getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
DisplayMetrics metrics = new DisplayMetrics();
display.getMetrics(metrics);
int width = metrics.widthPixels < 1280 ? metrics.widthPixels : 1280;
int height = -1; // MATCH_PARENT
getDialog().getWindow().setLayout(width, height);
}
Essentially this allows me to dynamically specify dimensions for my BottomSheet based on display.
A variation of the OP's answer that takes into consideration device's pixel density and orientation:
public final class MyBottomSheetDialogFragment extends BottomSheetDialogFragment {
...
private static int dpToPx(int dp) {
// https://developer.android.com/guide/practices/screens_support.html#dips-pels
float density = Resources.getSystem().getDisplayMetrics().density;
return (int) ((dp * density) + 0.5f);
}
#Override
public void onResume() {
super.onResume();
Configuration configuration = getActivity().getResources().getConfiguration();
if (configuration.orientation == Configuration.ORIENTATION_LANDSCAPE &&
configuration.screenWidthDp > 450) {
// you can go more fancy and vary the bottom sheet width depending on the screen width
// see recommendations on https://material.io/components/sheets-bottom#specs
getDialog().getWindow().setLayout(ViewUtils.dpToPx(450), -1);
}
}
}
As you mention, BottomSheetDialogFragment does not seem to respect "wrap_content" (it always matches parent on width, even if all the views it contains have a width of "wrap_content"). I found that the best workaround for me was to utilize OnGlobalLayoutListener to listen for the appropriate time to adjust the width. Example:
#Override
public void setupDialog(final Dialog dialog, int style) {
super.setupDialog(dialog, style);
View v = View.inflate(getActivity(), R.layout.my_bottom_sheet, null);
View viewWithGreatestWidth = v.findViewById(R.id.my_super_wide_view);
// Your view setup code goes here ....
if(getResources().getBoolean(R.bool.isTablet)) {
v.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
v.getViewTreeObserver().removeOnGlobalLayoutListener(this);
} else {
v.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
// Can also just use your own preset "max width" value here;
// This code just lets you fake "wrap_content" value
getDialog().getWindow().setLayout(viewWithGreatestWidth.getMeasuredWidth(), WindowManager.LayoutParams.WRAP_CONTENT);
}
});
}
dialog.setContentView(v);
}
If you want to do it purely in xml, you can set a style and use android:maxWidth
For example:
<LinearLayout
android:id="#+id/bottom_sheet"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="#color/white"
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior"
style="#style/styleWithFixedWidth"
>
and in styles.xml
<style name="styleWithFixedWidth">
<item name="android:maxWidth">550dp</item>
</style>

Expand/Collapse Lollipop toolbar animation (Telegram app)

I'm trying to figure out how the expand/collapse animation of the toolbar is done. If you have a look at the Telegram app settings, you will see that there is a listview and the toolbar. When you scroll down, the toolbar collapse, and when you scroll up it expands. There is also the animation of the profile pic and the FAB. Does anyone have any clue on that? Do you think they built all the animations on top of it? Maybe I'm missing something from the new APIs or the support library.
I noticed the same behaviour on the Google calendar app, when you open the Spinner (I don't think it's a spinner, but it looks like): The toolbar expands and when you scroll up, it collapse.
Just to clearify: I don't need the QuickReturn method. I know that probably Telegram app is using something similar. The exact method that I need is the Google Calendar app effect. I've tried with
android:animateLayoutChanges="true"
and the expand method works pretty well. But obviously, If I scroll up the ListView, the toolbar doesn't collapse.
I've also thought about adding a GestureListener but I want to know if there are any APIs or simpler methods of achieving this.
If there are none, I think I will go with the GestureListener. Hopefully to have a smooth effect of the Animation.
Thanks!
Edit :
Since the release of the Android Design support library, there's an easier solution. Check joaquin's answer
--
Here's how I did it, there probably are many other solutions but this one worked for me.
First of all, you have to use a Toolbar with a transparent background. The expanding & collapsing Toolbar is actually a fake one that's under the transparent Toolbar. (you can see on the first screenshot below - the one with the margins - that this is also how they did it in Telegram).
We only keep the actual Toolbar for the NavigationIcon and the overflow MenuItem.
Everything that's in the red rectangle on the second screenshot (ie the fake Toolbar and the FloatingActionButton) is actually a header that you add to the settings ListView (or ScrollView).
So you have to create a layout for this header in a separate file that could look like this :
<!-- The headerView layout. Includes the fake Toolbar & the FloatingActionButton -->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<RelativeLayout
android:id="#+id/header_container"
android:layout_width="match_parent"
android:layout_height="#dimen/header_height"
android:layout_marginBottom="3dp"
android:background="#android:color/holo_blue_dark">
<RelativeLayout
android:id="#+id/header_infos_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:padding="16dp">
<ImageView
android:id="#+id/header_picture"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginRight="8dp"
android:src="#android:drawable/ic_dialog_info" />
<TextView
android:id="#+id/header_title"
style="#style/TextAppearance.AppCompat.Title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="#+id/header_picture"
android:text="Toolbar Title"
android:textColor="#android:color/white" />
<TextView
android:id="#+id/header_subtitle"
style="#style/TextAppearance.AppCompat.Subhead"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/header_title"
android:layout_toRightOf="#+id/header_picture"
android:text="Toolbar Subtitle"
android:textColor="#android:color/white" />
</RelativeLayout>
</RelativeLayout>
<FloatingActionButton
android:id="#+id/header_fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
android:layout_margin="10dp"
android:src="#drawable/ic_open_in_browser"/>
</FrameLayout>
(Note that you can use negative margins/padding for the fab to be straddling on 2 Views)
Now comes the interesting part. In order to animate the expansion of our fake Toolbar, we implement the ListView onScrollListener.
// The height of your fully expanded header view (same than in the xml layout)
int headerHeight = getResources().getDimensionPixelSize(R.dimen.header_height);
// The height of your fully collapsed header view. Actually the Toolbar height (56dp)
int minHeaderHeight = getResources().getDimensionPixelSize(R.dimen.action_bar_height);
// The left margin of the Toolbar title (according to specs, 72dp)
int toolbarTitleLeftMargin = getResources().getDimensionPixelSize(R.dimen.toolbar_left_margin);
// Added after edit
int minHeaderTranslation;
private ListView listView;
// Header views
private View headerView;
private RelativeLayout headerContainer;
private TextView headerTitle;
private TextView headerSubtitle;
private FloatingActionButton headerFab;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View rootView = inflater.inflate(R.layout.listview_fragment, container, false);
listView = rootView.findViewById(R.id.listview);
// Init the headerHeight and minHeaderTranslation values
headerHeight = getResources().getDimensionPixelSize(R.dimen.header_height);
minHeaderTranslation = -headerHeight +
getResources().getDimensionPixelOffset(R.dimen.action_bar_height);
// Inflate your header view
headerView = inflater.inflate(R.layout.header_view, listview, false);
// Retrieve the header views
headerContainer = (RelativeLayout) headerView.findViewById(R.id.header_container);
headerTitle = (TextView) headerView.findViewById(R.id.header_title);
headerSubtitle = (TextView) headerView.findViewById(R.id.header_subtitle);
headerFab = (TextView) headerView.findViewById(R.id.header_fab);;
// Add the headerView to your listView
listView.addHeaderView(headerView, null, false);
// Set the onScrollListener
listView.setOnScrollListener(this);
// ...
return rootView;
}
#Override
public void onScrollStateChanged(AbsListView view, int scrollState)
{
// Do nothing
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount)
{
Integer scrollY = getScrollY(view);
// This will collapse the header when scrolling, until its height reaches
// the toolbar height
headerView.setTranslationY(Math.max(0, scrollY + minHeaderTranslation));
// Scroll ratio (0 <= ratio <= 1).
// The ratio value is 0 when the header is completely expanded,
// 1 when it is completely collapsed
float offset = 1 - Math.max(
(float) (-minHeaderTranslation - scrollY) / -minHeaderTranslation, 0f);
// Now that we have this ratio, we only have to apply translations, scales,
// alpha, etc. to the header views
// For instance, this will move the toolbar title & subtitle on the X axis
// from its original position when the ListView will be completely scrolled
// down, to the Toolbar title position when it will be scrolled up.
headerTitle.setTranslationX(toolbarTitleLeftMargin * offset);
headerSubtitle.setTranslationX(toolbarTitleLeftMargin * offset);
// Or we can make the FAB disappear when the ListView is scrolled
headerFab.setAlpha(1 - offset);
}
// Method that allows us to get the scroll Y position of the ListView
public int getScrollY(AbsListView view)
{
View c = view.getChildAt(0);
if (c == null)
return 0;
int firstVisiblePosition = view.getFirstVisiblePosition();
int top = c.getTop();
int headerHeight = 0;
if (firstVisiblePosition >= 1)
headerHeight = this.headerHeight;
return -top + firstVisiblePosition * c.getHeight() + headerHeight;
}
Note that there are some parts of this code I didn't test, so feel free to highlight mistakes. But overall, I'm know that this solution works, even though I'm sure it can be improved.
EDIT 2:
There were some mistakes in the code above (that I didn't test until today...), so I changed a few lines to make it work :
I introduced another variable, minHeaderTranslation, which replaced minHeaderHeight;
I changed the Y translation value applied to the header View from :
headerView.setTranslationY(Math.max(-scrollY, minHeaderTranslation));
to :
headerView.setTranslationY(Math.max(0, scrollY + minHeaderTranslation));
Previous expression wasn't working at all, I'm sorry about that...
The ratio calculation also changed, so that it now evolves from the bottom the toolbar (instead of the top of the screen) to the full expanded header.
Also check out CollapsingTitleLayout written by Chris Banes in Android team:
https://plus.google.com/+ChrisBanes/posts/J9Fwbc15BHN
Code: https://gist.github.com/chrisbanes/91ac8a20acfbdc410a68
Use design support library http://android-developers.blogspot.in/2015/05/android-design-support-library.html
include this in build.gradle
compile 'com.android.support:design:22.2.0'
compile 'com.android.support:appcompat-v7:22.2.+'
for recycler view include this also
compile 'com.android.support:recyclerview-v7:22.2.0'
<!-- AppBarLayout allows your Toolbar and other views (such as tabs provided by TabLayout)
to react to scroll events in a sibling view marked with a ScrollingViewBehavior.-->
<android.support.design.widget.AppBarLayout
android:id="#+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true">
<!-- specify tag app:layout_scrollFlags -->
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:layout_scrollFlags="scroll|enterAlways"/>
<!-- specify tag app:layout_scrollFlags -->
<android.support.design.widget.TabLayout
android:id="#+id/tabLayout"
android:scrollbars="horizontal"
android:layout_below="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
app:layout_scrollFlags="scroll|enterAlways"/>
<!-- app:layout_collapseMode="pin" will help to pin this view at top when scroll -->
<TextView
android:layout_width="match_parent"
android:layout_height="50dp"
android:text="Title"
android:gravity="center"
app:layout_collapseMode="pin" />
</android.support.design.widget.AppBarLayout>
<!-- This will be your scrolling view.
app:layout_behavior="#string/appbar_scrolling_view_behavior" tag connects this features -->
<android.support.v7.widget.RecyclerView
android:id="#+id/list"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
android:layout_width="match_parent"
android:layout_height="match_parent">
</android.support.v7.widget.RecyclerView>
</android.support.design.widget.CoordinatorLayout>
Your activity should extend AppCompatActivity
public class YourActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.your_layout);
//set toolbar
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
}
}
Your app theme should be like this
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.NoActionBar">
</style>
</resources>
This is my implementation:
collapsedHeaderHeight and expandedHeaderHeight are defined somewhere else, with the function getAnimationProgress I can get the Expand/Collapse progress, base on this value I do my animation and show/hide the real header.
listForumPosts.setOnScrollListener(new AbsListView.OnScrollListener() {
/**
* #return [0,1], 0 means header expanded, 1 means header collapsed
*/
private float getAnimationProgress(AbsListView view, int firstVisibleItem) {
if (firstVisibleItem > 0)
return 1;
// should not exceed 1
return Math.min(
-view.getChildAt(0).getTop() / (float) (expandedHeaderHeight - collapsedHeaderHeight), 1);
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
// at render beginning, the view could be empty!
if (view.getChildCount() > 0) {
float animationProgress = getAnimationProgress(view, firstVisibleItem);
imgForumHeaderAvatar.setAlpha(1-animationProgress);
if (animationProgress == 1) {
layoutForumHeader.setVisibility(View.VISIBLE);
} else {
layoutForumHeader.setVisibility(View.GONE);
}
}
}
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
// do nothing
}
}

ActionBar - custom view with centered ImageView, Action Items on sides

I have a requirement to center a custom logo (using an ImageView) in the Actionbar for the "Home" activity. I'm using ABS for this project. This is very similar to a another question posted on S.O. (ActionBar logo centered and Action items on sides), but I'm not sure if the ImageView or search menu makes a difference, as I'm not getting the results I'm looking for (a centered image), or if I've just got it wrong. Basically, I set an Icon on the left, insert the custom view in the center, and have a search icon on the right (OptionsMenu). The image does appear a bit to the right of the icon, but it's still left of centered. Any pointers on how to center an ImageView in the actionbar would be greatly appreciated.
Home.java:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
LayoutInflater inflater = (LayoutInflater) getSupportActionBar().getThemedContext()
.getSystemService(LAYOUT_INFLATER_SERVICE);
final View customActionBarView = inflater.inflate(
R.layout.actionbar_custom_view_home, null);
/* Show the custom action bar view and hide the normal Home icon and title */
final ActionBar actionBar = getSupportActionBar();
actionBar.setHomeButtonEnabled(true);
actionBar.setDisplayHomeAsUpEnabled(false);
actionBar.setDisplayShowTitleEnabled(false);
actionBar.setIcon(R.drawable.ic_ab_som);
actionBar.setCustomView(customActionBarView);
actionBar.setDisplayShowCustomEnabled(true);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = new MenuInflater(this);
inflater.inflate(R.menu.search, menu);
return true;
}
res/layout/actionbar_custom_view_home.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_gravity="center">
<ImageView
android:id="#+id/actionBarLogo"
android:contentDescription="#string/application_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="false"
android:duplicateParentState="false"
android:focusable="false"
android:longClickable="false"
android:padding="#dimen/padding_small"
android:src="#drawable/logo_horizontal" />
</LinearLayout>
res/menu/search.xml:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="#id/search_item"
android:icon="?attr/action_search"
android:title="#string/search_label"
android:showAsAction="ifRoom|collapseActionView">
</item>
</menu>
If you want imageview in Center of ActionBar then use:
just replace getActionBar(); to getSupportActionBar(); in below code
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final ActionBar actionBar = getActionBar();
actionBar.setCustomView(R.layout.actionbar_custom_view_home);
actionBar.setDisplayShowTitleEnabled(false);
actionBar.setDisplayShowCustomEnabled(true);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
your actionbar_custom_view_home.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal" >
<ImageView
android:id="#+id/actionBarLogo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="false"
android:focusable="false"
android:longClickable="false"
android:src="#drawable/ic_launcher" />
</LinearLayout>
Hide Actionbar Icon
final ActionBar actionBar = getActionBar();
actionBar.setCustomView(R.layout.actionbar_custom_view_home);
actionBar.setDisplayShowTitleEnabled(false);
actionBar.setDisplayShowCustomEnabled(true);
actionBar.setDisplayUseLogoEnabled(false);
actionBar.setDisplayShowHomeEnabled(false);
Note: for < 11 API use getSupportActionBar() and > 11 API use getActionBar()
EDITED: 02/03/16 for Toolbar
<android.support.v7.widget.Toolbar
style="#style/ToolBarStyle"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
android:minHeight="#dimen/abc_action_bar_default_height_material">
<ImageView
android:layout_width="wrap_content"
android:contentDescription="#string/logo"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="#drawable/ic_launcher"/>
</android.support.v7.widget.Toolbar>
Explained:
The pink container, is the real space where you will add the view.
The trick is doing some maths, to center the View (whatever) to the middle.
In my case, the View was a TextView. Here's my full method:
public void addTextToActionBar( String textToSet )
{
mActionbar.setDisplayShowCustomEnabled( true );
// Inflate the custom view
LayoutInflater inflater = LayoutInflater.from( this );
View header = inflater.inflate( R.layout.actionbar_header, null );
//Here do whatever you need to do with the view (set text if it's a textview or whatever)
TextView tv = (TextView) header.findViewById( R.id.program_title );
tv.setText( textToSet );
// Magic happens to center it.
int actionBarWidth = DeviceHelper.getDeviceWidth( this ); //Google for this method. Kinda easy.
tv.measure( 0, 0 );
int tvSize = tv.getMeasuredWidth();
try
{
int leftSpace = 0;
View homeButton = findViewById( android.R.id.home );
final ViewGroup holder = (ViewGroup) homeButton.getParent();
View firstChild = holder.getChildAt( 0 );
View secondChild = holder.getChildAt( 1 );
leftSpace = firstChild.getWidth()+secondChild.getWidth();
}
catch ( Exception ignored )
{}
mActionbar.setCustomView( header );
if ( null != header )
{
ActionBar.LayoutParams params = (ActionBar.LayoutParams) header.getLayoutParams();
if ( null != params )
{
int leftMargin = ( actionBarWidth / 2 - ( leftSpace ) ) - ( tvSize / 2 ) ;
params.leftMargin = 0 >= leftMargin ? 0 : leftMargin;
}
}
}
Layout:
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal|center_vertical|center"
android:orientation="horizontal" >
<TextView
android:id="#+id/program_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#android:color/white"
android:contentDescription="#string/content_description_program_title"
android:ellipsize="end"
android:maxLines="1"
android:textSize="22sp"/>
</RelativeLayout>
Enjoy.
I encountered this problem,here is my solution:
ActionBar.LayoutParams layoutParams = new ActionBar.LayoutParams(
ActionBar.LayoutParams.MATCH_PARENT, ActionBar.LayoutParams.MATCH_PARENT);
layoutParams.gravity = Gravity.CENTER_HORIZONTAL|Gravity.CENTER_HORIZONTAL;
actionBar.setCustomView(yourCustomView,layoutParams);
The ImageView in your code is centered relative to the LinearLayout, not to the Action Bar. You can add left margin (android:layout_marginLeft) to the layout to adjust image position.
Other way to do it is not to add an icon and action items to the Action Bar, but to use a custom layout with icon and buttons inside. But you will need to handle action items yourself in that case.
Late to the party but in case it helps anyone else - use a layer-list and set it as the background. Otherwise, the logo will be centering based on remaining space, not the entire toolbar as Reinherd mentions.
You can use a layer-list with a static background color, and an image with gravity set to center as below. Hope it helps!
toolbar.axml
<android.support.v7.widget.Toolbar
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="#drawable/toolbar_background"
android:theme="#style/ThemeOverlay.AppCompat.Dark"
app:popupTheme="#style/ThemeOverlay.AppCompat.Light"
app:layout_scrollFlags="scroll|enterAlways"
app:layout_collapseMode="pin">
</android.support.v7.widget.Toolbar>
toolbar_background.xml
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="rectangle">
<solid android:color="#color/colorPrimary" />
</shape>
</item>
<item>
<bitmap android:src="#drawable/logo" android:gravity="center" />
</item>
</layer-list>
I'm faced with the same problem and I suggest the following solution:
in your res/layout/actionbar_custom_view_home.xml change width of layout to wrap_content:
android:layout_width="wrap_content"
Get width of action bar like this:
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
//width of action bar is the same as width of whole screen
final int actionBarWidth = size.x;
Add layoutListener to your customActionBarView:
customActionBarView.addOnLayoutChangeListener(
new OnLayoutChangeListener() {
#Override
public void onGlobalLayout() {
float x = customActionBarView.getX();
int logoImageWidth = imageLogo.getWidth();
int logoPosition = actionBarWidth / 2 - logoImageWidth / 2;
if (x != logoPosition) {
customActionBarView.setX(logoPosition);
customActionBarView.requestLayout();
} else {
customActionBarView.removeOnLayoutChangeListener(this);
}
}
}
);
The only thing I found working is putting (put right or left as needed, or both):
android:layout_marginLeft|Right="?attr/actionBarSize"
that I found here:
http://sourcey.com/android-custom-centered-actionbar-with-material-design/
For me "layoutParams.leftMargin" did the magic. I am able to push icon from left to right.
androidx.appcompat.app.ActionBar actionBar = getSupportActionBar();
actionBar.setDisplayOptions(actionBar.getDisplayOptions()
| ActionBar.DISPLAY_SHOW_CUSTOM);
actionBar.setDisplayOptions(actionBar.getDisplayOptions());
ImageView imageView = new ImageView(actionBar.getThemedContext());
imageView.setImageResource(R.drawable.content_copy);
ActionBar.LayoutParams layoutParams = new ActionBar.LayoutParams(
ActionBar.LayoutParams.WRAP_CONTENT,
ActionBar.LayoutParams.WRAP_CONTENT
);
layoutParams.leftMargin = 50;
imageView.setLayoutParams(layoutParams);
actionBar.setCustomView(imageView);

AlertDialog setCustomTitle styling to match standard AlertDialog title

I'm working on an Android app and I have an AlertDialog subclass. I would like to put 2 ImageButtons on the right side of the title area of the dialog (similar to an the ActionBar in an Activity). I'm using setCustomTitle() to do this, which replaces the title area with a custom view of my own creation. This works fine, but the styling of my custom title area is not the same as the standard title styling (height, color, separator, etc).
My question is: with the understanding that styling varies by OS version and manufacturer, how can I style my custom title in the dialog so that it will match the standard title styling for other AlertDialogs?
Here is an image of anAlertDialog with standard styling (this is from ICS, but I want to be able to match any variant -- not this particular style)
And here is an image of an AlertDialog with custom title and buttons (note how the title height and color don't match the standard dialog)
EDIT: I can't just add the ImageButtons to the standard title view, because I don't have access to it. If you know of a (reliable, non-hack) method for me to add buttons to the standard title area, I would accept that as well.
Given that there is new interest in this question, let me elaborate about how I "solved" this.
First, I use ActionBarSherlock in my app. This is not necessary, I suppose, though it helps a lot because the styles and themes defined in the ABS project allow me to mimic the Holo theme on pre-ICS devices, which provides a consistent experience in the app.
Second, my "dialog" is no longer a dialog -- it's an activity themed as a dialog. This makes manipulation of the view hierarchy simpler, because I have complete control. So adding buttons to the title area is now trivial.
Here are the screenshots (2.2 device and 4.1 emulator). Note that the only significant styling difference is the EditText, which I have chosen not to address.
Here is my onCreate in my dialog activity:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_tag);
setTitle(R.string.tag_dialog_title);
View sherlockTitle = findViewById(android.R.id.title);
if (sherlockTitle != null) {
sherlockTitle.setVisibility(View.GONE);
}
View sherlockDivider = findViewById(R.id.abs__titleDivider);
if (sherlockDivider != null) {
sherlockDivider.setVisibility(View.GONE);
}
// setup custom title area
final View titleArea = findViewById(R.id.dialog_custom_title_area);
if (titleArea != null) {
titleArea.setVisibility(View.VISIBLE);
TextView titleView = (TextView) titleArea.findViewById(R.id.custom_title);
if (titleView != null) {
titleView.setText(R.string.tag_dialog_title);
}
ImageButton cancelBtn = (ImageButton) titleArea.findViewById(R.id.cancel_btn);
cancelBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
finish();
}
});
cancelBtn.setVisibility(View.VISIBLE);
ImageButton okBtn = (ImageButton) titleArea.findViewById(R.id.ok_btn);
okBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// do stuff here
finish();
}
});
okBtn.setVisibility(View.VISIBLE);
}
}
And here is the relevant layout for the activity:
<LinearLayout
android:orientation="vertical"
android:layout_height="fill_parent"
android:layout_width="fill_parent">
<LinearLayout
android:id="#+id/dialog_custom_title_area"
android:orientation="vertical"
android:fitsSystemWindows="true"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingRight="10dp">
<TextView
android:id="#+id/custom_title" style="?android:attr/windowTitleStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:minHeight="#dimen/abs__alert_dialog_title_height"
android:paddingLeft="16dip"
android:paddingRight="16dip"
android:textColor="#ffffff"
android:gravity="center_vertical|left" />
<ImageButton
android:id="#+id/ok_btn"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:minWidth="#dimen/abs__action_button_min_width"
android:minHeight="#dimen/abs__alert_dialog_title_height"
android:scaleType="center"
android:src="#drawable/ic_action_accept"
android:background="#drawable/abs__item_background_holo_dark"
android:visibility="visible"
android:layout_gravity="center_vertical"
android:contentDescription="#string/acc_done"/>
<ImageButton
android:id="#+id/cancel_btn"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:minWidth="#dimen/abs__action_button_min_width"
android:minHeight="#dimen/abs__alert_dialog_title_height"
android:scaleType="center"
android:src="#drawable/ic_action_cancel"
android:background="#drawable/abs__item_background_holo_dark"
android:visibility="visible"
android:layout_gravity="center_vertical"
android:contentDescription="#string/acc_cancel"
/>
</LinearLayout>
<View
android:id="#+id/dialog_title_divider"
android:layout_width="fill_parent"
android:layout_height="2dip"
android:background="#color/abs__holo_blue_light" />
</LinearLayout>
<RelativeLayout
android:id="#+id/list_suggestions_layout"
android:layout_height="wrap_content"
android:layout_width="fill_parent">
<!-- this is where the main dialog area is laid out -->
</RelativeLayout>
</LinearLayout>
And finally, in my AndroidManifext.xml, here is how I define my TagActivity:
<activity
android:icon="#drawable/ic_home"
android:name=".activity.TagActivity"
android:theme="#style/Theme.Sherlock.Dialog"/>
OK, maybe it is not the super perfect solution and maybe it is a bad solution, but I tried this on android 2.3.7 and android 4.1.2:
2.3.7 (real device)
4.1.2 (emulator)
We start by creating a dialog Title style to make sure we have some space for our icons:
res/values/dialogstyles.xml
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<style name="Dialog" parent="#android:style/Theme.Dialog">
<item name="android:windowTitleStyle">#style/MyOwnDialogTitle</item>
</style>
<style name="MyOwnDialogTitle">
<!-- we need to make sure our images fit -->
<item name="android:layout_marginRight">100dp</item>
</style>
</resources>
res/values-v11/dialogstyles.xml
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<style name="Dialog" parent="#android:style/Theme.Holo.Dialog">
<item name="android:windowTitleStyle">#style/MyOwnDialogTitle</item>
</style>
</resources>
Then we create our DialogFragment with two tricks:
set the style in the onCreate:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setStyle(DialogFragment.STYLE_NORMAL, R.style.Dialog);
}
override onCreateView and add our layout (of buttons) to the Dialog (see comments)
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
//we need the view to remove the tree observer (that's why it is final)
final View view = inflater.inflate(R.layout.dialog_custom, container);
getDialog().setTitle("Shush Dialog");
//register a layout listener to add our buttons
view.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#SuppressWarnings("deprecation")
#SuppressLint("NewApi")
#Override
public void onGlobalLayout() {
//inflate our buttons
View menu = LayoutInflater.from(getActivity()).inflate(R.layout.layout_mymenu, null);
//get the root view of the Dialog (I am pretty sure this is the weakest link)
FrameLayout fl = ((FrameLayout) getDialog().getWindow().getDecorView());
//get the height of the root view (to estimate the height of the title)
int height = fl.getHeight() - fl.getPaddingTop() - fl.getPaddingBottom();
//to estimate the height of the title, we subtract our view's height
//we are sure we have the heights btw because layout is done
height = height - view.getHeight();
//prepare the layout params for our view (this includes setting its width)
//setting the height is not necessary if we ensure it is small
//we could even add some padding but anyway!
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, height);
params.gravity = Gravity.RIGHT | Gravity.TOP;
//add the view and we are done
fl.addView(menu, params);
if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN)
view.getViewTreeObserver().removeOnGlobalLayoutListener(this);
else
view.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
});
return view;
}
Alright if it just images, then you just have ensure that everything that you create in xml is scaled by density pixels or DP for short. Most simple coding that sets paint are usually set by pixels as well and may need a manual coding version to density pixels.

How to make an Android progressbar with rounded loaderbar?

How do you make a progressbar with rounded corner at the right side (the end), not only in the left side (the start). What I currently have is nearly the layout what I want but the progressbar loader is just a straight vertical line, I'd like to get this line rounded.
Basically you should make a custom Widget, so you can cutomize it to your taste.
Here is a tutorial on exactly what you're looking for. link!
So what I ended up doing this in xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="#color/white"
android:id="#+id/splash_linear">
<FrameLayout
android:layout_width="144dp"
android:layout_height="13dp"
android:layout_gravity="center"
android:layout_marginTop="20dp">
<View android:id="#+id/progress_horizontal"
android:background="#drawable/progress_background"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<View android:id="#+id/progress_horizontal_bar"
android:background="#drawable/progress_bar"
android:layout_width="0dp"
android:layout_height="match_parent"/>
</FrameLayout>
</LinearLayout>
Then in code:
public void updateProgress(int percent) {
int progressBarSizeDp = 144; // the size of the progressbar
float scale = (float) (progressBarSizeDp/100.0);
int progressSize = (int) (percent * scale);
if(progressSize > progressBarSizeDp) {
progressSize = progressBarSizeDp;
} else if(progressSize < 20) {
progressSize = 20;
}
View progressBar = (View) findViewById(R.id.progress_horizontal_bar);
int py = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, progressSize, getResources().getDisplayMetrics());
LayoutParams params = new FrameLayout.LayoutParams(py, LayoutParams.MATCH_PARENT);
progressBar.setLayoutParams(params);
View splashMain = (View) findViewById(R.id.splash_linear);
splashMain.invalidate();
}
Found a nice link:
Custom progress bar with rounded corners
Basically it uses a custom RelativeLayout and a 9-patch approach to draw the rounded progress bar.

Categories

Resources