How to animate RecyclerView items when they appear - android

How can I animate the RecyclerView Items when there are appearing?
The default item animator only animates when a data is added or removed after the recycler data has been set.
How can this be achieved?

EDIT :
According to the ItemAnimator documentation :
This class defines the animations that take place on items as changes are made to the adapter.
So unless you add your items one by one to your RecyclerView and refresh the view at each iteration, I don't think ItemAnimator is the solution to your need.
Here is how you can animate the RecyclerView items when they appear using a CustomAdapter :
public class CustomAdapter extends RecyclerView.Adapter<CustomAdapter.ViewHolder>
{
private Context context;
// The items to display in your RecyclerView
private ArrayList<String> items;
// Allows to remember the last item shown on screen
private int lastPosition = -1;
public static class ViewHolder extends RecyclerView.ViewHolder
{
TextView text;
// You need to retrieve the container (ie the root ViewGroup from your custom_item_layout)
// It's the view that will be animated
FrameLayout container;
public ViewHolder(View itemView)
{
super(itemView);
container = (FrameLayout) itemView.findViewById(R.id.item_layout_container);
text = (TextView) itemView.findViewById(R.id.item_layout_text);
}
}
public CustomAdapter(ArrayList<String> items, Context context)
{
this.items = items;
this.context = context;
}
#Override
public CustomAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
{
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.custom_item_layout, parent, false);
return new ViewHolder(v);
}
#Override
public void onBindViewHolder(ViewHolder holder, int position)
{
holder.text.setText(items.get(position));
// Here you apply the animation when the view is bound
setAnimation(holder.itemView, position);
}
/**
* Here is the key method to apply the animation
*/
private void setAnimation(View viewToAnimate, int position)
{
// If the bound view wasn't previously displayed on screen, it's animated
if (position > lastPosition)
{
Animation animation = AnimationUtils.loadAnimation(context, android.R.anim.slide_in_left);
viewToAnimate.startAnimation(animation);
lastPosition = position;
}
}
}
And your custom_item_layout would look like this :
<FrameLayout
android:id="#+id/item_layout_container"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/item_layout_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceListItemSmall"
android:gravity="center_vertical"
android:minHeight="?android:attr/listPreferredItemHeightSmall"/>
</FrameLayout>
For more information about CustomAdapters and RecyclerView, refer to this training on the official documentation.
Problems on fast scroll
Using this method could cause problems with fast scrolling. The view could be reused while the animation is been happening. In order to avoid that is recommendable to clear the animation when is detached.
#Override
public void onViewDetachedFromWindow(final RecyclerView.ViewHolder holder)
{
((CustomViewHolder)holder).clearAnimation();
}
On CustomViewHolder:
public void clearAnimation()
{
mRootLayout.clearAnimation();
}
Old answer :
Give a look at Gabriele Mariotti's repo, I'm pretty sure you'll find what you need. He provides simple ItemAnimators for the RecyclerView, such as SlideInItemAnimator or SlideScaleItemAnimator.

Made Simple with XML only
Visit Gist Link
res/anim/layout_animation.xml
<?xml version="1.0" encoding="utf-8"?>
<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
android:animation="#anim/item_animation_fall_down"
android:animationOrder="normal"
android:delay="15%" />
res/anim/item_animation_fall_down.xml
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="500">
<translate
android:fromYDelta="-20%"
android:toYDelta="0"
android:interpolator="#android:anim/decelerate_interpolator"
/>
<alpha
android:fromAlpha="0"
android:toAlpha="1"
android:interpolator="#android:anim/decelerate_interpolator"
/>
<scale
android:fromXScale="105%"
android:fromYScale="105%"
android:toXScale="100%"
android:toYScale="100%"
android:pivotX="50%"
android:pivotY="50%"
android:interpolator="#android:anim/decelerate_interpolator"
/>
</set>
Use in layouts and recylcerview like:
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layoutAnimation="#anim/layout_animation"
app:layout_behavior="#string/appbar_scrolling_view_behavior" />

