I want to use this kind of UI in my Contact Android App. The picture shown resizes itself when we scroll down much and the Name 'XYZ' finally become the title of the action bar. Also how can I put textview over imageview?
What you're looking for is CollapsingToolbarLayout.
The general approach to this kind of animations is to use top level CoordinatorLayout which contain two children: the first child is the "collapsible" content, and the second is "body". The general hierarchy looks like this:
CoordinatorLayout
|-AppBarLayout
|-CollapsingToolbarLayout
|-Toolbar
|-ScrollView
The second child must not be ScrollView - use any ViewGroup that suits your needs, and just add the following attribute to its description in xml:
app:layout_behavior="#string/appbar_scrolling_view_behavior"
the above string resource should map to AppBarLayout.ScrollingViewBehavior (just use android.support.design.widget.AppBarLayout$ScrollingViewBehavior as this resource's value).
There is very good tutorial on this kind of animations on Codepath.
Related
I have a ScrollView which contains several RelativeLayouts and LinearLayouts like this:
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/scroll"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true" >
<RelativeLayout >
</RelativeLayout>
<LinearLayout >
</LinearLayout>
</ScrollView>
Now, when I click on one of those layouts, I want it to expand and reveal more information. I have managed to do this by scaling the layout I want vertically with a PropertyAnimator:
relativeLayout.animate().scaleY(100).setDuration(duration).start();
At the same time, I use another PropertyAnimator to move any Views below the one I expanded vertically so that there's enough space for the expanded layout. So far it is working.
Unfortunately, the Views that move somehow end up outside of the viewport of the ScrollView, so I'm unable to scroll down and see the information in those Views. Essentially, the vertical translation of those views renders their lower part unreachable, since the viewport does not expand too.
I have set android:fillViewPort="true" on the ScrollView. And I have also tried to do it programmatically with setFillViewPort() but neither has had any effect.
What's wrong? Why is it not working?
When you perform translation animations on Views then those Views don't really move inside the layout. Its just visual for the User, but when it comes to layouting and/or measuring than any translation values are ignored. It is always as if the Views are not translated at all.
What I am guessing you are doing right now is this:
You react to the click event and expand the View you want to expand.
You calculate how much the other Views need to move to accommodate the expanded View.
Then you perform translate animations on those Views by much they need to move.
And then as a result suddenly a few Views move off screen.
This approach can actually never work. You always need to remember that a Views position in the layout is determined just by the layout. All your translations are essentially just for show. So this is what's actually happening when you try to do the above:
You react the to the click event and expand the View.
This expansion causes Android to start a layouting and measuring process. The positions and sizes of all Views is calculated and they are positioned at their new location with their new size.
Since now the Views are already at the location they are going to be after the expansion you translation animation just moves the Views further down, beyond the point they are supposed to be.
As a side effect of this the Views seem to move off screen for no apparent reason.
So what can you do about this? Essentially you need to tackle this problem the other way around. As I mentioned above Android already calculates the new sizes and positions of all Views for you, and you can use that to your advantage.
There are two basic solutions for your problem. Either you let Android perform the animations for you with LayoutTransitions or you perform your animations manually. Both ways use something called the ViewTreeObserver. It can be used to listen for changes in the layout or new drawing processes.
But first and foremost: ScrollView is supposed to work with only one child. So to prevent any future bugs or problems put all your items in the ScrollView inside of another LinearLayout with vertical orientation.
1) Using LayoutTransition
This would only work from API Level 16 and above. Below API Level 16 visibility animations and translation animations would be handled automatically, but to get height changes animated you need to have API level 16.
One important thing I have to mention is that:
LayoutTransition animates changes for you. So you can remove all you custom animations if you use it. If you leave your own animations in you are just going to create conflicts with the animations performed by LayoutTransition.
If you don't like the animations performed by LayoutTransition you can customise them! I will explain how to do that further down below.
I usually use a helper method like this to setup a LayoutTransition.
public static void animateLayoutChanges(ViewGroup container) {
final LayoutTransition transition = new LayoutTransition();
transition.setDuration(300);
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
transition.enableTransitionType(LayoutTransition.CHANGING);
transition.enableTransitionType(LayoutTransition.APPEARING);
transition.enableTransitionType(LayoutTransition.CHANGE_APPEARING);
transition.enableTransitionType(LayoutTransition.DISAPPEARING);
transition.enableTransitionType(LayoutTransition.CHANGE_DISAPPEARING);
}
container.setLayoutTransition(transition);
}
This will enable all possible automatic transitions on API Level 16 and above and just use the by default enabled transitions below that. Just use it like this:
AnimatorUtils.animateLayoutChanges(linearLayout);
If you call this method on the LinearLayout in your ScrollView then all changes to height/width/visibility of the LinearLayout and its direct children will be animated for you. Also item add/remove animations are taken care of for you.
To enable all kinds of transitions like resize animations you need to set the LayoutTransitions in code, but you can enable basic transitions like item add/remove animations by setting the property
android:animateLayoutChanges="true"
on a ViewGroup in your xml layout.
There exists only minimal documentation on LayoutTransitions, but the basics are covered here.
If you want you can customise the animations for each event like adding/removing a View or changing something about the View like this:
// APPEARING handles items being added to the ViewGroup
transition.setAnimator(LayoutTransition.APPEARING, someAnimator);
// CHANGING handles among other things height or width changes of items in the ViewGroup
transition.setAnimator(LayoutTransition.CHANGING, someOtherAnimator);
Here is a DevByte video which explains LayoutTransitions in greater detail:
LayoutTransitions enable easy fade/move/resize animations
Also note that container views can essentially cut off parts of the animations when the height of a parent changes. This won't happen in your case since your ScrollView has a fixed size and does not resize based on the children inside the ScrollView, but if you implement something like this in a ViewGroup with wrap_content then you need to set android:clipChildren="false" on all containers above the Views you are trying to animate. You can alternatively also use setClipChildren() in code.
2) Animating all items manually.
This is a much more difficult than using LayoutTransitions, mainly because you have to know a lot about the layouting and measuring process, otherwise you are going to cause problems. Nevertheless once you get the hang of it you can perform all kinds of custom animations.
The basic process is like this:
Record current View state.
Change layout to the state after the animations are finished
After Android is done layouting and measuring everything record the new values.
Now animate the Views from their old position to their new one
The core of this process is listening for changes in the view hierarchy. This is done using the ViewTreeObserver. There are multiple possible callbacks you can use, for example OnPreDrawListener or OnGlobalLayoutListener. Generally you would implement them like this:
final Animator animator = setupAnimator();
animator.setTarget(view);
// Record the current state
animator.setupStartValues();
modifyChildrenOfLinearLayout();
linearLayout.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
// Remove the callback immediately we only need to catch it this one time.
linearLayout.getViewTreeObserver().removeOnGlobalLayoutListener(this);
// Record the new state
animator.setupEndValues();
// Start the animation
animator.start();
}
});
OnGlobalLayoutListener is better at catching layout changes since it is called after a layouting process has finished. OnPreDrawListener is called before the next frame is drawn, but their is no guarantee that the layouting process has already finished. But in practice this difference is negligible. Much more important is that on older slower devices there might be a short flash of the layout in its new state because they need some time to process each step. You can prevent that by using an OnPreDrawListener and returning false once. Since OnGlobalLayoutListener is also only completely available on newer API levels you should in most cases use OnPreDrawListener.
If LayoutTransitions does not provide you with an adequate solution to your problem and you have/want to implement the animations manually than learning how to perform animations efficiently is important. You can look at the source code of LayoutTransition here. The implementation of LayoutTransition essentially does exactly what I have been explaining here and it is a best practice implementation. I often find myself looking through the source code of the android.animation package to learn new things about how to animate efficiently and if you want to understand animations on Android I suggest you do the same!
You can also watch a few Android DevBytes videos about animations like this one:
ListView Expanding Cells Animation
In this video he explains how to animate an expanding cell in ListView by using an OnPreDrawListener.
Just always remember, the Layouting Engine is your friend. Don't try to reinvent the wheel and do stuff manually a layouting process would already do for you. And never call requestLayout() while performing animations!
From Android Developer site :
A ScrollView is a FrameLayout, meaning you should place one child in it containing the entire contents to scroll; this child may itself be a layout manager with a complex hierarchy of objects. A child that is often used is a LinearLayout in a vertical orientation, presenting a vertical array of top-level items that the user can scroll through.
I can see that you have added multiple layouts as child in Scroll View, please add one linear layout and add rest of layout in that LinearLayout.
Hope it will solve your problem
Try this.
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/scroll"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<RelativeLayout >
</RelativeLayout>
<LinearLayout >
</LinearLayout>
</LinearLayout>
</ScrollView>
Scrollview must have only one child to it. So I created only one LinearLayout child and add your rest code in it.
just add android:clipChildren="false"to your parent animated view and animation works outside of view.
I was testing my android app on device by enabling device show layout boundaries in developer option on device.
I check my listview with inflated view with textviews , rating bar and other views clearly seen as shown below .
later I tried twitter app but surprise to see only single view ???
anyone know how to get twitter like single view on listview ??
anyone know how to get twitter like single view on listview ??
Each list item is a single custom View object, not a ViewGroup or layout. Essentially, all the content is drawn directly onto the Canvas in onDraw() rather than relying on child ImageView and TextView elements. Images can be drawn easily enough by calling Drawable.draw() or Canvas.drawBitmap() and text is typically drawing using a Layout.
Additionally, this means all touch events are handled directly inside onTouchEvent() to handle taps on the lower icons and/or the avatar image, so there are no click listeners.
Edit: Here's a quick 30 second example that should be enough to get you started: https://gist.github.com/devunwired/8704007
They are probably using the include tag to include another layout file
you can do the same thing if you make your list_row_item layout something like this:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width=”match_parent”
android:layout_height=”match_parent”
>
<include layout="#layout/another_layout"/>
</LinearLayout>
The benefit to doing this is that you can swap layouts on the fly, perhaps even using a server side changes, rather than having to issue a software update
I am destroying a programmatically created fragment with:
getFragmentManager().beginTransaction().remove(getFragmentManager().findFragmentById(R.id.test)).commit();
Which is determined in the xml file like this:
<LinearLayout
android:id="#+id/test"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
</LinearLayout>
If I then create a fragment from the same class again in the mainactivity:
getSupportFragmentManager().beginTransaction()
.add(R.id.result_bar, testinstance)
.commit();
Then onCreate seem not called again (the fragment is just empty). What am I doing wrong here? Thanks.
Explanation: Why FrameLayout for Fragment
What is FrameLayout:
According to Google Documentation on Commons Layouts and this answer of What are the differences between LinearLayout, RelativeLayout, and AbsoluteLayout?, the ViewGroups as LinearLayout, RelativeLayout, AbsoluteLayout (decrepated), TableLayout, etc. are allow to display views:
LinearLayout: "displays views one by one"
RelativeLayout: "displays views regarding to another view"
TableLayout: "displays views in a table"
etc.
FrameLayout displays views by overlap the other. It is generally use to contain layouts:
"Frame layouts are one of the simplest and most efficient types of layouts used by Android developers to organize view controls. They are used less often than some other layouts, simply because they are generally used to display only one view, or views which overlap. The frame layout is often used as a container layout, as it generally only has a single child view (often another layout, used to organize more than one view)."
source: FrameLayout MobilTuts
"The Frame layout allows developers to display only a single or multiple UI elements within Frame Layout but each element will be positioned based on the top left of the screen, and elements that overlap will be displayed overlapping."
source: Android Frame Layout For Absolute Beginner
OK but, why do I need this for Fragment? (vs LinearLayout or <fragment>)
The Google Documentation of FrameLayout says:
"FrameLayout is designed to block out an area on the screen to display a single item."
FrameLayout will host a layout and it is willing for it. Whereas the rest of ViewGroups just display views. You can make a Fragment in all ViewGroups (I tested that, it was a surprise for me) but it will not a proper way to do this. The FrameLayout are:
"...the normal layout of choice when you want to overlap views."
If you create a layout with a <fragment .../>, your fragment will be not replace by another, because it is displayed, it is "attached" with its id on the view. To replace a fragment, you need to host it: "By encapsulating the Fragment within a FrameLayout, you can replace just the details" (see this answer).
Then, keep in mind that FrameLayouts are empties and can host a layout. Fragments (Fragment Documentation from Google, it explains very simply the facts to how use a fragment), when they are declared in xml, they must to have a class attached (an id), which cannot be replaced.
That's why we need a container to host this fragment and overlap the view of the activity!
Hope this will be helpful.
Note: If someone wants to edit this answer, because something it's not explain or badly explain, she/he could with heartily.
I've been searching for this topic for a while, and can't find enough resources or tuts about scrolling a PagerTabStrip. I'm using the FragmentStatePagerAdapter and populates the PagerTabStrip with getPageTitle(int position). I'd like to know how to make the titles scrollable. I'd like to scroll the titles without affecting the view by the time I stop or select into a specific title, then that's the time the view gets updated. I've been thinking to use HorizontialListView but not sure how to start. Hoping to learn from you. Thanks.
Found this on docu:
PagerTabStrip is an interactive indicator of the current, next, and
previous pages of a ViewPager. It is intended to be used as a child
view of a ViewPager widget in your XML layout. Add it as a child of a
ViewPager in your layout file and set its android:layout_gravity to
TOP or BOTTOM to pin it to the top or bottom of the ViewPager. The
title from each page is supplied by the method getPageTitle(int) in
the adapter supplied to the ViewPager.
I been searching on this on the web, but I didn't get any relevant resources. I just found out another library called actionsherlock that enables the scrolling of tabs without affecting the view which is exactly what I need, instead of using PagerTabStrip's listener .
I'm also searching for the same thing. Too bad you have to make your own implementation or use third-party library. I have read that this library offer the feature of scrolling tab independent of the content. But I have not tried it out yet.
http://viewpagerindicator.com/?utm_source=androidweekly&utm_medium=toolbox
Do you actually mean the ActionBarSherlock? Do you have an example?
I'm looking for a way to update a layout's content with a new view. Is there any way to easily do this. It would be similar to how tabs work, but I don't want to have to get into extending the current tab structure if I don't have to.
The final result would be a few buttons that would switch the content in a specific linearlayout for each button.
Any help?
Have a look at ViewSwitcher. This is designed to help with the kind of things you are suggesting.
If you want the contents in entirely different layout files, you can use a LayoutInflater and add the inflated view to the parent view.