I have been looking for answers on how to place the indeterminate horizontal progress bar below the action bar using AppCompat. I'm able to get the horizontal progress bar to appear, but it is at the top of the action bar. I want it under/below the action bar kind of like how gmail does it (except without the pull to refresh).
I used the following code to have the progress bar appear:
supportRequestWindowFeature(Window.FEATURE_PROGRESS);
setContentView(R.layout.main_activity);
setSupportProgressBarIndeterminate(Boolean.TRUE);
setSupportProgressBarVisibility(true);
but this places the horizontal progress bar at the top of the action bar. Anyone know how to place the progress bar below the action bar?
I faced a similar problem recently and solved it by creating my own progressbar and then aligning it by manipulating getTop() of the content view.
So first create your progressbar.
final LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, 20); //Use dp resources
mLoadingProgressBar = new ProgressBar(this, null, android.R.attr.progressBarStyleHorizontal);
mLoadingProgressBar.setIndeterminate(true);
mLoadingProgressBar.setLayoutParams(lp);
Add it to the window (decor view)
final ViewGroup decor = (ViewGroup) getWindow().getDecorView();
decor.addView(mLoadingProgressBar);
And in order to get it to its correct position Im using a ViewTreeObserver that listens until the view has been laid out (aka the View.getTop() isnt 0).
final ViewTreeObserver vto = decor.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
final View content = getView(android.R.id.content);
#Override
public void onGlobalLayout() {
int top = content.getTop();
//Dont do anything until getTop has a value above 0.
if (top == 0)
return;
//I use ActionBar Overlay in some Activities,
//in those cases it's size has to be accounted for
//Otherwise the progressbar will show up at the top of it
//rather than under.
if (getSherlock().hasFeature((int) Window.FEATURE_ACTION_BAR_OVERLAY)) {
top += getSupportActionBar().getHeight();
}
//Remove the listener, we dont need it anymore.
Utils.removeOnGlobalLayoutListener(decor, this);
//View.setY() if you're using API 11+,
//I use NineOldAndroids to support older
ViewHelper.setY(mLoadingProgressBar, top);
}
});
Hope that makes sense for you. Good luck!
Related
I have a kotlin app with bottom navigation setup.
I currently have 5 fragments [ProfileFragment, SearchFragment, HomeFragment, SettingsFragment, WebViewFragment]
All of these are just blank fragments. But in my Profile Fragment, I'm showing off a panaroma widget in the top half of the page
I know about making my whole app full screen, but then, on other fragments, content will get hidden under notched displays. And by content, I mean my employer's logo, which he wants, without fail.
So, I tried another way. I made the app full screen and added padding wherever, there was content hiding under the notch. Now, there happen to be various phones, without notches. The content looked unusually padded down, because, well, there was no notch.
If I make adjustments for notched display, the non-notch displays will give issues. And vice-versa.
So, I figured, why not instead of making all activities in my app fullscreen, If I can stretch the ProfileFragment to cover the status bar, or hide the status bar, it'd be a perfect solution.
Is there a way to do either of the following?
Hide the status bar on the ProfileFragment
Stretch the fragment to the top of the screen
Make the fragment full screen, without cutting off the bottom navigation
You can try adding this code in your Activity:
// Hide the status bar.
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_FULLSCREEN
// Remember that you should never show the action bar if the status bar is hidden, so hide that too if necessary.
actionBar?.hide()
More info here: https://developer.android.com/training/system-ui/status#kotlin
AndroidX (support library) has a built-in OnApplyWindowInsetsListener which helps you determine the window insets such as top (status bar) or bottom insets (ie. keyboard) in a device-compatible way.
Since the insets work for API 21+ you have to get the insets manually for below that. Here is an example in Java (v8), hope you get the hang of it:
public class MainActivity extends AppCompatActivity {
...
#Override
protected void onCreate(Bundle savedInstanceState) {
...
View mainContainer = findViewById(R.id.main_container); // You layout hierarchy root
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
ViewCompat.setOnApplyWindowInsetsListener(mainContainer , (v, insets) -> {
int statusBarHeight = 0;
if (!isInFullscreenMode(getWindow())) statusBarHeight = insets.getSystemWindowInsetTop();
// Get keyboard height
int bottomInset = insets.getSystemWindowInsetBottom();
// Add status bar and bottom padding to root view
v.setPadding(0, statusBarHeight, 0, bottomInset);
return insets;
});
} else {
int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
int statusBarHeight = 0;
if (resourceId > 0 && !isInFullscreenMode(getWindow())) {
statusBarHeight = getResources().getDimensionPixelSize(resourceId);
}
View decorView = getWindow().getDecorView();
decorView.getViewTreeObserver().addOnGlobalLayoutListener(() -> {
Rect r = new Rect();
//r will be populated with the coordinates of your view that area still visible.
decorView.getWindowVisibleDisplayFrame(r);
//get screen height and calculate the difference with the useable area from the r
int height = decorView.getContext().getResources().getDisplayMetrics().heightPixels;
int bottomInset = height - r.bottom;
// if it could be a keyboard add the padding to the view
// if the use-able screen height differs from the total screen height we assume that it shows a keyboard now
//set the padding of the contentView for the keyboard
mainContainer.setPadding(0, statusBarHeight, 0, bottomInset);
});
}
...
}
public static boolean isInFullscreenMode(Window activityWindow) {
return (activityWindow.getAttributes().flags & WindowManager.LayoutParams.FLAG_FULLSCREEN) == WindowManager.LayoutParams.FLAG_FULLSCREEN;
}
}
Note that for the bottom inset to work you have to tell Android that your activity is resizable, so in your AndroidManifest.xml:
<application
...>
<activity
android:name=".MainActivity"
...
android:windowSoftInputMode="adjustResize"/>
...
</application>
If you use AppCompatActivity, you can also use:
if(getSupportActionBar() != null) {
getSupportActionBar().hide();
}
in the onCreate methode.
I've search anywhere but I haven't found it. Anything I've found this far is always hiding the Action Bar when a ListView, GridView or RecyclerView is scrolled. What I need is how to hide the Action Bar when a ViewGroup (using ScrollView for example) is scrolled? Because I can't really convert my layout to any of those three, and there is no onScrollListener for ScrollView.
Thank you.
1.implement ViewTreeObserver.OnScrollChangedListener into your class,
2.Write this line into your onCreate "getWindow().requestFeature(Window.FEATURE_ACTION_BAR_OVERLAY);" (without quotes)
3.Now get ActionBar Height for Hide animation as follows -
final TypedArray mstyled = getTheme().obtainStyledAttributes(new int[]{android.R.attr.actionBarSize });
mActionBarHeight = styledAttributes.getDimension(0, 0);
mstyled.recycle();
4.Get Action Bar.
mActionBar = getActionBar();
5.Intilize scrollview.
((ScrollView)findViewById(R.id.parent)).getViewTreeObserver().addOnScrollChangedListener(this);
6.override onScrollChange and mode your method like this.
#Override
public void onScrollChanged() {
float mfloat = ((ScrollView)findViewById(R.id.parent)).getScrollY();
if (mfloat >= mActionBarHeight && mActionBar.isShowing()) {
mActionBar.hide();
} else if ( mfloat==0 && !mActionBar.isShowing()) {
mActionBar.show();
}
}
Hope it Helps.
Using
android.support.v4.widget.NestedScrollView
instead
Scrollview.
It worked good
I am currently developing an application in which I use a heavily modified Split Action Bar. Here is a link to the app's current state:
You'll notice a transparent action bar up top, with a custom view inflated into it, with a hacked together split action bar on bottom. The bottom view is actually a single action item with a custom view inflated into it and showAlways=true.
Currently I only support SDK v15+ and I don't really plan on changing that, but with the Lollipop AppCompat library that just released, I decided to implement it, so I could get some of that awesomeness in my app.
I've changed my theme to Theme.AppCompat.Light, and my MainActivity now extends ActionBarActivity instead of Activity.
All references to getActionBar have now been switched to getSupportActionBar, and with only those changes, this is what my activity now looks like:
You'll notice I got a UI dump from the Device Monitor, and it's shoving the bottom action bar into a weird space and calling that the action bar, and getting rid of my top custom view.
Here is my code for setting up my action bar:
public void initializeActionBar(){
View customNav = LayoutInflater.from(this).inflate(R.layout.action_bar_top, null);
actionBar = getSupportActionBar();
actionBar.setBackgroundDrawable(getResources().getDrawable(R.drawable.transparent_fifty_percent));
final PopupWindow window = addPopupWindow();
actionBarOptions = (ImageView)customNav.findViewById(R.id.options);
actionBarOptions.setVisibility(View.GONE);
actionBarOptions.setImageDrawable(app.svgToBitmapDrawable(getResources(), R.raw.vertical_ellipsis, app.scaleByDensity(48)));
actionBarOptions.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
window.showAsDropDown(actionBarOptions, 0, 0);
}
});
TextView title = (TextView) customNav.findViewById(R.id.screen_title);
Typeface font1 = Typeface.createFromAsset(getAssets(), "Merriweather-Italic.ttf");
title.setText("Parsley");
title.setTypeface(font1);
actionBar.setCustomView(customNav);
actionBar.setDisplayShowCustomEnabled(true);
actionBar.setDisplayShowTitleEnabled(false);
actionBar.setDisplayShowHomeEnabled(false);
actionBar.setDisplayUseLogoEnabled(false);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
new MenuInflater(this).inflate(R.menu.test, menu);
LinearLayout fullMenu = (LinearLayout) menu.findItem(R.id.full_menu).getActionView();
ViewGroup.LayoutParams params;
icon1 = (ImageView) fullMenu.findViewById(R.id.action_item1);
params = icon1.getLayoutParams();
params.width = getResources().getDisplayMetrics().widthPixels / 4;
params.height = (int) (48 * getResources().getDisplayMetrics().density);
icon1.setImageDrawable(app.svgToBitmapDrawable(getResources(), R.raw.shopping_list_icon, app.scaleByDensity(32)));
icon2 = (ImageView) fullMenu.findViewById(R.id.action_item2);
icon3 = (ImageView) fullMenu.findViewById(R.id.action_item3);
icon4 = (ImageView) fullMenu.findViewById(R.id.action_item4);
icon2.setImageDrawable(app.svgToBitmapDrawable(getResources(), R.raw.recipe_box_icon, app.scaleByDensity(32)));
icon3.setImageDrawable(app.svgToBitmapDrawable(getResources(), R.raw.icon_search, app.scaleByDensity(32)));
icon4.setImageDrawable(app.svgToBitmapDrawable(getResources(), R.raw.icon_add, app.scaleByDensity(32)));
params = icon2.getLayoutParams();
params.width = getResources().getDisplayMetrics().widthPixels / 4;
params.height = (int) (48 * getResources().getDisplayMetrics().density);
params = icon3.getLayoutParams();
params.width = getResources().getDisplayMetrics().widthPixels / 4;
params.height = (int) (48 * getResources().getDisplayMetrics().density);
params = icon4.getLayoutParams();
params.width = getResources().getDisplayMetrics().widthPixels / 4;
params.height = (int) (48 * getResources().getDisplayMetrics().density);
if (!firstLoad) {
setBottomActionBarActive();
setActiveTab(0);
}
optionsLoaded = true;
return true;
}
initializeActionBar() is called from onCreate in my activity. Any ideas what I'm doing wrong?
Toolbar should be used. In your case it's one toolbar at the top, and one at the bottom. Check android team blog, they have nice integration guide.
If you just want your bottom action bar back, you can simply change back to appcompat v7:20 ,and it works for me. The problem is split action bar is no longer being supported in appcomat v7:21.
While user482277's solution may work for instances with a more traditional split action bar, utilizing action items, navigation drawer, etc, it didn't quite work for me. What I ended up doing was building a pair of custom (compound really) views to emulate both the top and bottom action bar. I found this situation to work much better, especially with backwards compatibility. I don't have to worry about earlier versions supporting action bar, because at the end of the day, it's just a pair of classes that extend LinearLayout. In addition, I don't have to worry about different screen sizes (particularly tablets) not supporting the split version.
I'm currently making one of my very first applications. I'm using ActionBarSherlock.
I would like to make my logo overlap the actionbar (scrollview).
Currently I have main_activity.xml. In MainActivity.java I use setContentView to view main_activity.xml. After that I use getSupportActionBar() for ActionBarSherlock. I've tried things out using RelativeLayout (http://www.mkyong.com/android/android-relativelayout-example/). That didn't really work because there are multiple layouts.
So I've tried some things right and left, but it always ends up infront or behind the actionbar, or stops just before reaching the content. It's because of two different layouts, that's what I know. But how can I going to solve this? Is it possible? Thanks in advance!
What I want:
http://f.cl.ly/items/3N0w243N1t2Q3i1H1f1k/Untitled-1.png
You can either:
A. Split your image in two
Have the top part as the ActionBar logo, then show the bottom part over your content.
B. Use a single image
You'll need a layout file that contains just your logo (you'll probably want something like an ImageView inside a LinearLayout so you can easily set the correct margins).
Then after calling setContentView for your activity, add your logo view with:
ViewGroup decorViewGroup = (ViewGroup) getWindow().getDecorView();
decorViewGroup.addView(logoView);
Using a layout file
Example layout file (logo_view.xml):
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/logo_image"
android:scaleType="center"
android:layout_marginLeft="10dip"
/>
</LinearLayout>
Inflate the layout file:
LayoutInflater inflater = LayoutInflater.from(this);
View logoView = inflater.inflate(R.layout.logo_view, null, false);
Although the original answer works on some devices, on others the image sits under the status bar. I resolved this by getting the location of the top ActionBar and comparing it to the location of the top of the logo image and then just adding some top padding, as follows:
// Inflate logo layout
LayoutInflater inflater = LayoutInflater.from(this);
final View logoView = inflater.inflate(R.layout.menu_logo, null);
// Add logo to view
ViewGroup viewGroup = (ViewGroup) getWindow().getDecorView();
viewGroup.addView(logoView);
// Adjust the logo position
int resId = getResources().getIdentifier("action_bar_container", "id", "android");
final View actionBarView = viewGroup.findViewById(resId);
if (actionBarView != null) {
actionBarView.getViewTreeObserver().addOnGlobalLayoutListener(
new ViewTreeObserver.OnGlobalLayoutListener() {
public void onGlobalLayout() {
// Remove the listener
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
actionBarView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
} else {
actionBarView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
// Measure views
int[] location = new int[2];
actionBarView.getLocationOnScreen(location);
int[] logoLocation = new int[2];
logoView.getLocationOnScreen(logoLocation);
// Add top padding if necessary
if (location[1] > logoLocation[1]) {
logoView.setPadding(0, location[1] - logoLocation[1], 0, 0);
}
}
}
);
}
This worked on a wide range of devices (phones, big/small tablets - inc Kindle Fire HDX) running Android versions 4.0 up to 4.4.4 as well as Android L preview.
This question already has answers here:
Change position of Google Maps API's "My location" button
(17 answers)
Closed 4 years ago.
I have added a map fragment (API v2) to my app with the map covering the whole screen and a semi-transparent actionbar on top.
The activity uses a theme with android:windowActionBarOverlay set to true.
I have also enabled the "MyLocationButton" on the map, but since the map covers the full height of the screen, the button is covered by the action bar.
How can I make the map fragment draw the location button below the action bar or at the bottom of the screen instead?
Instead of creating your own button, just move the build in button according to the action bar size.
This code works for me and the button is just where the button should be (like in google maps):
// Gets the my location button
View myLocationButton = getSherlockActivity().findViewById(R.id.MainContainer).findViewById(2);
// Checks if we found the my location button
if (myLocationButton != null){
int actionBarHeight = 0;
TypedValue tv = new TypedValue();
// Checks if the os version has actionbar in it or not
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB){
if (getSherlockActivity().getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true))
actionBarHeight = TypedValue.complexToDimensionPixelSize(tv.data,getResources().getDisplayMetrics());
}
// Before the action bar was added to the api
else if(getSherlockActivity().getTheme().resolveAttribute(com.actionbarsherlock.R.attr.actionBarSize, tv, true)){
actionBarHeight = TypedValue.complexToDimensionPixelSize(tv.data,getResources().getDisplayMetrics());
}
// Sets the margin of the button
ViewGroup.MarginLayoutParams marginParams = new ViewGroup.MarginLayoutParams(myLocationButton.getLayoutParams());
marginParams.setMargins(0, actionBarHeight + 20, 20, 0);
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(marginParams);
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT, RelativeLayout.TRUE);
myLocationButton.setLayoutParams(layoutParams);
}
Just put this code in the onActivityCreated (if you will put it in the onCreateOptionsMenu, it will not support version before 3.0 - because the life cycle there is different.
Another thing, the "R.id.MainContainer" is the container of the map fragment.
I'm using ActionBar Sherlock, but it will work also for regular action bar with a few modifications..
Below (especially in fixMapControlLocations) i've addressed this with ActionBarSherlock.
Issues I had were on narrow screens, and the split action bar having the wrong offset depending on rotation. The isNarrow check through sherlock lets me know if its narrow.
Another key change is i'm setting the padding of the myLocation's parent's parent view. This picks up all controls inside, and based on hierarchyviewer is how google maps is doing it. The Google attribution logo is on the next parent up the tree in a Surface object. Not looking like that is easily movable, so i'm probably just going to end up loosing the bottom action bar's transparency effect to stay in compliance.
protected void onCreate(Bundle savedInstanceState) {
getWindow().requestFeature(Window.FEATURE_ACTION_BAR_OVERLAY);
super.onCreate(savedInstanceState);
setContentView(R.layout.map);
setUpMapIfNeeded();
getSupportActionBar().setBackgroundDrawable(d);
getSupportActionBar().setSplitBackgroundDrawable(d);
}
private void setUpMapIfNeeded() {
// Do a null check to confirm that we have not already instantiated the
// map.
if (map == null) {
// Try to obtain the map from the SupportMapFragment.
map = ((SupportMapFragment) getSupportFragmentManager()
.findFragmentById(R.id.map)).getExtendedMap();
// Check if we were successful in obtaining the map.
if (map != null) {
setUpMap();
}
}
}
private void setUpMap() {
fixMapControlLocations();
.....
}
private void fixMapControlLocations() {
SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
.findFragmentById(R.id.map);
int actionBarHeight = 0;
TypedValue tv = new TypedValue();
if (getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true))
{
actionBarHeight = TypedValue.complexToDimensionPixelSize(tv.data,getResources().getDisplayMetrics());
}
View myLocationParent = ((View)mapFragment.getView().findViewById(1).getParent());
View myLocationParentParent = ((View)myLocationParent.getParent());
myLocationParentParent.setPadding(0, actionBarHeight, 0, isNarrow()?actionBarHeight:0);
}
public boolean isNarrow() {
return ResourcesCompat.getResources_getBoolean(getApplicationContext(),
R.bool.abs__split_action_bar_is_narrow);
}
You can accomplish this with the recently-added GoogleMap.setPadding() method:
map.setPadding(leftPadding, topPadding, rightPadding, bottomPadding);
From the API docs:
This method allows you to define a visible region on the map, to signal to the map that portions of the map around the edges may be obscured, by setting padding on each of the four edges of the map. Map functions will be adapted to the padding. For example, the zoom controls, compass, copyright notices and Google logo will be moved to fit inside the defined region, camera movements will be relative to the center of the visible region, etc.
Also see the description of how padding works in GoogleMap.
This has already been filed as an enhancement (please star it if you haven't already) http://code.google.com/p/gmaps-api-issues/issues/detail?id=4670
As a temporary workaround I have added my own find location button below the actionbar (my map fragment is in a RelativeLayout so I just did alignParentRight and set appropriate margin top).
Then in my onClickHandler I did this:
public void onClickHandler(View target) {
switch (target.getId()) {
case R.id.my_fml_btn:
mMap.setMyLocationEnabled(true);
View fmlBtn = mMapWrapper.findViewById(2); //mMapWrapper is my RelativeLayout
if (fmlBtn != null) fmlBtn.setVisibility(View.INVISIBLE);
break;
}
}
I used hierarchyviewer to find the id of the button that was added by the maps api. It just happens to be the 2nd view added to the map (and set to invisible).
You can of course you can fiddle about with LayoutParams to offset this button rather than hide it but this button only appears after you setMyLocationEnabled to true! (in my use case I prefer to let the user decide before firing up the gps)
Make sure you use ?android:attr/actionBarSize (or ?attr/actionBarSize if you're using ActionBarSherlock) to correctly offset the content of the fragment.
Depending of the effect you're trying to accomplish, either apply this value as margin or padding. I'm guessing that because of the semi-transparant ActionBar, you'll want to try padding, in order to still have the map appear behind it (and keep the see-through effect). I'm just not 100% sure whether padding will actually move the 'Locate me' button down... If not, then probably applying a margin is your only other option.
See here for an example and more details on this attribute.