I animated fading in of Recyclerview items when they first appear as shown in the code below. Perhaps this will be of use to someone.
private final static int FADE_DURATION = 1000; //FADE_DURATION in milliseconds
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.getTextView().setText("some text");
// Set the view to fade in
setFadeAnimation(holder.itemView);
}
private void setFadeAnimation(View view) {
AlphaAnimation anim = new AlphaAnimation(0.0f, 1.0f);
anim.setDuration(FADE_DURATION);
view.startAnimation(anim);
}
You can also replace setFadeAnimation() with the following setScaleAnimation() to animate appearance of items by scaling them from a point:
private void setScaleAnimation(View view) {
ScaleAnimation anim = new ScaleAnimation(0.0f, 1.0f, 0.0f, 1.0f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
anim.setDuration(FADE_DURATION);
view.startAnimation(anim);
}
The code above has some warts in so far as when you scroll the RecyclerView items always fade or scale. If you wish you can add code to just allow the animation to happen when the fragment or activity containing the RecyclerView is first created (e.g. get the system time on creation and only allow animation for the first FADE_DURATION milliseconds).

I created animation from pbm's answer with little modification to make the aninmation run only once
in the other word the Animation appear with you scroll down only
private int lastPosition = -1;
private void setAnimation(View viewToAnimate, int position) {
// If the bound view wasn't previously displayed on screen, it's animated
if (position > lastPosition) {
ScaleAnimation anim = new ScaleAnimation(0.0f, 1.0f, 0.0f, 1.0f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
anim.setDuration(new Random().nextInt(501));//to make duration random number between [0,501)
viewToAnimate.startAnimation(anim);
lastPosition = position;
}
}
and in onBindViewHolder call the function
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.getTextView().setText("some text");
// call Animation function
setAnimation(holder.itemView, position);
}

You can add a android:layoutAnimation="#anim/rv_item_animation" attribute to RecyclerView like this:
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layoutAnimation="#anim/layout_animation_fall_down"
/>
thanks for the excellent article here:
https://proandroiddev.com/enter-animation-using-recyclerview-and-layoutanimation-part-1-list-75a874a5d213

A good place to start is this:
https://github.com/wasabeef/recyclerview-animators/blob/master/animators/src/main/java/jp/wasabeef/recyclerview/adapters/AnimationAdapter.java
You don't even need the full library, that class is enough.
Then if you just implement your Adapter class giving an animator like this:
#Override
protected Animator[] getAnimators(View view) {
return new Animator[]{
ObjectAnimator.ofFloat(view, "translationY", view.getMeasuredHeight(), 0)
};
}
#Override
public long getItemId(final int position) {
return getWrappedAdapter().getItemId(position);
}
you'll see items appearing from the bottom as they scroll, also avoiding the problem with the fast scroll.

Animating items in the recyclerview when they are binded in the adapter might not be the best idea as that can cause the items in the recyclerview to animate at different speeds. In my case, the item at the end of the recyclerview animate to their position quicker then the ones at the top as the ones at the top have further to travel so it made it look untidy.
The original code that I used to animate each item into the recyclerview can be found here:
http://frogermcs.github.io/Instagram-with-Material-Design-concept-is-getting-real/
But I will copy and paste the code in case the link breaks.
STEP 1: Set this inside your onCreate method so that you ensure the animation only run once:
if (savedInstanceState == null) {
pendingIntroAnimation = true;
}
STEP 2: You will need to put this code into the method where you want to start the animation:
if (pendingIntroAnimation) {
pendingIntroAnimation = false;
startIntroAnimation();
}
In the link, the writer is animating the toolbar icons, so he put it inside this method:
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main, menu);
inboxMenuItem = menu.findItem(R.id.action_inbox);
inboxMenuItem.setActionView(R.layout.menu_item_view);
if (pendingIntroAnimation) {
pendingIntroAnimation = false;
startIntroAnimation();
}
return true;
}
STEP 3: Now write the logic for the startIntroAnimation():
private static final int ANIM_DURATION_TOOLBAR = 300;
private void startIntroAnimation() {
btnCreate.setTranslationY(2 * getResources().getDimensionPixelOffset(R.dimen.btn_fab_size));
int actionbarSize = Utils.dpToPx(56);
toolbar.setTranslationY(-actionbarSize);
ivLogo.setTranslationY(-actionbarSize);
inboxMenuItem.getActionView().setTranslationY(-actionbarSize);
toolbar.animate()
.translationY(0)
.setDuration(ANIM_DURATION_TOOLBAR)
.setStartDelay(300);
ivLogo.animate()
.translationY(0)
.setDuration(ANIM_DURATION_TOOLBAR)
.setStartDelay(400);
inboxMenuItem.getActionView().animate()
.translationY(0)
.setDuration(ANIM_DURATION_TOOLBAR)
.setStartDelay(500)
.setListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
startContentAnimation();
}
})
.start();
}
My preferred alternative:
I would rather animate the whole recyclerview instead of the items inside the recyclerview.
STEP 1 and 2 remains the same.
In STEP 3, as soon as your API call returns with your data, I would start the animation.
private void startIntroAnimation() {
recyclerview.setTranslationY(latestPostRecyclerview.getHeight());
recyclerview.setAlpha(0f);
recyclerview.animate()
.translationY(0)
.setDuration(400)
.alpha(1f)
.setInterpolator(new AccelerateDecelerateInterpolator())
.start();
}
This would animate your entire recyclerview so that it flys in from the bottom of the screen.

Create this method into your recyclerview Adapter
private void setZoomInAnimation(View view) {
Animation zoomIn = AnimationUtils.loadAnimation(context, R.anim.zoomin);// animation file
view.startAnimation(zoomIn);
}
And finally add this line of code in onBindViewHolder
setZoomInAnimation(holder.itemView);

In 2019,
I would suggest putting all the item animations into the ItemAnimator.
Let's start with declaring the animator in the recycler-view:
with(view.recycler_view) {
adapter = Adapter()
itemAnimator = CustomAnimator()
}
Declare the custom animator then,
class CustomAnimator() : DefaultItemAnimator() {
override fun animateAppearance(
holder: RecyclerView.ViewHolder,
preInfo: ItemHolderInfo?,
postInfo: ItemHolderInfo): Boolean{} // declare what happens when a item appears on the recycler view
override fun animatePersistence(
holder: RecyclerView.ViewHolder,
preInfo: ItemHolderInfo,
postInfo: ItemHolderInfo): Boolean {} // declare animation for items that persist in a recycler view even when the items change
}
Similar to the ones above there is one for disappearance animateDisappearance, for add animateAdd, for change animateChange and move animateMove.
One important point would be to call the correct animation-dispatchers inside them.

