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.
Related
I have two views A and B. View B is completely covered view A which means the bounding rect of view B is more than bounding rect of view A.
The best example I can give is, I have a fixed bottom tabs layout and a RecyclerView with multiple linear layouts. When I scroll, one of the linear layout will be behind the bottom tabs layout.
So, How can I programatically check If view A is completely covered by other views (other views might not be known upfront)?
Is there any possible way to identify this?
Update:
I have tried the solution mentioned here. But it didn't solve my problem. In my case, the view bounds are exactly matching and I want which view is on top.
After lot of struggle, I have found a way to identify if view is overlapped by other views in the following way.
AccessibilityNodeInfo nodeInfo = AccessibilityNodeInfo.obtain();
viewA.onInitializeAccessibilityNodeInfo(nodeInfo);
if (!nodeInfo.isVisibleToUser()) {
// View is not visible to user. This also validates if viewA is overlapped by other views
}
nodeInfo.recycle();
Use the following method it will help you to find view overlapping:-
private boolean isYourViewOverlapping(View firstView, View secondView) {
int[] firstPosition = new int[2];
int[] secondPosition = new int[2];
firstView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
firstView.getLocationOnScreen(firstPosition);
secondView.getLocationOnScreen(secondPosition);
int r = firstView.getMeasuredWidth() + firstPosition[0];
int l = secondPosition[0];
return r >= l && (r != 0 && l != 0);
}
Ok... here's my situation.
I have a carousel of images in a HorizontalScrollView - which contains a LinearLayout - in my Activity, like so:
<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/slider"
android:scrollbars="none" >
<LinearLayout
android:id="#+id/carousel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
/>
</HorizontalScrollView>
I have a TypedArray, loop through it, and on each run, set these images programatically, add a ClickListener and a Tag, and add this ImageView to the LinearLayout (set in my Activity Layout), like so:
// Get the array
final TypedArray carouselArray = getResources().obtainTypedArray(R.array.carousel_array);
// Populate the Carousel with item
for (int i = 0 ; i < carouselArray.length() ; ++i) {
// Image Item
ImageView outerImage;
// Set the image view resource
if(i == 0) {
outerImage.setImageResource(R.drawable.toy_filter_clear);
}
else {
outerImage.setImageResource(carouselArray.getResourceId(i, -1));
}
// Set Touch Listener
outerImage.setOnTouchListener(this);
final String prepend = "CAROUSEL_";
final String index = String.valueOf(i);
final String tag = prepend.concat(index);
outerImage.setTag(tag);
/// Add image view to the Carousel container
mCarouselContainer.addView(outerImage);
}
But now, I just found out that I have to programatically add a second image to sit inside/on top of the first image at particular coordinates (damn you UI ppl!). I need these to be considered the same image/view essentially, so need to pack them together inside of a layout, I am assuming. So I have made a layout file, like so:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/carousel_item"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ImageView
android:id="#+id/carousel_outer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:src="#drawable/toy_filter_normal"
/>
<ImageView
android:id="#+id/carousel_inner"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="#+id/carousel_outer"
android:layout_alignRight="#+id/carousel_outer"
android:layout_marginBottom="10dp"
android:layout_marginRight="5dp"
android:src="#drawable/thumb_nofilter"
/>
</RelativeLayout>
This has the proper positioning, and the default images set on it. So what I want to be able to do is to reach into the Layout file, grab the ImageViews by their ID, overwrite the image if necessary, and then add that RelativeLayout to my LinearLayout at the end of my loop... sounds easy enough, right ?
My first attempt was to do it like this :
RelativeLayout item = (RelativeLayout) findViewById(R.id.carousel_item);
ImageView outerImage = (ImageView) item.findViewById(R.id.carousel_outer);
ImageView innerImage = (ImageView) item.findViewById(R.id.carousel_inner);
... but that gives me a NullPointer on the ImageView...So then I tried to inflate the RelativeLayout first, like this:
LayoutInflater inflater = (LayoutInflater) this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(R.layout.carousel_item_layout, null);
ImageView outerImage = (ImageView) view.findViewById(R.id.carousel_outer);
ImageView innerImage = (ImageView) view.findViewById(R.id.carousel_inner);
This gets rid of the NPE's, and (apparently) let's the images be set properly like so:
if(i == 0) {
outerImage.setImageResource(R.drawable.toy_filter_clear);
innerImage.setImageResource(0);
}
else {
outerImage.setImageResource(R.drawable.toy_filter_normal);
innerImage.setImageResource(carouselArray.getResourceId(i, -1));
}
but when I try to add the outerImage ImageView back to the LinearLayout, I get an NPE there:
mCarouselContainer.addView(outerImage);
More to the point, I don't want to add ONLY the one ImageView to the LinearLayout/HorizontalScrollView - I want to somehow pack the resulting images back into the RelativeLayout and add the whole thing back into my LinearLayout... but, it is worth mentioning, that this also gives me an NPE.
What is a guy to do ? Any thoughts appreciated...
Ok... Wow, thanks SO Code Monkey!
I managed to fix this with a one line fix, by adding the inflated View to the LinearLayout instead of the ImageView or the RelativeLayout (which wasn't doing anything), like so:
mCarouselContainer.addView(view);
Don't know why I hadn't tried that before, but I was unclear on whether as it's children were being updated if it would reflect the parent, so to speak... now I know it was.
I'm gonna keep the question up, as I think it's helpful... ?
I want to add some spacing between the actionbar's back navigation button, icon and title. Anyone knows how to do it?
int actionbarTitleId = getResources().getIdentifier("android:id/action_bar_title", null, null); //TextView hosted in LinearLayout
int upImageViewId = getResources().getIdentifier("android:id/up", null, null);//ImageView hosted in LinearLayout or HomeView
int homeId = android.R.id.home; //ImageView hosted in HomeView
Use findViewById with each of the ids to get View (or appropriate cast) for each.
Try not to modify the LayoutParams. But add padding between them or just use existing LayoutParams to modify the margins.
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!
I have a number of elements in a ListView that scroll off the screen.
I would like there to be blank space at the end of the View. That is, the user should be able to scroll past the last element such that the last element is in the middle of the viewport.
I could use an OverScroller, but I think that would only enable the View to have a bouncy effect like one often sees on the iPhone.
Is there something I might have overlooked?
The scrolled-to-the-botton screen should look something like this:
The accepted answer is too complicated, and addFooterView is not for this kind of thing. The proper and simpler way is to set the paddingTop and paddingBottom, and you need to set clipToPadding to "false". In your list view or grid view, add the following:
android:paddingTop="100dp"
android:paddingBottom="100dp"
android:clipToPadding="false"
You'll get blank space at the top and the bottom that moves with your finger scroll.
Inflate any layout of your choice (this could be an XML of and ImageView with no drawable and with set height and width of your choice)
Measure the screen height and create new LayoutParams and set the height of it to 1/2 of the screen height
Set the new layout params on your inflated view
Use the ListView's addFooterView() method to add that view to the bottom of your list (there is also an addHeaderView())
Code to measure screen height
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
int screenHeight = display.getHeight();
Code to set half screen height:
View layout = inflater.inflate(R.layout.mylistviewfooter, container, false);
ViewGroup.LayoutParams lp = layout.getLayoutParams();
lp.height = screenHeight/2;
layout.setLayoutParams(lp);
myListView.addFooterView(layout);
An Aside:
When you add a footer or header view to any listview, it has to be done before adding the adapter. Also, if you need to get your adapter class after doing this you will need to know calling the listview's adapter by getAdapter() will return an instance of HeaderViewListAdapter in which you will need to call its getWrappedAdapter method
Something like this :
MyAdapterClassInstance myAdapter = (MyAdapterClassInstance) ((HeaderViewListAdapter) myListView.getAdapter()).getWrappedAdapter();
this 100% works.
in adapter set your code like this
//in getCount
#Override
public int getCount() {
return ArrayList.size()+1;
}
//in getview make your code like this
public View getView(final int i, View view, ViewGroup viewGroup) {
view = inflter.inflate(R.layout.yourlayout, null);
if(i<getCount()-1) {
//your code
}
else{
ViewGroup itemContainer =(ViewGroup) view.findViewById(R.id.container);
itemContainer.setVisibility(View.INVISIBLE);
}
Return view;
}
if you have multiple listviews in your app, create an xml of a footer, something like this:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:minHeight="200dp"
android:layout_width="match_parent"
android:layout_height="200dp"></LinearLayout>
and then in the code, use this:
listView.addFooterView(LayoutInflater.from(getActivity()).inflate(R.layout.empty200, null));
This do the job in a simple way
android:paddingBottom="100dp"
android:clipToPadding="false"
Try the followings:
View footer = new View(getActivity());
footer.setLayoutParams( new AbsListView.LayoutParams( LayoutParams.FILL_PARENT, 100 ));
// 100 is the height, you can change it.
mListView.addFooterView(footer, null, false);