I need to do some like ripple effect of Listview item background changing. I try to use ObjectAnimator like this:
AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(activity,
R.animator.animator_bkg);
set.setTarget(childLinear);
set.setInterpolator(new AccelerateInterpolator());
set.start();
R.animator.animator_bkg:
<objectAnimator
android:propertyName="backgroundColor"
android:duration="3000"
android:valueFrom="#color/white"
android:valueTo="#color/redTrans"
android:repeatCount="-1"
android:repeatMode="reverse"/>
It fluently changes a background (complete filling), but I need gradual filling of ListView item like ripple effect after touch the button.
I think, maybe I can use Canvas with overriding onDraw, but it's to hard for application and it can be some lags.
You can do it with a custom view and implement the circular transition in onDraw(), but it's complicated.
You can work around the complexity by using ViewAnimationUtils.createCircularReveal() on sub view. But the draw back is that it's only for API 21+.
In few words, your root layout of the cell has to be a FrameLayout or a RelativeLayout.
When the transition starts, you dynamically add 2 views under your cell with the start and the end color, then transition with the circular reveal between the 2. At the end of the transition, you just remove the 2 sub views to keep the view hierarchy a bit cleaner.
Here is the result :
In code :
Cell layout:
<FrameLayout
android:id="#+id/cell_root"
android:layout_width="match_parent"
android:layout_height="72dp">
<LinearLayout
android:id="#+id/cell_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp"
android:background="#FF00FF">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Title"
android:textSize="18sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="This is content"
android:textSize="14sp" />
</LinearLayout>
</FrameLayout>
Code that trigger the background transition :
private void changeBackgroundColor() {
final FrameLayout startingColorFrame = new FrameLayout(mCellRoot.getContext());
final FrameLayout endingColorFrame = new FrameLayout(mCellRoot.getContext());
startingColorFrame.setBackground(mCellContent.getBackground());
endingColorFrame.setBackground(mPendingColor);
mCellContent.setBackground(null);
endingColorFrame.setVisibility(View.GONE);
mCellRoot.addView(endingColorFrame, 0, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
mCellRoot.addView(startingColorFrame, 0, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
int finalRadius = (int) Math.sqrt(mCellRoot.getWidth()*mCellRoot.getWidth() + mCellRoot.getHeight()*mCellRoot.getHeight());
final int sourceX = mCellRoot.getWidth() / 3;
final int sourceY = mCellRoot.getHeight() / 2;
// this is API 21 minimum. Add proper checks
final Animator circularReveal = ViewAnimationUtils.createCircularReveal(endingColorFrame, sourceX, sourceY, 0, finalRadius);
endingColorFrame.setVisibility(View.VISIBLE);
circularReveal.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(final Animator animation) {
super.onAnimationEnd(animation);
mStartButton.setEnabled(true);
mCellContent.setBackground(mPendingColor);
mPendingColor = startingColorFrame.getBackground();
mCellRoot.removeView(startingColorFrame);
mCellRoot.removeView(endingColorFrame);
}
});
// customize the animation here
circularReveal.setDuration(800);
circularReveal.setInterpolator(new AccelerateInterpolator());
circularReveal.start();
}
Related
I need to implement a circular reveal animation after the transition between 2 fragments is finished so that the ImageButton (android:id="#+id/update_profile_pic") will be revealed with a nice animation
The problem is that it doesn't work as it should:
setEnterSharedElementCallback(new SharedElementCallback() {
Handler handler = new Handler();
#Override
public void onSharedElementEnd(List<String> sharedElementNames,
List<View> sharedElements,
List<View> sharedElementSnapshots) {
handler.postDelayed(new Runnable() {
#Override
public void run() {
update_profile_pic.setVisibility(View.GONE);
// get the center for the clipping circle
int cx = update_profile_pic.getWidth() / 2;
int cy = update_profile_pic.getHeight() / 2;
// get the final radius for the clipping circle
float finalRadius = (float) Math.hypot(cx, cy);
// create the animator for this view (the start radius is zero)
Animator anim = ViewAnimationUtils.createCircularReveal(update_profile_pic, cx, cy, 0f, finalRadius);
// make the view visible and start the animation
update_profile_pic.setVisibility(View.VISIBLE);
anim.start();
}
}, 600);
}
});
When I try this animation in a button click listener, the button needs to be clicked twice to make the animation work
The layout is like this:
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/activity_simple_two"
android:layout_gravity="center_horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
tools:context=".FragmentB"
xmlns:app="http://schemas.android.com/apk/res-auto">
<com.mikhaellopez.circularimageview.CircularImageView
android:id="#+id/profile_picture"
android:clickable="false"
android:src="#drawable/roni"
app:civ_border_color="#color/fadedText"
app:civ_border_width="0.1dp"
android:layout_width="260dp"
android:layout_height="260dp"
android:layout_margin="18dp"
android:transitionName="#string/simple_fragment_transition"/>
<ImageButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="16dp"
android:scaleType="centerInside"
android:elevation="3dp"
android:layout_margin="26dp"
android:id="#+id/update_profile_pic"
android:src="#drawable/ic_menu_camera"
android:background="#drawable/round_button"
android:backgroundTint="#color/colorAccent"
app:layout_anchor="#id/profile_picture"
app:layout_anchorGravity="bottom|right|end"
android:visibility="gone"/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
Any help?
try to set visibility of your ImageButton to invisible instead of gone initially.
I'm working on getting a animation to work. I'm moving a search bar from the middle of the screen to the top of the screen. The animation works fine, but once it's moved I can't interact with anything. I've updated the position but can't interact with it regardless. Here's what I've got so far:
private void moveSearchToTop()
{
FrameLayout root = (FrameLayout) findViewById( R.id.rootLayout );
DisplayMetrics dm = new DisplayMetrics();
this.getWindowManager().getDefaultDisplay().getMetrics( dm );
statusBarOffset = dm.heightPixels - root.getMeasuredHeight();
int originalPos[] = new int[2];
mSearchBar.getLocationOnScreen( originalPos );
search_top = statusBarOffset - originalPos[1];
TranslateAnimation anim = new TranslateAnimation( 0, 0, 0, search_top);
anim.setDuration(500);
anim.setFillAfter(true);
anim.setAnimationListener(new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
}
#Override
public void onAnimationEnd(Animation animation) {
mSearchBar.layout(0, search_top, 0, mSearchBar.getHeight() + search_top);
}
#Override
public void onAnimationRepeat(Animation animation) {
}
});
mSearchBar.startAnimation(anim);
}
and here's what the view looks like in xml:
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:id="#+id/search_bar"
android:background="#android:drawable/dialog_holo_light_frame">
<ImageView
android:id="#+id/search_icon"
android:layout_width="wrap_content"
android:onClick="onSearchClick"
android:layout_height="25dp"
android:layout_gravity="center_vertical"
android:src="#drawable/search"/>
<EditText
android:id="#+id/search_field"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_weight="1"
android:singleLine="true"
android:hint="Search"/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="25dp"
android:layout_gravity="center_vertical"
android:src="#drawable/camera"/>
</LinearLayout>
Do I need to update the position of all of the child elements too? Thanks for a push in the right position.
No, you won't need to modify the child views. They will go where the search bar goes.
It looks to me like you are using your search_top variable both as a delta (for the TranslateAnimation constructor) and as a position within its parent (in the call to layout). It's really a delta, so the call to layout is incorrect.
Is the parent of your searchBar the "root" FrameLayout? If so, then you just want to use 0 as the new y coordinate in the call to layout. Also, you are using 0 as the right coordinate, which means that it has no width after the animation. This call would position your search bar at the upper-left edge of its parent, and maintain its current width and height:
mSearchBar.layout(0, 0, mSearchBar.getWidth(), mSearchBar.getHeight());
I also would recommend that you do not use screen position. It's safer to just set the position of views within parent views. For example, if the search bar's parent really is root, then you could get the delta with:
search_top = - mSearchBar.getTop();
I am new in Android animation and my requirement is to translate a view from one layout to layout in a single xml file on click of that view.
Scenario:
Suppose I click a button, present on the top of the header in a xml file,and it should move/translate downwards (it should give an impact that it lies on the other layout downwards to header), and also I want that when the user clicks on the same again, it should now move to its original position.
Here I am explaining with my xml file:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#drawable/app_bg"
android:orientation="vertical" >
<RelativeLayout
android:id="#+id/top"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#drawable/header"
android:paddingLeft="10dp"
android:paddingRight="10dp" >
<Button
android:id="#+id/btnSearchHeader"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_centerInParent="true"
android:background="#drawable/search_icon" />
</RelativeLayout>
<RelativeLayout
android:id="#+id/bottom"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#color/app_transparent"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:layout_marginTop="10dp"
android:visibility="visible" >
<Button
android:id="#+id/btnMenu"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:layout_marginRight="5dp"
android:text="ABC" />
<Button
android:id="#+id/btnSearchSelected"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toRightOf="#+id/btnMenu"
android:text="CDE" />
</RelativeLayout>
</LinearLayout>
MORE PRECISE REQUIREMENT SPECIFICATION (Kindly read carefully:)
Here I have two sub inner layouts:-
Top Layout - id-> top
Bottom Layout- id -> bottom
Now a view (Button -> btnSearchHeader) is lying in my top layout and I want to animate the same to the bottom layout (it should give an impact that it is translated with a translate animation to the bottom layout) on click of that button and when the user clicks on that button, it should again translate back to its original position with a translate animation .. i.e it should show back in the top layout
I have no idea how to give these impacts using translate animations, however i just have a basic translate animation knowledge which is insufficient for me to work upon my requirement.
Any type of related help is appreciable.
Thanks
Have you tried something simple like the following?
final int topHeight = findViewById(R.id.top).getHeight();
final int bottomHeight = findViewById(R.id.bottom).getHeight();
final View button = findViewById(R.id.btnSearchHeader);
final ObjectAnimator moveDownAnim = ObjectAnimator.ofFloat(button, "translationY", 0.F, topHeight + bottomHeight / 2 - button.getHeight() / 2);
final ObjectAnimator moveUpAnim = ObjectAnimator.ofFloat(button, "translationY", topHeight + bottomHeight / 2 - button.getHeight() / 2, 0.F);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (0.F == v.getTranslationY())
moveDownAnim.start();
else
moveUpAnim.start();
}
});
If you actually need the button view to change parents, you can use AnimatorListener to achieve this at the end of each animation. Something like:
moveDownAnim.addListener(new Animator.AnimatorListener() {
#Override
public void onAnimationEnd(Animator animation) {
((ViewGroup)findViewById(R.id.top)).removeView(button);
((ViewGroup)findViewById(R.id.bottom)).addView(button);
((RelativeLayout)button.getLayoutParams()).addRule(RelativeLayout.CENTER_IN_PARENT);
button.setTranslationY(0.F); // Since it is now positioned in the new layout, no need for translation.
}
#Override
public void onAnimationCancel(Animator animation) { /* NOP */ }
#Override
public void onAnimationRepeat(Animator animation) { /* NOP */ }
#Override
public void onAnimationStart(Animator animation) { /* NOP */ }
});
(And analogous listener for the moveUpAnim.)
However, I doubt you need to actually do this to achieve the visual effect you want. But if you do this part, you will probably also need to set a fixed height for your top view as opposed to wrap_content. (Otherwise, if a layout pass happens while the button has been moved to the bottom view, the top layout's height might go to 0 if there's nothing else in it.) Easiest would be to just do this directly in the xml layout file. However, if you want to "do it on the fly", you can change the layout's height in the onAnimationEnd() method using something like:
#Override
public void onAnimationEnd(Animator animation) {
final ViewGroup topLayout = findViewById(R.id.top);
topLayout.getLayoutParams().height = topLayout.getHeight(); // Keep it the same height...
topLayout.removeView(button);
((ViewGroup)findViewById(R.id.bottom)).addView(button);
((RelativeLayout)button.getLayoutParams()).addRule(RelativeLayout.CENTER_IN_PARENT);
button.setTranslationY(0.F); // Since it is now positioned in the new layout, no need for translation.
}
I really have a serious problem with animating three list views, the flow of animation is as shown in the following images
here is my layout file
<ListView android:id="#+id/categoriesList"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layerType="hardware">
</ListView>
<ListView android:id="#+id/subCategoriesList"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layerType="hardware">
</ListView>
<ListView android:id="#+id/productsList"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layerType="hardware">
</ListView>
and here is my code
for list1 on item click listener
long time = AnimationUtils.currentAnimationTimeMillis();
collapseSize = (int)(categoriesListView.getMeasuredWidth() / 4);
ObjectAnimator animator = new ObjectAnimator();
animator.setTarget(subCategoriesListView);
animator.setPropertyName("translationX");
animator.setFloatValues(3*collapseSize,0);
animator.setStartDelay(time);
animator.setDuration(1000);
animator.addUpdateListener(ShopFragment.this);
ValueAnimator.ofObject(new WidthEvaluator(categoriesListView), categoriesListView.getWidth(),collapseSize).setDuration(1000).start();
animator.start();
for list2 onItemClick Listener
ObjectAnimator animator = new ObjectAnimator();
animator.setTarget(productsListView);
animator.setPropertyName("translationX");
animator.setFloatValues(collapseSize,0);
animator.setStartDelay(time);
animator.setDuration(1000);
ValueAnimator.ofObject(new WidthEvaluator(categoriesListView),
categoriesListView.getWidth(),0).setDuration(1000).start();
ValueAnimator.ofObject(new WidthEvaluator(subCategoriesListView), subCategoriesListView.getWidth(),collapseSize).setDuration(1000).start();
animator.start();
and here is the Width Evaluator
private class WidthEvaluator extends IntEvaluator {
private View v;
public WidthEvaluator(View v) {
this.v = v;
}
#Override
public Integer evaluate(float fraction, Integer startValue,
Integer endValue) {
int num = (Integer)super.evaluate(fraction, startValue, endValue);
ViewGroup.LayoutParams params = v.getLayoutParams();
params.width = num;
v.setLayoutParams(params);
return num;
}
}
User presses the back button to reverse the animation. The main issue is the animation is not smooth at all, it just jumps to the new position. Can anyone help me solve this?
Note: Those list views are inside a fragment, not an activity, if that matters. Also, I am using nineold library for backward compatibility.
I'm not quite sure what you're trying to achieve, but I think it can be done much more simply with layout animations. I suggest something like the following layout:
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:animateLayoutChanges="true"
android:orientation="horizontal">
<ListView android:layout_width="100dp" ... />
<ListView android:layout_width="300dp" ... />
<ListView android:layout_width="300dp" ... />
</LinearLayout>
So this is a horizontal LinearLayout that contains all your ListViews. Notice the animateLayoutChanges setting in the LinearLayout. This means if you resize or add/remove view in the layout it'll animate that change for you. Now you can set the width of the first ListView, or set it to Visibility.GONE and it should animate that change for you. You can do similar things to the other ListViews as needed.
I have a view layout like this:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="fill_parent"
android:layout_height="fill_parent" android:background="#color/light_gray"
android:padding="5dip">
<View android:id="#+id/fixedSpace" android:layout_width="fill_parent"
android:layout_height="50dip" android:background="#color/aqua"
android:layout_alignParentBottom="true" android:clickable="true"
android:onClick="onClickStartAnimation" />
<View android:id="#+id/dynamicSpace" android:layout_width="fill_parent"
android:layout_height="200dip" android:background="#color/lime"
android:layout_above="#id/fixedSpace" />
<View android:id="#+id/remainingSpace" android:layout_width="fill_parent"
android:layout_height="fill_parent" android:background="#color/pink"
android:layout_alignParentTop="true" android:layout_above="#id/dynamicSpace" />
</RelativeLayout>
What I want to achieve is basically a grow/shrink behavior of dynamicSpace over the time t. With animations I can produce the following:
t=1:
t=2:
t=3:
However, that doesn't really resize my views, in particular dynamicSpace and remainingSpace. It just animates the view dynamicSpace moving in. But the view "container" already has the space occupied right from the beginning.
Correct would be that the lime colored dynamicSpace starts with 0px and the pink colored remainingSpace takes over, so there is no gray space in between.
Scale the View
Since you say you are doing it over time t, it sounds like a LinearInterpolator is best.
EDIT:
I tried replacing the below with an AsyncTask thread and it is far smoother. I think the key is I keep the thread running in the background and just use it when I want to resize something, thus reducing overhead
Create a custom AnimationListener and put the code for resizing the view in the onAnimationRepeat method.
Then do a dummy animation and set repeat on the animation to infinite. Once the view has reached the final size, set repeat count on the animation to zero (again in onAnimationRepeat):
class ResizeAnimationListener implements AnimationListener{
int finalHeight; // max Height
int resizeAmount; // amount to resize each time
View view; // view to resize
public ResizeAnimationListener(int finalHeight; View view, int resizeAmount) {
super();
finalHeight; = finalHeight;
this.resizeAmount = resizeAmount;
this.view = view;
}
#Override
public void onAnimationEnd(Animation animation) {
}
#Override
public void onAnimationRepeat(Animation animation) {
int newHeight;
int currentHeight;
current = view.getMeasuredHeight();
newHeight= currentHeight+ resizeAmount;
if(newHeight> finalHeight){
// check if reached final height
// set new height to the final height
newHeight = finalHeight;
// set repeat count to zero so we don't have any more repeats
anim.setRepeatCount(0);
}
// set new height
LayoutParams params = view.getLayoutParams();
params.height = newHeight;
v.setLayoutParams(params);
}
#Override
public void onAnimationStart(Animation animation) {
}
};
class DummyAnimation extends Animation{}
float frameRate = 1000/30;
DummyAnimation anim = new DummyAnimation();
anim.setDuration((long)frameRate);
anim.setRepeatCount(Animation.INFINITE);
ResizeAnimationListener animListener = new ResizeAnimationListener(((View)view.getParent()).getHeight(), view, 25);
anim.setAnimationListener(animListener);
view.startAnimation(anim);
I made this work on my own app . However, views anchored to the view I'm resizing (and thus moving on screen when I resize my view) seem to glitch out. Probably related to repeated resizing rather than anything else, but just a warning. Maybe someone else knows why?