Just extends your Adapter like below
public class RankingAdapter extends AnimatedRecyclerView<RankingAdapter.ViewHolder>
And add super method to onBindViewHolder
#Override
public void onBindViewHolder(ViewHolder holder, final int position) {
super.onBindViewHolder(holder, position);
It's automate way to create animated adapter like "Basheer AL-MOMANI"
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.ScaleAnimation;
import java.util.Random;
/**
* Created by eliaszkubala on 24.02.2017.
*/
public class AnimatedRecyclerView<T extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<T> {
#Override
public T onCreateViewHolder(ViewGroup parent, int viewType) {
return null;
}
#Override
public void onBindViewHolder(T holder, int position) {
setAnimation(holder.itemView, position);
}
#Override
public int getItemCount() {
return 0;
}
protected int mLastPosition = -1;
protected void setAnimation(View viewToAnimate, int position) {
if (position > mLastPosition) {
ScaleAnimation anim = new ScaleAnimation(0.0f, 1.0f, 0.0f, 1.0f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
anim.setDuration(new Random().nextInt(501));//to make duration random number between [0,501)
viewToAnimate.startAnimation(anim);
mLastPosition = position;
}
}
}

I think, is better to use it like this: (in RecyclerView adapter override just a one method)
override fun onViewAttachedToWindow(holder: ViewHolder) {
super.onViewAttachedToWindow(holder)
setBindAnimation(holder)
}
If You want every attach animation there in RV.

Related

Animate layout change of bottom sheet

In my application I use a bottom sheet (from the support library) which works great. Now I would like to animate a layout change while the sheet is dragged up. For this I have created a subclass of BottomSheetCallback (this is normaly an inner class of a Fragment so not all objects used in this calss are initialized here):
public class MyBehavior extends BottomSheetBehavior.BottomSheetCallback {
Transition transition;
float lastOffset = 0;
Scene scene;
public PlayerBehavior() {
TransitionInflater inflater = TransitionInflater.from(getContext());
transition = inflater.inflateTransition(R.transition.player);
//transition.setDuration(300);
scene = fullLayout;
transition.setInterpolator(new Interpolator() {
#Override
public float getInterpolation(float v) {
return lastOffset;
}
});
}
#Override
public void onStateChanged(#NonNull View bottomSheet, int newState) {
if(newState == BottomSheetBehavior.STATE_DRAGGING) {
TransitionManager.go(scene, transition);
}
}
#Override
public void onSlide(View bottomSheet, final float slideOffset) {
scene = (slideOffset > lastOffset) ? smallLayout : fullLayout;
lastOffset = slideOffset;
}
}
As you can see I also created two Scene from different layout files and a custom Transition to animate between the scenes with the TransitionManager. My problem is that the Transition should be based on the slideOffset parameter (in range of 0-1) but the TransitionManager uses the Animation class in the background which is normally time based in Android.
I tried to create the custom Intapolator but this does not work properly. So how can I create a Transition which is based on an external variable and not on time?
Based on your description, I think you are trying to achieve something like google maps bottom sheet behaviour. The layout changes as the bottomsheet is dragged up.
If that is what you are trying to achieve then you don't need to enforce custom animations, as the bottomsheetdialog itself has those animation behaviour when incorporated inside a parent Coordinator Layout.
Here is a sample code of how I'm implementing the same behaviour. It also makes the FloatingActionButton invisible when the bottomsheet is dragged up to full screen size :
Create a bottomsheetdialog that you want to use inside your main layout
public class CustomBottomDialog extends BottomSheetDialogFragment {
String mSomeName;
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// if some arguments are passed from the calling activity
mSomeName = getArguments().getString("some_name");
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View bottomSheet = inflater.inflate(R.layout.bottomsheet_layout, container, false);
// initialise your bottomsheet_layout items here
TextView tvName = bottomSheet.findViewById(R.id.display_name);
tvName.setText(mSomeName);
tvName.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// do something here
((MainActivity)getActivity()).doSomething();
}
});
return bottomSheet;
}
}
bottomsheet_layout:
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.FloatingActionButton
android:id="#+id/nav"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/navigation_tilt_grey"
app:backgroundTint="#color/colorAccent"
app:elevation="3dp"
app:fabSize="normal"
android:layout_marginEnd="#dimen/activity_horizontal_margin"
app:layout_anchor="#+id/live_dash"
app:layout_anchorGravity="top|right" />
<!--BottomSheet-->
<android.support.v4.widget.NestedScrollView
android:id="#+id/live_dash"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#F3F3F3"
android:clipToPadding="true"
app:layout_behavior="android.support.design.widget.BottomSheetBe
havior"
tools:layout_editor_absoluteY="150dp">
<!--Include your items here, the height of all items combined
will take the main screen layout size with animation-->
</android.support.v4.widget.NestedScrollView>
</android.support.design.widget.CoordinatorLayout>
Calling this BottomSheet from your activity:
public void notifyBottomSheet(String somename){
BottomSheetDialogFragment customDialogFragment = new CustomBottomDialog();
Bundle args = new Bundle();
args.putString("some_name", somename);
customDialogFragment.setArguments(args);
customDialogFragment.show(getSupportFragmentManager(), customDialogFragment.getTag());
customDialogFragment.setCancelable(false); // if you don't wish to hide
}
Hope this solves what you are trying to achieve.
To easily slide something off the bottom of the screen, you can use code such as:
final int activityHeight = findViewById(android.R.id.content).getHeight();
cardContainer.animate().yBy(activityHeight - cardContainer.getY()).setDuration(SLIDE_OUT_DURATION);
where cardContainer is the view you are trying to slide off the screen.
See this blog post for the complete example. Note that you can also use translationY instead of yBy. Another, more generic way of doing it is with this code:
public static ViewPropertyAnimator slideOutToBottom(Context ctx, View view) {
final int screenHeight = ctx.getResources().getDisplayMetrics().heightPixels;
int[] coords = new int[2];
view.getLocationOnScreen(coords);
return view.animate().translationY(screenHeight - coords[Y_INDEX]).setDuration(SLIDE_OUT_DURATION);
}
public static ViewPropertyAnimator slideInFromBottom(Context ctx, View view) {
final int screenHeight = ctx.getResources().getDisplayMetrics().heightPixels;
int[] coords = new int[2];
view.getLocationOnScreen(coords);
view.setTranslationY(screenHeight - coords[Y_INDEX]);
return view.animate().translationY(0).setDuration(SLIDE_IN_DURATION).setInterpolator(new OvershootInterpolator(1f));
}
## Translation Animation ##
<?xml version="1.0" encoding="utf-8"?>
<set
xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="#android:anim/accelerate_decelerate_interpolator"
android:fillAfter="true"
>
<translate
android:fromYDelta="100%p"
android:toYDelta="-30%p"
android:duration="900" />
</set>
## Main Activity ##
#Override
protected void onResume() {
super.onResume();
Animation am= AnimationUtils.loadAnimation(this,R.anim.fadeout);
tv5.startAnimation(am);
Animation myanim= AnimationUtils.loadAnimation(this,R.anim.translate);
tv1.startAnimation(myanim);
myanim.setStartOffset(500);
Animation animation= AnimationUtils.loadAnimation(this,R.anim.translate);
animation.setStartOffset(1000);
tv2.startAnimation(animation);
Animation an= AnimationUtils.loadAnimation(this,R.anim.translate);
an.setStartOffset(1500);
tv3.startAnimation(an);
Animation ab= AnimationUtils.loadAnimation(this,R.anim.translate);
ab.setStartOffset(2000);
tv4.startAnimation(ab);
Animation ac= AnimationUtils.loadAnimation(this,R.anim.fadein);
ac.setStartOffset(2500);
btn1.startAnimation(ac);
}
I'm not sure if that is what you want but maybe instead of using transition, you can use the function animate() since with this function, you can change all things about your animation (time, visibility etc.).

