I am implementing the search functionality which consists of a search box, and a list of categories. For design purposes I have used EditText , GridView etc.
But when the activity starts I don't want all the search box to be displayed. I just want the EditText, and then when the EditText is clicked I need the view to expand, and when some other part of the screen is clicked, for example touching the ListView or pressing the button I need the view to collapse in its default state consisting the EditText.
I can use View.GONE or VIEW.VISIBLE but I want to reach a smooth animation for expanding and collapsing.
My activity_layout.xml:
<RelativeLayout
android:id="#+id/search_box"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="2dp"
android:layout_marginRight="2dp"
android:background="#android:color/transparent"
>
<android.support.v7.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:id="#+id/cv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:background="#drawable/njoftime_item_background"
card_view:cardCornerRadius="1dp"
card_view:cardElevation="2dp">
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#color/white"
android:id="#+id/sr"
android:animateLayoutChanges="true"
android:padding="20dp">
<RelativeLayout
android:layout_width="fill_parent"
android:id="#+id/search_area"
android:layout_height="wrap_content"
android:focusableInTouchMode="true"
android:background="#drawable/njoftime_item_background"
>
<ImageButton
android:layout_width="100dp"
android:layout_height="50dp"
android:layout_marginLeft="1dp"
android:id="#+id/btn_search"
android:layout_alignParentRight="true"
android:background="#color/njoftime_main_color"
android:src="#drawable/ic_search_white"
android:onClick="searchPressed"
android:scaleType="fitCenter"
/>
<EditText
android:layout_width="fill_parent"
android:layout_height="50dp"
android:layout_toLeftOf="#+id/btn_search"
android:id="#+id/search_text"
android:layout_alignParentLeft="true"
android:background="#null"
android:ems="20"
android:textSize="18sp"
android:paddingLeft="10dp"
android:imeOptions="actionSearch"
android:inputType="text"
android:maxLines="1"
android:textColor="#color/njoftime_desc"
android:textCursorDrawable="#null"
/>
</RelativeLayout>
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="#+id/search_categories_area"
android:layout_below="#+id/search_area"
android:paddingTop="20dp"
android:paddingBottom="10dp"
>
<GridView
android:id="#+id/search_grid"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:drawSelectorOnTop="false"
android:horizontalSpacing="2dp"
android:numColumns="5"
android:padding="4dp"
android:verticalSpacing="4dp" >
</GridView>
</RelativeLayout>
</RelativeLayout>
</android.support.v7.widget.CardView>
</RelativeLayout>
For this I have used android:animateLayoutChanges="true" and the following code
my_relative_layout = (RelativeLayout) findViewById(R.id.search_categories_area);
with:
public void searchPressed(View v) {
if (!searchbox_expanded) {
my_relative_layout.setVisibility(View.VISIBLE);
searchbox_expanded = true;
} else {
my_relative_layout.setVisibility(View.GONE);
searchbox_expanded = false;
}
}
Well the expanding is really smooth animation, but the collapsing is not animated.
I have used other solutions online but I have not reached the desire effect due to the complex layout I think. Any solutions will be appreciated.
This is quite simple :
To collapse the view you only need to add an onClickListener on the parent view (search_box) which triggers a function to collapse the view with an animation.
To expand the view you need to trigger another animation to expend the view when clicking on the search EditText
Here is an example of animation to collapse and to expend a view:
` import android.view.View;
import android.view.animation.Animation;
import android.view.animation.Transformation;
/**
* Created by wajdichamakhi on 26/01/15.
*/
public class ResizeAnimation extends Animation {
private int endHeight; // distance between start and end height
private int endWidth; // distance between start and end width
private View view;
/**
* constructor, do not forget to use the setParams(int, int) method before
* starting the animation
*
* #param v
*/
public ResizeAnimation(View v) {
this.view = v;
}
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
view.getLayoutParams().height = (int) (view.getHeight() + (endHeight - view.getHeight()) * interpolatedTime);
view.getLayoutParams().width = (int) (view.getWidth() + (endWidth - view.getWidth()) * interpolatedTime);
view.requestLayout();
}
/**
* set the starting and ending height for the resize animation
* starting height is usually the views current height, the end height is the height
* we want to reach after the animation is completed
*
* #param endWidths width in pixels
* #param endHeight height in pixels
*/
public void setParams(int endWidths, int endHeight) {
this.endWidth = endWidths;
this.endHeight = endHeight;
}
/**
* set the duration for the hideshowanimation
*/
#Override
public void setDuration(long durationMillis) {
super.setDuration(durationMillis);
}
#Override
public boolean willChangeBounds() {
return true;
}
/**
* Verify if this animation needs to be fired or not.
*/
public boolean isValidAnimation() {
if (this.endWidth == view.getWidth() && this.endHeight == view.getHeight())
return false;
else
return true;
}
}`
Now you put the function that returns a set of animation which contains this animation. I work with animation set so I can add several animation at once to the view and create a cool animation.
public AnimationSet getExpend_CollapseAnimationSet(final View v, int height, int width) {
AnimationSet animationSet = new AnimationSet(true);
ResizeAnimation resizeAnimation = new ResizeAnimation(v);
resizeAnimation.setParams(
width,
height);
if (resizeAnimation.isValidAnimation()) {
resizeAnimation.setDuration(ANIMATION_DURATION);
animationSet.addAnimation(resizeAnimation);
}
return animationSet;
}
Now use this function in your listeners with the right width and height
AnimationSet initialStateAnimation = getExpend_CollapseAnimationSet(myViewToExpend_Collapse,deiredHeight, desiredWidth);
myViewToExpend_Collapse.startAnimation(initialStateAnimation);
Related
I have a CardView with the following views:
- RelativeLayout (view_1)
- TextView
- TextView
- RelativeLayout (view_2)
- ImageView
- ImageView
When i open my ListView view_2 is set to Visible.GONE so you cant see it.
Now i used the ExpandAnimation from this blog. Here you have the code:
/**
*
*/
public class ExpandAnimation extends Animation {
/**
*
*/
private AnimationCallback callback = null;
/**
*
*/
private View viewToAnimate = null;
/**
*
*/
private RelativeLayout.LayoutParams viewToAnimateLayoutParams = null;
/**
*
*/
private int marginStart = 0;
/**
*
*/
private int marginEnd = 0;
/**
*
*/
private boolean isVisibleAfter = false;
/**
*
*/
private boolean isEndedAlready = false;
/**
*
* #param callback
* #param view
* #param duration
*/
public ExpandAnimation(AnimationCallback callback, View view, int duration) {
this.setDuration(duration);
this.callback = callback;
this.viewToAnimate = view;
this.viewToAnimateLayoutParams = (RelativeLayout.LayoutParams) view.getLayoutParams();
this.isVisibleAfter = (view.getVisibility() == View.VISIBLE);
this.marginStart = this.viewToAnimateLayoutParams.bottomMargin;
this.marginEnd = (this.marginStart == 0 ? (0 - view.getHeight()) : 0);
view.setVisibility(View.VISIBLE);
}
/**
*
* #param interpolatedTime
* #param transformation
*/
#Override
protected void applyTransformation(float interpolatedTime, Transformation transformation) {
super.applyTransformation(interpolatedTime, transformation);
if (interpolatedTime < 1.0f) {
this.viewToAnimateLayoutParams.bottomMargin = this.marginStart + (int) ((this.marginEnd - this.marginStart) * interpolatedTime);
this.viewToAnimate.requestLayout();
} else if (!this.isEndedAlready) {
this.viewToAnimateLayoutParams.bottomMargin = this.marginEnd;
this.viewToAnimate.requestLayout();
if (this.isVisibleAfter) {
this.viewToAnimate.setVisibility(View.GONE);
}
this.isEndedAlready = true;
this.callback.onAnimationCompleted(-1);
}
}
}
I use it inside my AdapterView:
public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, AnimationCallback {
private TextView textViewValue = null;
private TextView textViewTime = null;
private RelativeLayout relativeLayoutExpand = null;
private ImageView imageViewMood = null;
private ImageView imageViewMeal = null;
public ViewHolder(View itemView) {
super(itemView);
this.textViewValue = (TextView) itemView.findViewById(R.id.textview_value);
this.textViewTime = (TextView) itemView.findViewById(R.id.textview_time);
this.relativeLayoutExpand = (RelativeLayout) itemView.findViewById(R.id.list_row_expand);
this.imageViewMood = (ImageView) itemView.findViewById(R.id.imageview_mood);
this.imageViewMeal = (ImageView) itemView.findViewById(R.id.imageview_meal);
itemView.setOnClickListener(this);
}
#Override
public void onClick(View view) {
this.animate(this.relativeLayoutExpand);
}
public void animate(final View view) {
ExpandAnimation expand = new ExpandAnimation(this, view, 500);
view.startAnimation(expand);
}
public void onAnimationCompleted(int position) {
}
}
I only have one problem now: When i expand an item inside my list the first time it appears instantly without animation and after it the closing and opening works smoothly with animation.
Does someone know why the first opening happens without animation but every click after that is triggering the correct animation?
I also need the animation for the first click.
EDIT
Is it because initially my views visibility is GONE and so the height is unknown? If yes: How could i solve this?
EDIT 2
No, my XML layout file does not set the view to GONE:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginBottom="10dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<RelativeLayout
android:id="#+id/datatransfer_measure_list_row_data"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingEnd="10dp"
android:paddingStart="10dp"
android:paddingTop="10dp">
<TextView
android:id="#+id/datatransfer_measure_list_row_textview_value"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:lines="1"
android:maxLines="1"
android:singleLine="true"
android:text="#string/text_value"
android:textColor="#color/textcolorprimary"
android:textSize="30sp" />
<TextView
android:id="#+id/datatransfer_measure_list_row_textview_time"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/datatransfer_measure_list_row_textview_value"
android:layout_gravity="center"
android:layout_marginBottom="10dp"
android:gravity="start"
android:lines="1"
android:maxLines="1"
android:singleLine="true"
android:textColor="#color/textcolorprimary"
android:textSize="14sp" />
<ImageView
android:id="#+id/datatransfer_measure_list_row_imageview_expand"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentTop="true"
android:contentDescription="#string/text_description"
android:scaleType="fitCenter"
android:src="#drawable/ic_expand_more_black_24dp" />
</RelativeLayout>
<RelativeLayout
android:id="#+id/datatransfer_measure_list_row_expand"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/datatransfer_measure_list_row_data"
android:background="#color/primaryColor"
android:paddingBottom="10dp"
android:paddingEnd="10dp"
android:paddingStart="10dp"
android:paddingTop="10dp">
<RelativeLayout
android:id="#+id/datatransfer_measure_list_row_mood"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:lines="1"
android:maxLines="1"
android:singleLine="true"
android:text="#string/text_mood"
android:textColor="#color/textcolorsecundary"
android:textSize="30sp" />
<ImageView
android:id="#+id/datatransfer_measure_list_row_imageview_mood"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:contentDescription="#string/text_description"
android:scaleType="fitCenter" />
</RelativeLayout>
<ImageView
android:id="#+id/datatransfer_measure_list_row_imageview_meal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/datatransfer_measure_list_row_mood"
android:layout_centerInParent="true"
android:contentDescription="#string/text_description" />
</RelativeLayout>
</RelativeLayout>
</android.support.v7.widget.CardView>
I set the View.GONE in my ListAdapter:
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
...
viewHolder.textViewTime.setText(time);
viewHolder.textViewValue.setText(value + " " + unitValue);
viewHolder.relativeLayoutExpand.setVisibility(View.GONE); // <-- HERE
...
}
Edit 3
Maybe it is important to know that the container of my cardviews is a recyclerview:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/datatransfer_measure_list_textview_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:background="#color/primaryColorDark"
android:gravity="center"
android:padding="10dp"
android:textColor="#color/white"
android:textSize="18dp" />
<android.support.design.widget.CoordinatorLayout
android:id="#+id/datatransfer_measure_list_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#+id/datatransfer_measure_list_textview_name">
<android.support.v4.widget.SwipeRefreshLayout
android:id="#+id/datatransfer_measure_list_swipe_refresh_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="#+id/datatransfer_measure_list_row_parent"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:padding="10dp"
android:textColor="#color/textcolorprimary" />
</android.support.v4.widget.SwipeRefreshLayout>
<android.support.design.widget.FloatingActionButton
android:id="#+id/datatransfer_measure_list_floating_action_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="20dp"
android:layout_marginEnd="16dp"
android:clickable="true"
android:src="#drawable/ic_search_white_24dp"
app:backgroundTint="#color/primaryColorDark"
app:borderWidth="0dp"
app:elevation="6dp"
app:layout_anchor="#id/datatransfer_measure_list_row_parent"
app:layout_anchorGravity="bottom|right|end"
app:layout_behavior="de.hof.ime_dc.idia_move.views.buttons.ScrollFABBehaviour"
app:rippleColor="#color/accentColor" />
</android.support.design.widget.CoordinatorLayout>
</RelativeLayout>
I really need a solution for that. It is disturbing many of needed functionality.
Edit 4
I added some Log-Outputs:
=============================== EXPAND =========================================
08-02 12:27:22.471 V/ExpandAnimation﹕ View is visible: 8 (GONE)
08-02 12:27:22.471 V/ExpandAnimation﹕ marginStart: 0
08-02 12:27:22.471 V/ExpandAnimation﹕ marginEnd: 0
08-02 12:27:22.472 V/ExpandAnimation﹕ View is visible: 0 (VISIBLE)
============================== COLLAPSE ========================================
08-02 12:27:51.015 V/ExpandAnimation﹕ View is visible: 0 (VISIBLE)
08-02 12:27:51.015 V/ExpandAnimation﹕ marginStart: 0
08-02 12:27:51.015 V/ExpandAnimation﹕ marginEnd: -156
08-02 12:27:51.015 V/ExpandAnimation﹕ View is visible: 8 (GONE)
=============================== EXPAND =========================================
08-02 12:27:56.007 V/ExpandAnimation﹕ View is visible: 8 (GONE)
08-02 12:27:56.007 V/ExpandAnimation﹕ marginStart: -156
08-02 12:27:56.007 V/ExpandAnimation﹕ marginEnd: 0
08-02 12:27:56.008 V/ExpandAnimation﹕ View is visible: 0 (VISIBLE)
============================== COLLAPSE ========================================
08-02 12:27:51.015 V/ExpandAnimation﹕ View is visible: 0 (VISIBLE)
08-02 12:27:51.015 V/ExpandAnimation﹕ marginStart: 0
08-02 12:27:51.015 V/ExpandAnimation﹕ marginEnd: -156
08-02 12:27:51.015 V/ExpandAnimation﹕ View is visible: 8 (GONE)
=============================== EXPAND =========================================
08-02 12:27:56.007 V/ExpandAnimation﹕ View is visible: 8 (GONE)
08-02 12:27:56.007 V/ExpandAnimation﹕ marginStart: -156
08-02 12:27:56.007 V/ExpandAnimation﹕ marginEnd: 0
08-02 12:27:56.008 V/ExpandAnimation﹕ View is visible: 0 (VISIBLE)
As you can see the first marginStart of the expand is 0 but it has to be -156.
You did not show your XML but I assume you got android:visibility="gone" there. If so, you should remove it from layout file (so it is visible by default). if it should be gone, call setVisibility(View.GONE) on it after inflation. That way you will get the same result but view would also be able to get its dimensions.
As your code stands, it reads the values for the margins as you have initialised them, as 0,0.
You'll need to set the view to VISIBLE to get margins, as set to GONE it will have zero margins.
So order your code like this:
this.viewToAnimate = view;
view.setVisibility(View.VISIBLE);
this.viewToAnimateLayoutParams = (RelativeLayout.LayoutParams) view.getLayoutParams();
// Now the margins will not be zero.
this.marginStart = this.viewToAnimateLayoutParams.bottomMargin;
this.marginEnd = (this.marginStart == 0 ? (0 - view.getHeight()) : 0);
Don't initialise these variables.
private int marginStart = 0;
private int marginEnd = 0;
or even this:
private RelativeLayout.LayoutParams viewToAnimateLayoutParams = null;
You only need to initialise the ones that need a start value for you algorithm.
Like bool values.
private boolean isVisibleAfter = false;
private boolean isEndedAlready = false;
I'm having issue with this line of code:
this.isVisibleAfter = (view.getVisibility() == View.VISIBLE);
I don't understand why your code is not showing some warning or error in your logcat with this line.
(view.getVisibility() == View.VISIBLE) is not returning a value of visibility.
this.isVisibleAfter = view.getVisibility();
// will return the visibility of the view not a bool value.
if(view.getVisibility() == View.VISIBLE){
// will run only if the visibility is set to visible.
if(view.getVisibility() == View.INVISIBLE)
if(view.getVisibility() == View.GONE)
It's not a boolean value. It can return one of three values.
I suggest changing it to:
if(view.getVisibility() == View.VISIBLE){
this.isVisibleAfter = true;
}
else{
this.isVisibleAfter = false;
}
Using GONE will just collapse the space taken up by that view. INVISIBLE will retain the space taken up by the view. Unless you want to keep your layout elements forced into a certain position, I would prefer GONE.
I hope this helps, let me know.
I am having problem in animation of a single list item. how can i get the flip view upon click on the single item and get the detail on back of the item?
Create your list row item as below
<RelativeLayout
android:id="#+id/rellay"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_marginTop="20dp"
android:layout_centerHorizontal="true" >
<TextView
android:id="#+id/back"
android:layout_width="300dp"
android:layout_height="200dp"
android:background="#drawable/linearlayoutshape"
android:gravity="center"
android:text="BACK"
android:textAppearance="#android:style/TextAppearance.Large"
/>
<TextView
android:id="#+id/front"
android:layout_width="300dp"
android:layout_height="200dp"
android:background="#drawable/linearlayoutshape"
android:gravity="center"
android:text="FRONT"
android:textAppearance="#android:style/TextAppearance.Large" />
In this case i used to textView as Front and Back.
Now inside your adapter add this method..
public void flip(final View front, final View back, final int duration)
{
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
AnimatorSet set = new AnimatorSet();
set.playSequentially(
ObjectAnimator.ofFloat(front, "rotationY", 90).setDuration(duration / 2),
ObjectAnimator.ofInt(front, "visibility", View.GONE).setDuration(0),
ObjectAnimator.ofFloat(back, "rotationY", -90).setDuration(0),
ObjectAnimator.ofInt(back, "visibility", View.VISIBLE).setDuration(0),
ObjectAnimator.ofFloat(back, "rotationY", 0).setDuration(duration / 2));
set.start();
} else
{
front.animate().rotationY(90).setDuration(duration / 2)
.setListener(new AnimatorListenerAdapter()
{
#Override
public void onAnimationEnd(Animator animation) {
front.setVisibility(View.GONE);
back.setRotationY(-90);
back.setVisibility(View.VISIBLE);
back.animate().rotationY(0).setDuration(duration / 2).setListener(null);
}
});
}
}
Now on click of row item just call flip method like
flip(txtFront, txtBack, DURATION);
It will add the flip animation and make txtBack visible and hide the txtFront
I have been trying to implement an animation on a relativeLayout. The animation works fine but after the animation is complete the edittext and the imageview inside the relative layout stops responding to any kind of user activity.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<RelativeLayout
android:id="#+id/rl_maincontroller_mainLayout"
android:layout_width="match_parent"
android:layout_height="380dp"
android:background="#color/button_color" >
<ImageView
android:id="#+id/iv_maincontroller_backImage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_centerVertical="true"
android:src="#drawable/back" />
<EditText
android:id="#+id/tv_searchBox"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:layout_toRightOf="#+id/iv_maincontroller_backImage"
android:background="#android:color/white"
android:drawableLeft="#drawable/search"
android:drawablePadding="10dp"
android:gravity="center_vertical"
android:layout_centerVertical="true"
android:hint="#string/search"
android:paddingLeft="10dp"
android:textColor="#android:color/black"
android:textColorHint="#color/grey"
android:textSize="17sp" />
</RelativeLayout>
</LinearLayout>
Before the animation the controls on the widgets work fine, the animation changes the parent relative layout from height 380 to 120 and after that the widgets become dead to any kind of user activity. I cannot control them. Can anyone please suggest an answer. Need it very urgently, have been stuck in it for hours.
The code for animating the layout is put below. I got it from a link and it worked as I wanted the animation to work but the widgets inside the layout became dead.
public class ResizeAnimation extends Animation {
private int startHeight;
private int deltaHeight; // distance between start and end height
private View view;
/**
* constructor, do not forget to use the setParams(int, int) method before
* starting the animation
* #param v
*/
public ResizeAnimation (View v) {
this.view = v;
}
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
view.getLayoutParams().height = (int) (startHeight + deltaHeight * interpolatedTime);
view.requestLayout();
}
/**
* set the starting and ending height for the resize animation
* starting height is usually the views current height, the end height is the height
* we want to reach after the animation is completed
* #param start height in pixels
* #param end height in pixels
*/
public void setParams(int start, int end) {
this.startHeight = start;
deltaHeight = end - startHeight;
}
/**
* set the duration for the hideshowanimation
*/
#Override
public void setDuration(long durationMillis) {
super.setDuration(durationMillis);
}
#Override
public boolean willChangeBounds() {
return true;
}
Then I call this class as follows:
ResizeAnimation a = new ResizeAnimation(layout);
a.setDuration(500);
a.setParams(oldHeight, newHeight);
layout.startAnimation(a);
I have researched the questions thoroughly, but could not yet find the answer. Also, my excuses for my poor english since I am not a native speaker.
The problem: in my android layout we have a status_text with a listview below the status_text. When the status_text is touched, we animate a 'move down' on the status_text and listview so that only the first of the listview row is still on screen. The listview is now still usable.
When the status_text is touched again, we move the status_text and listview up so that the listview uses half of the screen.
The problem we are facing is that during the 'move up' only the first row is animated. After the 'move up' the other rows suddenly appear.
What we would like to have is a 'move up' where the previously hidden rows slide onto the screen.
The layout:
We are using this layout (slightly simplified to focus on the problem at hand):
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/fragment_declareren_choose_verzekerden"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<!-- Dummy anchor to put top of listview in the middle of the screen -->
<RelativeLayout
android:id="#+id/anchor"
style="#style/anchor_status_container"
android:layout_centerVertical="true" >
</RelativeLayout>
<!-- Example image -->
<ImageView
android:id="#+id/my_image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="#id/footer"
android:adjustViewBounds="true"
android:contentDescription="#string/image_description"
android:scaleType="centerCrop"
android:src="#drawable/empty" />
<!-- Clickable text which moves up and down on click -->
<RelativeLayout
android:id="#+id/status_container"
style="#style/status_container"
android:layout_alignTop="#id/anchor"
android:background="#color/white" >
<TextView
android:id="#+id/status_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginRight="#dimen/spacing_sml"
android:layout_alignTop="#id/status_container" />
</RelativeLayout>
<!-- Listview which moves up and down with the status_container -->
<RelativeLayout
android:id="#+id/listView_container"
style="#style/padding_content_horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#id/status_container"
android:background="#color/white" >
<ListView
android:id="#+id/mylistView"
style="#style/myListviewStyle"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="true"
android:divider="#null"
android:dividerHeight="0dp" />
</RelativeLayout>
<!-- Footer with buttons -->
<RelativeLayout
android:id="#+id/footer_button_container"
style="#style/footer"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<Button
android:id="#+id/button_again"
style="#style/btn_secondary"
android:layout_width="wrap_content"
android:layout_alignParentLeft="true"
android:text="#string/opnieuw"
android:visibility="gone" />
<Button
android:id="#+id/button_next"
style="#style/btn_primary"
android:layout_width="wrap_content" />
</RelativeLayout>
</RelativeLayout>
And the code (again a bit simplified to show only the problem at hand. Some fade-in/out and rotations are removed):
// The code
#Override
public void onClick(View view)
{
int viewId = view.getId();
if (viewId == R.id.status_container)
{
// Someone clicked the text, move the statusbar (and so the listview) up or down
if (this.viewIsInUpperPosition)
{
startStatusAnimation();
}
}
}
private void startStatusAnimation()
{
if (animationIsRunning)
{
return;
}
setAnimationIsRunning(animValues.START);
// 0. Initialisation
final View statusContainer = (View) getView().findViewById(R.id.status_container);
final View listContainer = (View) getView().findViewById(R.id.listView_container);
final ListView listView = (ListView) getView().findViewById(R.id.myListView);
final View footerButtonContainer = (View) getView().findViewById(R.id.footer_button_container);
// 1. Calculate distance for animation
if (toggleViewDistance == 0)
{
int listViewContainerHeight = listContainer.getHeight();
int footerHeight = footerButtonContainer.getHeight();
int spaceForListView = listViewContainerHeight - footerHeight;
toggleViewDistance = spaceForListView;
}
// 2. Decide if the movement is up or down
float translationDistance = (viewIsInUpperPosition) ? toggleViewDistance : 0 - toggleViewDistance;
// 3. Create the animation
TranslateAnimation yMove = new TranslateAnimation(0, 0, 0, translationDistance);
yMove.setDuration(animValues.ANIMATION_Y_DURATION);
yMove.setInterpolator(new AccelerateDecelerateInterpolator());
// Do here something with scaling and rotating of other objects, not relevant for the question on StackOverflow
// 4. Actions after animation
yMove.setAnimationListener(new AnimationListener()
{
#Override
public void onAnimationEnd(Animation arg0)
{
// Fade de listView in als je van onderen naar boven animeert
if (!viewIsInUpperPosition)
{
// Do some fading, outside scope of question
}
// Create layout after the animation
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) statusContainer.getLayoutParams();
if (viewIsInUpperPosition)
{
// View was previously in upper position, now put the statusbar aligned with the footer
params = new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
params.addRule(RelativeLayout.ABOVE, footerButtonContainer.getId());
}
else
{
// View was previously in bottom position, so put it under the anchor
params = new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
params.addRule(RelativeLayout.ALIGN_TOP, R.id.anchor);
}
}
statusContainer.setLayoutParams(params); // Set the new layout params
viewIsInUpperPosition = !viewIsInUpperPosition;
setAnimationIsRunning(animValues.END);
}
#Override
public void onAnimationRepeat(Animation arg0)
{
// Empty
}
#Override
public void onAnimationStart(Animation arg0)
{
// empty
}
});
// 5. Start the animation
statusContainer.startAnimation(yMove);
listContainer.startAnimation(yMove);
}
Any advice on how to have the rows of the listview 'slide in' on the screen? Much appreciated!
I figured it out. So I am answering my own question in case someone stumbles upon this question.
What needs to be done is that the listview is drawn off-screen. This can be forced by calling the measure- and layout-methods with the off-screen coordinates of the listview.
This fixed it for my code:
// 5a. Draw the listview off-screen
if (translationDistance < 0)
{
// Do this only when the listview is sliding up, e.g. sliding the window in.
int listViewContainerVerticalPos = listContainer.getTop(); // De positie van de listview
// The required height of the listview
int listContainerHeight = (int) Math.abs(translationDistance) + statusContainer.getHeight();
int measureWidth = View.MeasureSpec.makeMeasureSpec(listContainer.getWidth(), View.MeasureSpec.EXACTLY);
int measureHight = View.MeasureSpec.makeMeasureSpec(listContainerHeight, View.MeasureSpec.EXACTLY);
listContainer.measure(measureWidth, measureHight);
listContainer.layout(0, listContainerVerticalPos, listContainer.getMeasuredWidth(), listContainerVerticalPos
+ listContainerHeight);
}
I have this layout for ListView item
<com.example.donutgraph.TalkingFrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:padding="10dp" >
<TextView
android:id="#+id/position"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="TextView" />
<FrameLayout
android:layout_width="150dp"
android:layout_height="150dp" >
<com.example.donutgraph.GaugeView
android:id="#+id/gauge"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_gravity="center" />
<ImageView
android:id="#+id/img"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_gravity="right"
android:layout_marginRight="10dp"
android:layout_marginTop="20dp" />
</FrameLayout>
</com.example.donutgraph.TalkingFrameLayout>
The one which animated is a GaugeView. I create a simple time-based animation which will invalidate the view like this
private Runnable animator = new Runnable() {
#Override
public void run() {
....
update(now);
if (!isFinish()) {
needNewFrame = true;
} else {
Log.e(tag, "millisecond : " + (now - startTime));
}
if (needNewFrame) {
postDelayed(this, timeBetweenFrame);
}
invalidate();
}
};
private void update(long now) {
float dt = Math.min(now - lastTime, timeBetweenFrame);
currentTime += dt;
if (currentTime > duration) {
currentTime = duration;
}
}
protected void onDraw(Canvas canvas){
....
float percent = getPercentForTime(currentTime);
canvas.drawArc(rectF, 270, percent * -360.0f, false, paint);
....
canvas.drawText(String.valueOf(percentInt), xPos, yPos, paint);
}
This is the result I archieve
The problem here is because of animation need to call invalidate so it invoke all onDraw() in a hierarchy which I don't want and I think because of that it made my ListView so laggy when scrolling during the animation. When no animation on this GaugeView ListView runs smoothly.
Anyway to optimize this so that I can scroll smoothly?
and also is there anyway to prevent invalidate() call to invoke onDraw() on all view hierarchy? because everything is the same except the GaugeView which will animate and it is the only one that changing. It seem like a waste to call onDraw() on my TalkingFrameLayout again and again without changing.
Thanks