Similar Facebook reactions - Animated view with ObjectAnimator clicklistener not working

I'm trying to animate a set of views vertically in an RecyclerView.Adapter, the animation works well using android:clipChildren="false", android:clipToPadding="false" and viewHolder.linear.postInvalidate(), but the ClickListener does not work after animation ends. I'm using ObjectAnimator because I read this link Android Animation - Button stays clickable
Some code
#Override
public void onBindViewHolder(final TViewHolder viewHolder, int i) {
viewHolder.flGroupButtons.postInvalidate();
viewHolder.iv1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//not working
});
..... clicklisteners
viewHolder.iv4.setOnClickListener(new View.OnClickListener() {
PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat("scaleX", 1f);
PropertyValuesHolder scaleY = PropertyValuesHolder.ofFloat("scaleY", 1f);
PropertyValuesHolder pvTranslativ1 = PropertyValuesHolder.ofFloat("translationY", viewHolder.iv1.getY() - (measureHeight * 5));
ObjectAnimator animatoriv1 = ObjectAnimator.ofPropertyValuesHolder(viewHolder.iv1, scaleX, scaleY, pvTranslativ1);
animatoriv1.setInterpolator(new DecelerateInterpolator());
animatoriv1.setDuration(300);
....
......
AnimatorSet as = new AnimatorSet();
as.playTogether(animatoriv1, animatoriv2, animatoriv3, animatoriv4);
as.start();
});
}
Only iv1 clicklistener is working when is not collapsed.
How can I get it work?
You can't do this, because this is not the way Android touch event process works.
ViewGroup or it's subclass(FrameLayout, RelativeLayout, LinearLayout, etc) dispatch touch event to it's childviews based on childview's frame.so when you click on iv2 or iv3 or iv4, the touch event didn't even passed to the parent(RecyclerView's item),so the parent view never had a change to handle or dispatch the touch event to iv2 or iv3 or iv4.
what you should do is pop a new Window with iv1-iv4 in it using WindowManager, and carefully calculate the position so that it will look just like the way you did to the RecyclerView's item.only then you can receive the touchevent and onClick event
Facebook Reactions is definitely using WindowManager to pop a new Window with buttons:
here's what it looks like when I turn on the Profile GPU rendering option in Developer options
notice there are two rows of bars on screen now when I long click the like button,the smaller one is a new window which contains reactions buttons
The clicklistener is just in the wrong position, it should be in the viewholder
// Adapter
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int type) {
return NewsViewHolder.newInstance(parent);
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
final NewsEntry newsEntity = news.get(position);
((NewsViewHolder) holder).bind(newsEntity);
}
// ViewHolder
public NewsViewHolder(NewsViewHolderBinding binding) {
super(binding.getRoot());
itemView.findViewById(R.id.viewname).setOnClickListener(this);
}

Unknown animation name: decelerateInterpolator

Android Studio 1.5
Device Samsung 4.4.2
I am trying to animate items loaded from a ArrayList into a recyclerview. I when the dropdown arrow is clicked the items should animate (decelerate) when expanded and should animate when collapsed. However, currently the list items just appears.
Code that calls the setAnimation
#Override
public void onBindChildViewHolder(ChatChildViewHolder childViewHolder, int position, Object childListItem) {
ChatChildTitles chatChildTitles = (ChatChildTitles)childListItem;
childViewHolder.tvChildTitle.setText(chatChildTitles.getTitle());
setAnimation(childViewHolder.cvChildRooms, position);
}
Code for setting the animation
private void setAnimation(CardView viewToAnimate, int position) {
Animation animation = AnimationUtils.loadAnimation(mContext, android.R.anim.fade_in);
animation.setInterpolator(mContext, android.R.anim.decelerate_interpolator);
viewToAnimate.startAnimation(animation);
}
Here is a couple of screenshots:
In the collapsed state
After the arrow has been clicked expland the list
This is my layout I am using that represents the rows that will be displayed in the recyclerView:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
android:id="#+id/cvChildRooms"
xmlns:card="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
card:cardBackgroundColor="#color/child_header_lighter_grey"
card:contentPadding="4dp"
card:cardPreventCornerOverlap="true">
<de.hdodenhof.circleimageview.CircleImageView
android:id="#+id/profile_image"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_gravity="center_vertical|start"
android:src="#drawable/photorace"/>
<TextView
android:id="#+id/tvChildTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|center"
android:text="Coffee Latte Room"
android:fontFamily="sans-serif-light"
android:textSize="16sp"
android:textColor="#android:color/black"/>
</android.support.v7.widget.CardView>
I have a function that should start the animation.
private void setAnimation(CardView viewToAnimate, int position) {
Animation animation = AnimationUtils.loadAnimation(mContext, android.R.anim.decelerate_interpolator);
viewToAnimate.startAnimation(animation);
}
I have tested using the following that works ok with slide_in_left. However, I don't want them to slide in from the left
Animation animation = AnimationUtils.loadAnimation(mContext, android.R.anim.slide_in_left);
viewToAnimate.startAnimation(animation);
Many thanks for any suggestions,
If you want to use a decelerate interpolator you need to set it AS an interpolator, not as the animator:
private void setAnimation(CardView viewToAnimate, int position) {
Animation animation = AnimationUtils.loadAnimation(mContext, android.R.anim.fade_in); //change this with your desidered (or custom) animation
animation.setInterpolator(mContext, android.R.anim.decelerate_interpolator);
viewToAnimate.startAnimation(animation);
}
UPDATE
You said that you are using com.bignerdranch.android:expandablerecyclerview:2.0.3.
From the official docs of the library, it's clearly state how to create expand/collapse animations:
You can also create your own animations for expansion by overriding
ParentViewHolder#onExpansionToggled(boolean), which will be called for
you when the itemView is expanded or collapsed.
I suggest you to take a look at the official example of the library.
You can't use decelerate_interpolator because it's not an animation, it is an interpolator:
An interpolator defines the rate of change of an animation. This
allows the basic animation effects (alpha, scale, translate, rotate)
to be accelerated, decelerated, repeated, etc.
Reference:
http://developer.android.com/reference/android/view/animation/Interpolator.html
As you can see the XML that describing them are completely different:
Source of decelerate_interpolator.xml:
<decelerateInterpolator />
Source of slide_in_left.xml:
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate android:fromXDelta="-50%p" android:toXDelta="0"
android:duration="#android:integer/config_mediumAnimTime"/>
<alpha android:fromAlpha="0.0" android:toAlpha="1.0"
android:duration="#android:integer/config_mediumAnimTime" />
</set>
To animate the list as they expand and collapse, consider using an ItemAnimator from android.
You will need to set up a custom itemAnimator, something similar to the one in the link below:
https://gist.github.com/ademar111190/dc988c8d899dae0193f7
Set the itemAnimator in the method runPendingAnimations to your decelerate interpolator.
#Override
public void runPendingAnimations() {
if (!mViewHolders.isEmpty()) {
int animationDuration = 300;
AnimatorSet animator;
View target;
for (final RecyclerView.ViewHolder viewHolder : mViewHolders) {
target = viewHolder.itemView;
target.setPivotX(target.getMeasuredWidth() / 2);
target.setPivotY(target.getMeasuredHeight() / 2);
animator = new AnimatorSet();
animator.playTogether(
ObjectAnimator.ofFloat(target, "translationX", -target.getMeasuredWidth(), 0.0f),
ObjectAnimator.ofFloat(target, "alpha", target.getAlpha(), 1.0f)
);
animator.setTarget(target);
animator.setDuration(animationDuration);
animator.setInterpolator(new DecelerateInterpolator());
animator.setStartDelay((animationDuration * viewHolder.getPosition()) / 10);
animator.addListener(new AnimatorListener() {
#Override
public void onAnimationEnd(Animator animation) {
mViewHolders.remove(viewHolder);
}
});
animator.start();
}
}
}
Then you will need to set the itemAnimator to the recycler view.
recyclerView.setItemAnimator(new MyItemAnimator());
You can use below code for do that.
private void hideViews() {
recyclerView.animate().translationY(-recyclerView.getHeight()).setInterpolator(new AccelerateInterpolator(2));
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mFabButton.getLayoutParams();
int fabBottomMargin = lp.bottomMargin;
mFabButton.animate().translationY(mFabButton.getHeight()+fabBottomMargin).setInterpolator(new AccelerateInterpolator(2)).start();
}
private void showViews() {
recyclerView.animate().translationY(0).setInterpolator(new DecelerateInterpolator(2));
mFabButton.animate().translationY(0).setInterpolator(new DecelerateInterpolator(2)).start();
}
You can call this method onClick of your Button.
Hope it will help you.

How to zoom in/out item(only textview) of Vertical ScrollView in android when scroll Vertical

I want to create below layout under which item will scroll vertically. whenever we scroll up or down the centered(28) textview should be zoom in position.i found this link Gallery like view with center image zoom but they scroll horizontally using image based on their requirement. i need vertically scroll only using textview
please let me know if any body know the logic.......
First of all, thanks to #sanjay kumar had a good question.
After 2 days, I found a best solution for this question.
You can use RecyclerView and make sure the LinearLayoutManager should like this
LinearLayoutManager yourLinearLayout= new LinearLayoutManager(getApplicationContext());
yourLinearLayout.setOrientation(LinearLayoutManager.VERTICAL);
yourLinearLayout.setLayoutManager(yearLayoutManager);
The most difficult problem is how can you get the middle Item of recyclerView, well I think you can learn more in this link:
https://github.com/plattysoft/SnappingList
Finally, to make the middle Item bigger than the others. In the RecyclerView's Adapter should be like this
public class DateAdapter extends RecyclerView.Adapter<DateAdapter.DateViewHolder> {
private ArrayList<LabelerDate> dateDataList;
private static final int VIEW_TYPE_PADDING = 1;
private static final int VIEW_TYPE_ITEM = 2;
private int selectedItem = -1;
public DateAdapter(ArrayList<Datasource> data, int paddingWidthDate) {
this.dateDataList = data;
this.paddingWidthDate = paddingWidthDate;
}
#Override
public DateViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
//todo
}
#Override
public void onBindViewHolder(DateViewHolder holder, int position) {
Datasource data = dateDataList.get(position);
if (getItemViewType(position) == VIEW_TYPE_ITEM) {
holder.tvDate.setText(String.valueOf(data.valueDate));
holder.tvDate.setVisibility(View.VISIBLE);
holder.imgSmall.setVisibility(View.VISIBLE);
if (position == selectedItem) {
holder.tvDate.setTextColor(Color.parseColor("#094673"));
holder.tvDate.setTextSize(35);
holder.imgSmall.setBackgroundResource(R.color.textviewbold);
} else {
holder.tvDate.setTextColor(Color.GRAY);
holder.tvDate.setTextSize(35);
holder.imgSmall.setBackgroundResource(R.color.gray);
}
}
}
public void setSelecteditem(int selecteditem) {
this.selectedItem = selecteditem;
notifyDataSetChanged();
}
#Override
public int getItemViewType(int position) {
//todo
}
public class DateViewHolder extends RecyclerView.ViewHolder {
//todo
}}
After created The recyclerView, adapter and datasource. You can look at the onBindViewHolder(). If Item is selected then it's become bigger and change to black. If is not selected it will become Gray. My layout look like yours
use this library
Polidea view library
Put your TextView into a LinearLayout that lives on a ZoomView.
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.polidea.ZoomView
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<LinearLayout
android:id="#+id/myLinearLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical" >
</LinearLayout>
</com.polidea.ZoomView>
</ScrollView>
The Polidea custom View didn't work in my case, as is. Had to a add a constructor with AttributeSet parameter, because it threw an xml inflation error. Also I had to make the ScrollView wrap only the TextView.

Scroll RecyclerView to show selected item on top

I'm looking for a way to scroll a RecyclerView to show the selected item on top.
In a ListView I was able to do that by using scrollTo(x,y) and getting the top of the element that need to be centered.
Something like:
#Override
public void onItemClick(View v, int pos){
mylistView.scrollTo(0, v.getTop());
}
The problem is that the RecyclerView returns an error when using it's scrollTo method saying
RecyclerView does not support scrolling to an absolute position
How can I scroll a RecyclerView to put the selected item at the top of the view?
If you are using the LinearLayoutManager or Staggered GridLayoutManager, they each have a scrollToPositionWithOffset method that takes both the position and also the offset of the start of the item from the start of the RecyclerView, which seems like it would accomplish what you need (setting the offset to 0 should align with the top).
For instance:
//Scroll item 2 to 20 pixels from the top
linearLayoutManager.scrollToPositionWithOffset(2, 20);
If you looking for vertical LinearLayout Manager you can achieve smooth scrolling using a custom LinearSmoothScroller:
import android.content.Context;
import android.graphics.PointF;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.LinearSmoothScroller;
import android.support.v7.widget.RecyclerView;
public class SnappingLinearLayoutManager extends LinearLayoutManager {
public SnappingLinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
super(context, orientation, reverseLayout);
}
#Override
public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state,
int position) {
RecyclerView.SmoothScroller smoothScroller = new TopSnappedSmoothScroller(recyclerView.getContext());
smoothScroller.setTargetPosition(position);
startSmoothScroll(smoothScroller);
}
private class TopSnappedSmoothScroller extends LinearSmoothScroller {
public TopSnappedSmoothScroller(Context context) {
super(context);
}
#Override
public PointF computeScrollVectorForPosition(int targetPosition) {
return SnappingLinearLayoutManager.this
.computeScrollVectorForPosition(targetPosition);
}
#Override
protected int getVerticalSnapPreference() {
return SNAP_TO_START;
}
}
}
use an instance of the layoutmanager in recycle view and then calling recyclerView.smoothScrollToPosition(pos); will smooth scroll to selected position to top of the recycler view
//Scroll item pos
linearLayoutManager.scrollToPositionWithOffset(pos, 0);
You just need to call recyclerview.scrollToPosition(position). That's fine!
If you want to call it in adapter, just let your adapter has the instance of recyclerview or the activity or fragment which contains recyclerview,than implements the method getRecyclerview() in them.
I hope it can help you.
If you want to scroll automatic without show scroll motion then you need to write following code:
mRecyclerView.getLayoutManager().scrollToPosition(position);
If you want to display scroll motion then you need to add following code.
=>Step 1: You need to declare SmoothScroller.
RecyclerView.SmoothScroller smoothScroller = new
LinearSmoothScroller(this.getApplicationContext()) {
#Override
protected int getVerticalSnapPreference() {
return LinearSmoothScroller.SNAP_TO_START;
}
};
=>step 2: You need to add this code any event you want to perform scroll to specific position.
=>First you need to set target position to SmoothScroller.
smoothScroller.setTargetPosition(position);
=>Then you need to set SmoothScroller to LayoutManager.
mRecyclerView.getLayoutManager().startSmoothScroll(smoothScroller);
just call this method simply:
((LinearLayoutManager)recyclerView.getLayoutManager()).scrollToPositionWithOffset(yourItemPosition,0);
instead of:
recyclerView.scrollToPosition(yourItemPosition);
same with speed regulator
public class SmoothScrollLinearLayoutManager extends LinearLayoutManager {
private static final float MILLISECONDS_PER_INCH = 110f;
private Context mContext;
public SmoothScrollLinearLayoutManager(Context context,int orientation, boolean reverseLayout) {
super(context,orientation,reverseLayout);
mContext = context;
}
#Override
public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state,
int position) {
RecyclerView.SmoothScroller smoothScroller = new TopSnappedSmoothScroller(recyclerView.getContext()){
//This controls the direction in which smoothScroll looks for your view
#Override
public PointF computeScrollVectorForPosition(int targetPosition) {
return new PointF(0, 1);
}
//This returns the milliseconds it takes to scroll one pixel.
#Override
protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
return MILLISECONDS_PER_INCH / displayMetrics.densityDpi;
}
};
smoothScroller.setTargetPosition(position);
startSmoothScroll(smoothScroller);
}
private class TopSnappedSmoothScroller extends LinearSmoothScroller {
public TopSnappedSmoothScroller(Context context) {
super(context);
}
#Override
public PointF computeScrollVectorForPosition(int targetPosition) {
return SmoothScrollLinearLayoutManager.this
.computeScrollVectorForPosition(targetPosition);
}
#Override
protected int getVerticalSnapPreference() {
return SNAP_TO_START;
}
}
}
Try what worked for me cool!
Create a variable private static int displayedposition = 0;
Now for the position of your RecyclerView in your Activity.
myRecyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
}
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
LinearLayoutManager llm = (LinearLayoutManager) myRecyclerView.getLayoutManager();
displayedposition = llm.findFirstVisibleItemPosition();
}
});
Place this statement where you want it to place the former site displayed in your view .
LinearLayoutManager llm = (LinearLayoutManager) mRecyclerView.getLayoutManager();
llm.scrollToPositionWithOffset(displayedposition , youList.size());
Well that's it , it worked fine for me \o/
what i did to restore the scroll position after refreshing the RecyclerView on button clicked:
if (linearLayoutManager != null) {
index = linearLayoutManager.findFirstVisibleItemPosition();
View v = linearLayoutManager.getChildAt(0);
top = (v == null) ? 0 : (v.getTop() - linearLayoutManager.getPaddingTop());
Log.d("TAG", "visible position " + " " + index);
}
else{
index = 0;
}
linearLayoutManager = new LinearLayoutManager(getApplicationContext());
linearLayoutManager.scrollToPositionWithOffset(index, top);
getting the offset of the first visible item from the top before creating the linearLayoutManager object and after instantiating it the scrollToPositionWithOffset of the LinearLayoutManager object was called.
I don't know why I didn't find the best answer but its really simple.
recyclerView.smoothScrollToPosition(position);
No errors
Creates Animations
What i may add here is how to make it work together with DiffUtil and ListAdapter
You may note that calling recyclerView.scrollToPosition(pos) or (recyclerView.layoutManager as LinearLayoutManager).scrollToPositionWithOffset(pos, offset) wouldn't work if called straight after adapter.submitList. It is because the differ looks for changes in a background thread and then asynchronously notifies adapter about changes. On a SO i have seen several wrong answers with unnecessary delays & etc to solve this.
To handle the situation properly the submitList has a callback which is invoked when changes have been applied.
So the proper kotlin implementations in this case are:
//memorise target item here and a scroll offset if needed
adapter.submitList(items) {
val pos = /* here you may find a new position of the item or just use just a static position. It depends on your case */
recyclerView.scrollToPosition(pos)
}
//or
adapter.submitList(items) { recyclerView.smoothScrollToPosition(pos) }
//or etc
adapter.submitList(items) { (recyclerView.layoutManager as LinearLayoutManager).scrollToPositionWithOffset(pos, offset) }
Introduction
None of the answers explain how to show last item(s) at the top. So, the answers work only for items that still have enough items above or below them to fill the remaining RecyclerView. For instance, if there are 59 elements and a 56-th element is selected it should be at the top as in the picture below:
So, let's see how to implement this in the next paragraph.
Solution
We could handle those cases by using linearLayoutManager.scrollToPositionWithOffset(pos, 0) and additional logic in the Adapter of RecyclerView - by adding a custom margin below the last item (if the last item is not visible then it means there's enough space fill the RecyclerView). The custom margin could be a difference between the root view height and the item height. So, your Adapter for RecyclerView would look as follows:
...
#Override
public void onBindViewHolder(ViewHolder holder, final int position) {
...
int bottomHeight = 0;
int itemHeight = holder.itemView.getMeasuredHeight();
// if it's the last item then add a bottom margin that is enough to bring it to the top
if (position == mDataSet.length - 1) {
bottomHeight = Math.max(0, mRootView.getMeasuredHeight() - itemHeight);
}
RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)holder.itemView.getLayoutParams();
params.setMargins(0, 0, params.rightMargin, bottomHeight);
holder.itemView.setLayoutParams(params);
...
}
...
If your LayoutManager is LinearLayoutManager you can use scrollToPositionWithOffset(position,0); on it and it will make your item the first visible item in the list. Otherwise, you can use smoothScrollToPosition on the RecyclerView directly.
I ended up using the below code.
RecyclerView.LayoutManager layoutManager = mainList.getLayoutManager();
if (layoutManager instanceof LinearLayoutManager) {
// Scroll to item and make it the first visible item of the list.
((LinearLayoutManager) layoutManager).scrollToPositionWithOffset(position, 0);
} else {
mainList.smoothScrollToPosition(position);
}
scroll at particular position
and this helped me alot.
by click listener you can get the position in your adapter
layoutmanager.scrollToPosition(int position);
In my case my RecyclerView have a padding top like this
<android.support.v7.widget.RecyclerView
...
android:paddingTop="100dp"
android:clipToPadding="false"
/>
Then for scroll a item to top, I need to
recyclerViewLinearLayoutManager.scrollToPositionWithOffset(position, -yourRecyclerView.getPaddingTop());
please note that if scrollToPosition not work notice that your RecyclerView was inside a NestedScrollView; refer to this post
This is pretty simple
recyclerView.scrollToPosition(position)
If you've Recycler view inside nestedscrollview :
val y = recyclerview.getChildAt(0).y
recyclerview.smoothScrollTo(0, y.toInt())
If your Recycler view is not inside nestedscrollview :
recyclerview.smoothScrollToPosition(index)
or
recyclerview.layoutManager?.smoothScrollToPosition(recyclerview, null ,index)
I use the code below to smooth-scroll an item (thisView) to the top.
It works also for GridLayoutManager with views of different heights:
View firstView = mRecyclerView.getChildAt(0);
int toY = firstView.getTop();
int firstPosition = mRecyclerView.getChildAdapterPosition(firstView);
View thisView = mRecyclerView.getChildAt(thisPosition - firstPosition);
int fromY = thisView.getTop();
mRecyclerView.smoothScrollBy(0, fromY - toY);
Seems to work good enough for a quick solution.

Categories

Resources