if i expand any item in the recyclerview it expands fine but when i scroll down the recylerview i found other items also expanded , due to recycling it took the expanded size not the original one
public class FeedAdapter extends RecyclerView.Adapter<FeedAdapter.MyViewHolder> implements View.OnClickListener {
private LayoutInflater inflater;
private Context mcontext;
private int mOriginalHeight = 0;
private boolean mIsViewExpanded = false;
public FeedAdapter(Context context) {
inflater = LayoutInflater.from(context);
mcontext = context;
}
#Override
public MyViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
View view = inflater.inflate(R.layout.custom_row, viewGroup, false);
MyViewHolder holder = new MyViewHolder(view);
holder.frame.setOnClickListener(this);
return holder;
}
#Override
public void onBindViewHolder(MyViewHolder myViewHolder, int i) {
}
#Override
public int getItemCount() {
return 100;
}
#Override
public void onClick(final View v) {
if (mOriginalHeight == 0) {
mOriginalHeight = v.getHeight();
}
ValueAnimator valueAnimator;
if (v.getHeight() < (mOriginalHeight + (int) (mOriginalHeight * 1.5))) {
valueAnimator = ValueAnimator.ofInt(mOriginalHeight, mOriginalHeight + (int) (mOriginalHeight * 1.5));
} else {
valueAnimator = ValueAnimator.ofInt(mOriginalHeight + (int) (mOriginalHeight * 1.5), mOriginalHeight);
}
valueAnimator.setDuration(300);
valueAnimator.setInterpolator(new LinearInterpolator());
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
public void onAnimationUpdate(ValueAnimator animation) {
Integer value = (Integer) animation.getAnimatedValue();
v.getLayoutParams().height = value.intValue();
v.requestLayout();
}
});
valueAnimator.start();
}
class MyViewHolder extends RecyclerView.ViewHolder {
FrameLayout frame;
public MyViewHolder(View itemView) {
super(itemView);
frame = (FrameLayout) itemView.findViewById(R.id.base);
}
}
}
so
how do i fix that and also is there a better way to expand and collapse items ?
I ran into the same issue. This happens because of the ViewHolderPattern.
https://developer.android.com/reference/android/support/v7/widget/RecyclerView.ViewHolder.html#isRecyclable%28%29
Android will recycle the view, regardlessly if it's expanded or not.
So tell android that if your view is expanded, it is not recyclable.
In Your ViewHolder Class add:
public MyViewHolder(View itemView) {
super(itemView);
frame = (FrameLayout) itemView.findViewById(R.id.base);
frame.setTag(this); // to get a reference to viewholder later on
}
In Your OnClick Method add:
#Override
public void onClick(final View v) {
MyViewHolder holder = (MyViewHolder) v.getTag();
holder.setIsRecyclable(false);
if (mOriginalHeight == 0) {
mOriginalHeight = v.getHeight();
}
ValueAnimator valueAnimator;
...
}
This should solve Your Problem.
Remember to set "setIsRecyclable()" to true again after the view isn't expanded any more so that android can recycle it.
Hope I could help.
Cheers
Florestan
Related
When expanding and collapsing my Recyclerview items, divider lines drawn multiple times or overdrawn with items.
Also when expanding and collapsing views, dividers thickness is getting reduced.
My problem is divider line drawn each and every time I expand and collapse item in Recyclerview.
So is it possible to prevent divider line drawing if it already drawn?
While expanding an item, divider line will move according to the view.?
Below is my RecyclerView Decoration Class used for divider line,
public class SeparatorDecoration extends RecyclerView.ItemDecoration {
private final Paint mPaint;
/**
* Create a decoration that draws a line in the given color and width between the items in the view.
* #param context a context to access the resources.
* #param color the color of the separator to draw.
* #param heightDp the height of the separator in dp.
*/
public SeparatorDecoration(#NonNull Context context, #ColorInt int color,
#FloatRange(from = 0, fromInclusive = false) float heightDp) {
mPaint = new Paint();
mPaint.setColor(color);
final float thickness = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
heightDp, context.getResources().getDisplayMetrics());
mPaint.setStrokeWidth(thickness);
}
#Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) view.getLayoutParams();
// we want to retrieve the position in the list
final int position = params.getViewAdapterPosition();
// and add a separator to any view but the last one
if (position <state.getItemCount()) {
outRect.set(40, 0, 40, (int) mPaint.getStrokeWidth()); // left, top, right, bottom
} else {
outRect.setEmpty(); // 0, 0, 0, 0
}
}
#Override
public void onDrawOver(#NonNull Canvas c, #NonNull RecyclerView parent, #NonNull RecyclerView.State state) {
final int offset = (int) (mPaint.getStrokeWidth() / 2);
// this will iterate over every visible view
for (int i = 0; i < parent.getChildCount(); i++) {
// get the view
final View view = parent.getChildAt(i);
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) view.getLayoutParams();
// get the position
final int position = params.getViewAdapterPosition();
// and finally draw the separator
if (position < parent.getChildCount()) {
final int ty = (int)(view.getTranslationY() + 0.5f);
final int top = view.getBottom() - params.bottomMargin + ty;
final int bottom = top + (int) mPaint.getStrokeWidth();
c.drawLine(view.getLeft(), view.getBottom() + offset, view.getRight(), view.getBottom() + offset, mPaint);
}
}
}
}
below is my RecyclerView Adapter class,
public class DisplayNotificationAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{
private Context context;
private List<NotificationDetails> notificationRecords;
private DeleteNotificationListener deleteNotificationListener;
private String TAG = DisplayNotificationAdapter.class.getSimpleName();
interface DeleteNotificationListener {
void updateNotificationList(List<NotificationDetails> details);
}
public DisplayNotificationAdapter(Context context, DeleteNotificationListener listener, List < NotificationDetails > notificationRecordsList) {
this.context = context;
this.deleteNotificationListener = listener;
this.notificationRecords = notificationRecordsList;
}
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder (#NonNull ViewGroup parent, int viewType){
LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
AndroidLogger.log(5,TAG,"oncreate");
View listItem = layoutInflater.inflate(R.layout.display_notification_recycler_view_list_item, parent, false);
return new NotificationViewHolder(listItem);
}
#RequiresApi(api = Build.VERSION_CODES.N)
#Override
public void onBindViewHolder (#NonNull RecyclerView.ViewHolder holder,int position){
NotificationDetails notification = notificationRecords.get(position);
NotificationViewHolder viewHolder = (NotificationViewHolder) holder;
String currentDateString = DateFormat.getDateInstance().format(Long.parseLong(notification.getTimeStamp()));
String filePath=generateFilePath(notification.getFileName());
Bitmap myBitmap = BitmapFactory.decodeFile(filePath);
#SuppressLint("SimpleDateFormat")
DateFormat dateFormat = new SimpleDateFormat("hh:mm aa");
String time = dateFormat.format(Long.parseLong(notification.getTimeStamp()));
if (notification.isExpanded()) {
viewHolder.expandCollapseImageView.setImageDrawable(context.getDrawable(ImageDrawable.getDrawable("Up Arrow")));
expandView(viewHolder.notificationImageview);
}
else {
viewHolder.expandCollapseImageView.setImageDrawable(context.getDrawable(ImageDrawable.getDrawable("Down Arrow")));
collapseView(viewHolder.notificationImageview);
}
viewHolder.notificationImageview.setImageBitmap(myBitmap);
viewHolder.notificationTextView.setText(notification.getMessage());
viewHolder.notificationTimeTextView.setText(time);
Calendar now = Calendar.getInstance();
Calendar date = Calendar.getInstance();
date.setTimeInMillis(Long.parseLong(notification.getTimeStamp()));
viewHolder.expandCollapseImageView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(notification.isExpanded()) {
notification.setExpanded(false);
viewHolder.expandCollapseImageView.setImageDrawable(context.getDrawable(ImageDrawable.getDrawable("Down Arrow")));
viewHolder.notificationTextView.setMaxLines(1);
notifyItemChanged(position);
}
else {
notification.setExpanded(true);
viewHolder.expandCollapseImageView.setImageDrawable(context.getDrawable(ImageDrawable.getDrawable("Up Arrow")));
viewHolder.notificationTextView.setMaxLines(Integer.MAX_VALUE);
notifyItemChanged(position);
}
}
});
if (now.get(Calendar.DATE) == date.get(Calendar.DATE))
viewHolder.notificationDateTextView.setText("Today");
else if (now.get(Calendar.DATE) - date.get(Calendar.DATE) == 1)
viewHolder.notificationDateTextView.setText("Yesterday");
else
viewHolder.notificationDateTextView.setText(currentDateString);
if(notification.getTitle()==null)
viewHolder.notificationTitleTextView.setText("title");
else
viewHolder.notificationTitleTextView.setText(notification.getTitle());
}
private String generateFilePath(String fileName) {
File imageFileDirectory = context.getDir("image", Context.MODE_PRIVATE); //Creating an internal dir;
if (!imageFileDirectory.exists()) {
imageFileDirectory.mkdirs();
}
/*
* app server provide "U" file name after we set read status they provide same file name as "R"
*/
String createFilePath = imageFileDirectory + "/" + fileName;
return createFilePath;
}
public void removeSingleNotification ( int position){
DatabaseHelper databaseHelper = new DatabaseHelper(context);
databaseHelper.deleteSingleNotificationRecord(notificationRecords.get(position).getId());
notificationRecords.remove(position);
deleteNotificationListener.updateNotificationList(notificationRecords);
notifyDataSetChanged();
}
private void removeFromList (String id) {
for (NotificationDetails detail : notificationRecords) {
if (detail.getId().equalsIgnoreCase(id))
notificationRecords.remove(detail);
}
}
#Override
public int getItemCount () {
return notificationRecords.size();
}
public void expandView(final View v) {
int matchParentMeasureSpec = View.MeasureSpec.makeMeasureSpec(((View) v.getParent()).getWidth(), View.MeasureSpec.EXACTLY);
int wrapContentMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
v.measure(matchParentMeasureSpec, wrapContentMeasureSpec);
final int targetHeight = v.getMeasuredHeight();
// Older versions of android (pre API 21) cancel animations for views with a height of 0.
v.getLayoutParams().height = 1;
v.setVisibility(View.VISIBLE);
Animation a = new Animation()
{
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
v.getLayoutParams().height = interpolatedTime == 1
? ViewGroup.LayoutParams.WRAP_CONTENT
: (int)(targetHeight * interpolatedTime);
v.requestLayout();
}
#Override
public boolean willChangeBounds() {
return true;
}
};
// Expansion speed of 1dp/ms
a.setDuration((int)(targetHeight / v.getContext().getResources().getDisplayMetrics().density));
v.startAnimation(a);
}
public void collapseView(final View v) {
//collapse(pos);
final int initialHeight = v.getMeasuredHeight();
Animation a = new Animation()
{
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
if(interpolatedTime == 1){
v.setVisibility(View.GONE);
}else{
v.getLayoutParams().height = initialHeight - (int)(initialHeight * interpolatedTime);
v.requestLayout();
}
}
#Override
public boolean willChangeBounds() {
return true;
}
};
// Collapse speed of 1dp/ms
a.setDuration((int)(initialHeight / v.getContext().getResources().getDisplayMetrics().density));
v.startAnimation(a);
}
public static class NotificationViewHolder extends RecyclerView.ViewHolder {
private TextView notificationTextView, notificationDateTextView, notificationTimeTextView, notificationTitleTextView;
private ImageView notificationImageview,expandCollapseImageView;
private ConstraintLayout parent;
public NotificationViewHolder(#NonNull View itemView) {
super(itemView);
notificationTextView = itemView.findViewById(R.id.notification_text_view);
notificationDateTextView = itemView.findViewById(R.id.notification_date_text_view);
notificationTimeTextView = itemView.findViewById(R.id.notification_time_text_view);
notificationTitleTextView = itemView.findViewById(R.id.notification_title_text_view);
notificationImageview = itemView.findViewById(R.id.notification_image_view);
expandCollapseImageView = itemView.findViewById(R.id.expand_collapse_arrow);
parent = itemView.findViewById(R.id.notification_parent);
}
}
}
UPDATE
I doesn't able to solve this issue. So instead of using RecyclerView.ItemDecoration I have used a View inside layout like below,
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:id="#+id/view_div"
android:background="#color/grey"
Doing like above solves the issue.
[This is my screen contains the issue][1]][1]
https://i.stack.imgur.com/CBY80.jpg
I want to change the height of the view and decrease height all the bottom view. This works well in the fragment, but does not work in the adapter.
I do not want to squeeze the main view, I need to increase only the size of containerAvatar.
public class OrderAdapter extends ArrayAdapter {
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = layoutInflater.inflate(R.layout.open_order_item, parent, false);
containerAvatar = convertView.findViewById(R.id.container_avatar_size);
imgAvatar = convertView.findViewById(R.id.img_open_order_avatar);
Glide.with(context)
.load("http://neuronovosti.ru/wp-content/uploads/2017/12/Drosophila-Brain-1024x1024.jpg")
.into(imgAvatar);
containerAvatar.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
final ViewGroup.LayoutParams lp = containerAvatar.getLayoutParams();
final ResizeAnimation a = new ResizeAnimation(containerAvatar);
a.setDuration(500);
a.setParams(lp.height, lp.height * 2);
containerAvatar.startAnimation(a);
}
});
return convertView;
}
public class ResizeAnimation extends Animation {
private int startHeight;
private int deltaHeight; // distance between start and end height
private View view;
public ResizeAnimation(View v) {
this.view = v;
}
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
view.getLayoutParams().height = (int) (startHeight + deltaHeight * interpolatedTime);
view.requestLayout();
}
public void setParams(int start, int end) {
this.startHeight = start;
deltaHeight = end - startHeight;
}
#Override
public void setDuration(long durationMillis) {
super.setDuration(durationMillis);
}
#Override
public boolean willChangeBounds() {
return true;
}
That's what you need
Try with setLayoutParams() Method of View -
private void setDimensions(View view, int width, int height){
android.view.ViewGroup.LayoutParams params = view.getLayoutParams();
params.width = width;
params.height = height;
view.setLayoutParams(params);
}
Added it on your Adapter -
public class OrderAdapter extends ArrayAdapter {
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = layoutInflater.inflate(R.layout.open_order_item, parent, false);
containerAvatar = convertView.findViewById(R.id.container_avatar_size);
imgAvatar = convertView.findViewById(R.id.img_open_order_avatar);
Glide.with(context)
.load("http://neuronovosti.ru/wp-content/uploads/2017/12/Drosophila-Brain-1024x1024.jpg")
.into(imgAvatar);
containerAvatar.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
setDimensions(containerAvatar, 100, 100);
}
});
return convertView;
}
private void setDimensions(View view, int width, int height){
android.view.ViewGroup.LayoutParams params = view.getLayoutParams();
params.width = width;
params.height = height;
view.setLayoutParams(params);
}
}
I created a recyclerview with a list of 'events'.This works fine for a list of events that is below 5. but as soon as i get 6 or more events in the list the last event will not expand when clicked, instead it dissapears. the closing animation also stops working with more than 6 events in the list.
how it should behave:
User taps event > view expands to full screen
User taps an expanded event > view collapses back to it's original size
User taps an event while another event is expanded > expanded event is set to original height en tapped event expands to fullscreen
current behavior:
User taps event > all views expand correctly except for the last item in the list
User taps expanded event > view collapses but does not animate
User taps an event while another event is expanded > expanded event collapses and tapped event expands correctly
User taps the last event in the list > the event dissapears (probably decreased it's size to below 0)
I know it probably has something to do with the way the recyclerview reuses its views when they are out of the screen. To fix this i check the position of the tapped event by the eventId instead of the position in the list, but this still leaves the issues that i talked about above.
public class EventRecyclerAdapter extends RecyclerView.Adapter<EventRecyclerAdapter.ViewHolder> {
private Context c;
private List<Event> items = new ArrayList<>();
private RelativeLayout container;
private int screenheight;
private EventListFragment eventListFragment;
private int expandedPosition = -1;
private static final String TAG = "EventRecyclerAdapter";
public interface ItemClickedListener {
void itemClicked(int position);
}
private ItemClickedListener itemClickedListener;
public EventRecyclerAdapter(List<Event> itemlist, Context c, EventListFragment eventListFragment, ItemClickedListener listener) {
this.items = itemlist;
this.c = c;
this.eventListFragment = eventListFragment;
this.itemClickedListener = listener;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
// create a new view
View itemLayoutView = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item, null);
WindowManager wm = (WindowManager) c.getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
Point size = new Point();
display.getSize(size);
screenheight = size.y;
// Get the screen height from the device
Resources r = c.getResources();
float px = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 80, r.getDisplayMetrics());
screenheight -= px;
ViewHolder viewHolder = new ViewHolder(itemLayoutView);
return viewHolder;
}
// Replace the contents of a view (invoked by the layout manager)
#Override
public void onBindViewHolder(ViewHolder viewHolder, int position) {
Event event = items.get(position);
// - get data from your itemsData at this position
// - replace the contents of the view with that itemsData
viewHolder.tvName.setText(event.getName());
viewHolder.tvLocation.setText(event.getLocation().getName());
viewHolder.tvDate.setText(Helper.dateDoubleToString(event.getStartDate()));
viewHolder.tvTicketCount.setText(String.valueOf(event.getNumberOfTickets()));
viewHolder.background.setBackgroundColor(Color.GRAY);
viewHolder.eventId = event.getId();
// Load the background image
if (event.getEventImageId() != null) {
Picasso.with(c).load(Helper.imageUrlString(event.getEventImageId())).into(viewHolder.background);
ColorMatrix matrix = new ColorMatrix();
matrix.setSaturation(0);
ColorMatrixColorFilter filter = new ColorMatrixColorFilter(matrix);
viewHolder.background.setColorFilter(filter);
}
// Check if the view needs to be expanded, collapsed or just drawn normally.
if (expandedPosition == event.getId()) {
if (event.expanded) {
collapseView(viewHolder, event);
} else if (!event.expanded) {
expandView(viewHolder, position, event);
}
} else {
setContainerHeight(viewHolder, event);
}
}
private void expandView(final EventRecyclerAdapter.ViewHolder viewHolder, final int pos, Event event) {
ResizeAnimation resizeAnimation = new ResizeAnimation(
viewHolder.container,
viewHolder.container.getHeight(),
screenheight
);
resizeAnimation.setDuration(Constants.ANIMATION_SPEED);
resizeAnimation.setAnimationListener(new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
}
#Override
public void onAnimationEnd(Animation animation) {
viewHolder.infoContainer.setVisibility(View.VISIBLE);
viewHolder.closeIcon.setVisibility(View.VISIBLE);
itemClickedListener.itemClicked(pos);
}
#Override
public void onAnimationRepeat(Animation animation) {
}
});
viewHolder.itemView.startAnimation(resizeAnimation);
viewHolder.expanded = true;
event.expanded = true;
}
private void collapseView(final EventRecyclerAdapter.ViewHolder viewHolder, Event event) {
ResizeAnimation resizeAnimation = new ResizeAnimation(
viewHolder.container,
viewHolder.container.getHeight(),
getContainerCollapsedHeight()
);
resizeAnimation.setDuration(Constants.ANIMATION_SPEED);
viewHolder.infoContainer.setVisibility(View.INVISIBLE);
viewHolder.closeIcon.setVisibility(View.INVISIBLE);
viewHolder.itemView.startAnimation(resizeAnimation);
viewHolder.expanded = false;
event.expanded = false;
}
private void setContainerHeight(EventRecyclerAdapter.ViewHolder viewHolder, Event event) {
viewHolder.container.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getContainerCollapsedHeight()));
viewHolder.infoContainer.setVisibility(View.INVISIBLE);
viewHolder.closeIcon.setVisibility(View.INVISIBLE);
event.expanded = false;
viewHolder.expanded = false;
}
private int getContainerCollapsedHeight() {
int containerHeight;
// Define the item containers height
if (items.size() <= 3) {
containerHeight = screenheight / items.size();
} else {
containerHeight = screenheight / 3;
}
return containerHeight;
}
/**
* Clear all current data and swap add the new data list.
* The expanded position also gets reset
* #param events
*/
public void swap(List<Event> events) {
this.items.clear();
this.items.addAll(events);
this.expandedPosition = -1;
Log.v(TAG,"SWAP SIZE : " + items.size());
notifyDataSetChanged();
}
// inner class to hold a reference to each item of RecyclerView
class ViewHolder extends RecyclerView.ViewHolder {
public TextView tvLocation, tvDate, tvTicketCount;
public TextView tvName;
public ImageView background;
public View container;
public View infoContainer;
public TextView closeIcon;
public int eventId;
public boolean expanded = false;
public ViewHolder(final View itemLayoutView) {
super(itemLayoutView);
tvName = (TextView) itemLayoutView.findViewById(R.id.tvName);
tvLocation = (TextView) itemLayoutView.findViewById(R.id.tvLocation);
tvDate = (TextView) itemLayoutView.findViewById(R.id.tvDate);
background = (ImageView) itemLayoutView.findViewById(R.id.background);
tvTicketCount = (TextView) itemLayoutView.findViewById(R.id.ticket_count);
container = itemLayoutView.findViewById(R.id.list_item_container);
infoContainer = itemLayoutView.findViewById(R.id.info_container);
closeIcon = (TextView) itemLayoutView.findViewById(R.id.close_icon);
infoContainer.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Activity mainActivity = (Activity) c;
FragmentManager fm = mainActivity.getFragmentManager();
//add
FragmentTransaction ft = fm.beginTransaction();
ft.setCustomAnimations(R.animator.slide_to_top, R.animator.slide_from_bottom);
ft.addToBackStack(ft.toString());
ft.add(R.id.content_frame, EventFragment.newInstance(items.get(getAdapterPosition())), Constants.EVENT_FRAGMENT_TAG);
//commit change
ft.commit();
}
});
container.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
expandedPosition = eventId;
notifyDataSetChanged();
}
});
}
}
// Return the size of your itemsData (invoked by the layout manager)
#Override
public int getItemCount() {
return items.size();
}
}
i think it's somehow running the collapseView method when i tap the last item in the list, causing its height to become below 0. But i'm unable to figure out why this is happening.
I hope someone is able to spot what's wrong here.
you can try this in OnClilck of recycleview Item
#Override
public void onClick(View view)
{
LayoutParams params = view.getLayoutParams();
if (!large)
{
params.height = 2 * view.getHeight();
} else {
params.height = view.getHeight()/2;
}
large = !large;
view.setLayoutParams(params);
}
I'm creating a Q&A where each question is a card. The answer starts showing the first line, but when its clicked it should expanded to show the full answer.
When an answer is expanded/collapsed the rest of the RecyclerView should animate to make room for the expansion or collapse to avoid showing a blank space.
I watched the talk on RecyclerView animations, and believe I want a custom ItemAnimator, where I override animateChange. At that point I should create an ObjectAnimator to animate the height of the View's LayoutParams. Unfortunately I'm having a hard time tying it all together. I also return true when overriding canReuseUpdatedViewHolder, so we reuse the same viewholder.
#Override
public boolean canReuseUpdatedViewHolder(RecyclerView.ViewHolder viewHolder) {
return true;
}
#Override
public boolean animateChange(#NonNull RecyclerView.ViewHolder oldHolder,
#NonNull final RecyclerView.ViewHolder newHolder,
#NonNull ItemHolderInfo preInfo,
#NonNull ItemHolderInfo postInfo) {
Log.d("test", "Run custom animation.");
final ColorsAdapter.ColorViewHolder holder = (ColorsAdapter.ColorViewHolder) newHolder;
FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) holder.tvColor.getLayoutParams();
ObjectAnimator halfSize = ObjectAnimator.ofInt(holder.tvColor.getLayoutParams(), "height", params.height, 0);
halfSize.start();
return super.animateChange(oldHolder, newHolder, preInfo, postInfo);
}
Right now I'm just trying to get something to animate, but nothing happens... Any ideas?
I think your animation was not working because you cannot animate LayoutParams that way although it would be neat if you could. I tried the code you had and all it did was make my view jump to the new height. Only way I found to get this to work was to use a ValueAnimator as you can see in the example below.
I noticed some shortcomings when using the DefaultItemAnimator to show/hide a view by updating its visibility. Although it did make room for the new view and animated the rest of the items up and down based on the visibility of the expandable view, I noticed it did not animate the height of the expandable view. It simply faded into place and out of place using alpha value only.
Below is a custom ItemAnimator that has size and alpha animations based on hiding/showing a LinearLayout in the ViewHolder layout. It also allows the reuse of the same ViewHolder and attempts handling partial animations correctly if the user taps the header quickly:
public static class MyAnimator extends DefaultItemAnimator {
#Override
public boolean canReuseUpdatedViewHolder(RecyclerView.ViewHolder viewHolder) {
return true;
}
private HashMap<RecyclerView.ViewHolder, AnimatorState> animatorMap = new HashMap<>();
#Override
public boolean animateChange(#NonNull RecyclerView.ViewHolder oldHolder, #NonNull final RecyclerView.ViewHolder newHolder, #NonNull ItemHolderInfo preInfo, #NonNull ItemHolderInfo postInfo) {
final ValueAnimator heightAnim;
final ObjectAnimator alphaAnim;
final CustomAdapter.ViewHolder vh = (CustomAdapter.ViewHolder) newHolder;
final View expandableView = vh.getExpandableView();
final int toHeight; // save height for later in case reversing animation
if(vh.isExpanded()) {
expandableView.setVisibility(View.VISIBLE);
// measure expandable view to get correct height
expandableView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
toHeight = expandableView.getMeasuredHeight();
alphaAnim = ObjectAnimator.ofFloat(expandableView, "alpha", 1f);
} else {
toHeight = 0;
alphaAnim = ObjectAnimator.ofFloat(expandableView, "alpha", 0f);
}
heightAnim = ValueAnimator.ofInt(expandableView.getHeight(), toHeight);
heightAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
expandableView.getLayoutParams().height = (Integer) heightAnim.getAnimatedValue();
expandableView.requestLayout();
}
});
AnimatorSet animSet = new AnimatorSet()
.setDuration(getChangeDuration());
animSet.playTogether(heightAnim, alphaAnim);
animSet.addListener(new Animator.AnimatorListener() {
private boolean isCanceled;
#Override
public void onAnimationStart(Animator animation) { }
#Override
public void onAnimationEnd(Animator animation) {
if(!vh.isExpanded() && !isCanceled) {
expandableView.setVisibility(View.GONE);
}
dispatchChangeFinished(vh, false);
animatorMap.remove(newHolder);
}
#Override
public void onAnimationCancel(Animator animation) {
isCanceled = true;
}
#Override
public void onAnimationRepeat(Animator animation) { }
});
AnimatorState animatorState = animatorMap.get(newHolder);
if(animatorState != null) {
animatorState.animSet.cancel();
// animation already running. Set start current play time of
// new animations to keep them smooth for reverse animation
alphaAnim.setCurrentPlayTime(animatorState.alphaAnim.getCurrentPlayTime());
heightAnim.setCurrentPlayTime(animatorState.heightAnim.getCurrentPlayTime());
animatorMap.remove(newHolder);
}
animatorMap.put(newHolder, new AnimatorState(alphaAnim, heightAnim, animSet));
dispatchChangeStarting(newHolder, false);
animSet.start();
return false;
}
public static class AnimatorState {
final ValueAnimator alphaAnim, heightAnim;
final AnimatorSet animSet;
public AnimatorState(ValueAnimator alphaAnim, ValueAnimator heightAnim, AnimatorSet animSet) {
this.alphaAnim = alphaAnim;
this.heightAnim = heightAnim;
this.animSet = animSet;
}
}
}
This is the result using a slightly modified RecyclerView demo.
Update:
Just noticed your use case is actually a bit different after rereading the question. You have a text view and only want to show a single line of it and then later expand it to show all lines. Fortunately that simplifies the custom animator:
public static class MyAnimator extends DefaultItemAnimator {
#Override
public boolean canReuseUpdatedViewHolder(RecyclerView.ViewHolder viewHolder) {
return true;
}
private HashMap<RecyclerView.ViewHolder, ValueAnimator> animatorMap = new HashMap<>();
#Override
public boolean animateChange(#NonNull RecyclerView.ViewHolder oldHolder, #NonNull final RecyclerView.ViewHolder newHolder, #NonNull ItemHolderInfo preInfo, #NonNull ItemHolderInfo postInfo) {
ValueAnimator prevAnim = animatorMap.get(newHolder);
if(prevAnim != null) {
prevAnim.reverse();
return false;
}
final ValueAnimator heightAnim;
final CustomAdapter.ViewHolder vh = (CustomAdapter.ViewHolder) newHolder;
final TextView tv = vh.getExpandableTextView();
if(vh.isExpanded()) {
tv.measure(View.MeasureSpec.makeMeasureSpec(((View) tv.getParent()).getWidth(), View.MeasureSpec.AT_MOST), View.MeasureSpec.UNSPECIFIED);
heightAnim = ValueAnimator.ofInt(tv.getHeight(), tv.getMeasuredHeight());
} else {
Paint.FontMetrics fm = tv.getPaint().getFontMetrics();
heightAnim = ValueAnimator.ofInt(tv.getHeight(), (int)(Math.abs(fm.top) + Math.abs(fm.bottom)));
}
heightAnim.setDuration(getChangeDuration());
heightAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
tv.getLayoutParams().height = (Integer) heightAnim.getAnimatedValue();
tv.requestLayout();
}
});
heightAnim.addListener(new Animator.AnimatorListener() {
#Override
public void onAnimationEnd(Animator animation) {
dispatchChangeFinished(vh, false);
animatorMap.remove(newHolder);
}
#Override
public void onAnimationCancel(Animator animation) { }
#Override
public void onAnimationStart(Animator animation) { }
#Override
public void onAnimationRepeat(Animator animation) { }
});
animatorMap.put(newHolder, heightAnim);
dispatchChangeStarting(newHolder, false);
heightAnim.start();
return false;
}
}
And the new demo:
You don't have to implement a custom ItemAnimator the default DefaultItemAnimator already supports what you need. However you need to tell this Animator which views changed. I guess you are calling notifyDataSetChanged() in your adapter. This prevents the animation for a single changed item in the RecyclerView (in your case the expand/collapse of the item).
You should use notifyItemChanged(int position) for the items that were changed. Here is a short itemClicked(int position) method that expands/collapses views in the RecyclerView. The field expandedPosition keeps track of the currently expanded item:
private void itemClicked(int position) {
if (expandedPosition == -1) {
// selected first item
expandedPosition = position;
notifyItemChanged(position);
} else if (expandedPosition == position) {
// collapse currently expanded item
expandedPosition = -1;
notifyItemChanged(position);
} else {
// collapse previously expanded item and expand new item
int oldExpanded = expandedPosition;
expandedPosition = position;
notifyItemChanged(oldExpanded);
notifyItemChanged(position);
}
}
This is the result:
According the documentation, you need to return false in animateChange or call runPendingAnimations later. Try returning false.
http://developer.android.com/reference/android/support/v7/widget/RecyclerView.ItemAnimator.html
Try this class:
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.graphics.Paint;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.widget.TextView;
/**
* Created by ankitagrawal on 2/14/16.
*/
public class AnimatedViewHolder extends RecyclerView.ViewHolder
implements View.OnClickListener {
private int originalHeight = 0;
private boolean mIsViewExpanded = false;
private TextView textView;
// ..... CODE ..... //
public AnimatedViewHolder(View v) {
super(v);
v.setOnClickListener(this);
// Initialize other views, like TextView, ImageView, etc. here
// If isViewExpanded == false then set the visibility
// of whatever will be in the expanded to GONE
if (!mIsViewExpanded) {
// Set Views to View.GONE and .setEnabled(false)
textView.setLines(1);
}
}
#Override
public void onClick(final View view) {
// Declare a ValueAnimator object
ValueAnimator valueAnimator;
if(mIsViewExpanded) {
view.measure(View.MeasureSpec.makeMeasureSpec(((View) view.getParent()).getWidth(), View.MeasureSpec.AT_MOST), View.MeasureSpec.UNSPECIFIED);
mIsViewExpanded = false;
valueAnimator = ValueAnimator.ofInt(view.getHeight(), view.getMeasuredHeight());
} else {
Paint.FontMetrics fm = ((TextView)view).getPaint().getFontMetrics();
valueAnimator = ValueAnimator.ofInt(view.getHeight(), (int) (Math.abs(fm.top) + Math.abs(fm.bottom)));
mIsViewExpanded = true;
}
valueAnimator.addListener(new Animator.AnimatorListener() {
#Override
public void onAnimationEnd(Animator animation) {
}
#Override
public void onAnimationCancel(Animator animation) { }
#Override
public void onAnimationStart(Animator animation) { }
#Override
public void onAnimationRepeat(Animator animation) { }
});
valueAnimator.setDuration(200);
valueAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
public void onAnimationUpdate(ValueAnimator animation) {
view.getLayoutParams().height = (Integer) animation.getAnimatedValue();
view.requestLayout();
}
});
valueAnimator.start();
}
}
The Advantage of this approach is it only add animation to onClick event and that best suits your requirement.
adding animation to viewholder will be too burdensome to your requirement.
and itemAnimator as per doc are animation for layout out items so also not best suits your requirement.
For expand & collapse animation android there is github library for it.
ExpandableRecyclerView
1).Add dependencies in the build.gradle file
dependencies {
compile 'com.android.support:recyclerview-v7:22.2.0'
compile 'com.bignerdranch.android:expandablerecyclerview:1.0.3'
}
Image of Expand & Collapse Animation
2) Expand & Collapse animation for RecyclerView animation
public static class ExampleViewHolder extends RecyclerView.ViewHolder
implements View.OnClickListener {
private int originalHeight = 0;
private boolean isViewExpanded = false;
private YourCustomView yourCustomView
public ExampleViewHolder(View v) {
super(v);
v.setOnClickListener(this);
// Initialize other views, like TextView, ImageView, etc. here
// If isViewExpanded == false then set the visibility
// of whatever will be in the expanded to GONE
if (isViewExpanded == false) {
// Set Views to View.GONE and .setEnabled(false)
yourCustomView.setVisibility(View.GONE);
yourCustomView.setEnabled(false);
}
}
#Override
public void onClick(final View view) {
// If the originalHeight is 0 then find the height of the View being used
// This would be the height of the cardview
if (originalHeight == 0) {
originalHeight = view.getHeight();
}
// Declare a ValueAnimator object
ValueAnimator valueAnimator;
if (!mIsViewExpanded) {
yourCustomView.setVisibility(View.VISIBLE);
yourCustomView.setEnabled(true);
mIsViewExpanded = true;
valueAnimator = ValueAnimator.ofInt(originalHeight, originalHeight + (int) (originalHeight * 2.0)); // These values in this method can be changed to expand however much you like
} else {
mIsViewExpanded = false;
valueAnimator = ValueAnimator.ofInt(originalHeight + (int) (originalHeight * 2.0), originalHeight);
Animation a = new AlphaAnimation(1.00f, 0.00f); // Fade out
a.setDuration(200);
// Set a listener to the animation and configure onAnimationEnd
a.setAnimationListener(new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
}
#Override
public void onAnimationEnd(Animation animation) {
yourCustomView.setVisibility(View.INVISIBLE);
yourCustomView.setEnabled(false);
}
#Override
public void onAnimationRepeat(Animation animation) {
}
});
// Set the animation on the custom view
yourCustomView.startAnimation(a);
}
valueAnimator.setDuration(200);
valueAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
public void onAnimationUpdate(ValueAnimator animation) {
Integer value = (Integer) animation.getAnimatedValue();
view.getLayoutParams().height = value.intValue();
view.requestLayout();
}
});
valueAnimator.start();
}
}
Hope this will help you.
In my scenario if I click on a expanded card from card_1 then all other cards should be invisible. Also, can I change the positions of the card runtime so it can move 2nd card on the position of the first and it will expand to full screen so there will be no need to hide other cards.
Please check the attached image.
public class DashboardMarketAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final int VIEW_TYPE_TOP_MENU = 0;
private static final int VIEW_TYPE_MARKET_MOVERS = 1;
private static final int VIEW_TYPE_DERIVATIVES = 2;
private static final int VIEW_TYPE_NEWS = 3;
private final Context context;
private final String[] menuItems;
public DashboardMarketAdapter(Context context, String[] menuItems) {
super();
this.context = context;
this.menuItems = menuItems;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v;
switch (viewType) {
case VIEW_TYPE_TOP_MENU:
v = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_top_view, parent, false);
return new TopMenuViewHolder(v, parent.getContext());
case VIEW_TYPE_MARKET_MOVERS:
v = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_market_movers, parent, false);
return new MarketMoversViewHolder(v, parent.getContext());
case VIEW_TYPE_DERIVATIVES:
v = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_market_derivatives, parent, false);
return new MarketDerivativesViewHolder(v);
case VIEW_TYPE_NEWS:
v = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_top_news, parent, false);
return new TopNewsViewHolder(v);
default:
throw new RuntimeException("there is no type that matches the type " + viewType + " + make sure your using types correctly");
}
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
}
/**
* The length of the string array will tell recycler view the count of the card views.
*/
#Override
public int getItemCount() {
return menuItems.length;
}
/**
* #param position this will return the view of the card according to the position of the card.
*/
#Override
public int getItemViewType(int position) {
switch (position) {
case VIEW_TYPE_TOP_MENU:
return VIEW_TYPE_TOP_MENU;
case VIEW_TYPE_MARKET_MOVERS:
return VIEW_TYPE_MARKET_MOVERS;
case VIEW_TYPE_DERIVATIVES:
return VIEW_TYPE_DERIVATIVES;
case VIEW_TYPE_NEWS:
return VIEW_TYPE_NEWS;
default:
return 0;
}
}
/**
* This view holder is holding the view of topmost card,
* which contains tab and viewpager.
*/
static class TopMenuViewHolder extends RecyclerView.ViewHolder {
#InjectView(R.id.pager)
ViewPager top_view_pager;
#InjectView(R.id.tabs)
DashboardSlidingTabLayout top_view_tabs;
#InjectView(R.id.expand)
Button btnExpand;
#InjectView(R.id.card_view)
CardView cardView;
private DashboardTabAdapter adapter;
private CharSequence Titles[];
private int NumberOfTabs;
int pos;
static int setView = 0;
int minHeight;
public static final int TOP_VIEW = 0;
public static final int INDICES = 1;
public static final int COMMODITY = 2;
public static final int CURRENCY = 3;
public TopMenuViewHolder(View itemView, final Context context) {
super(itemView);
ButterKnife.inject(this, itemView);
Titles = context.getResources().getStringArray(R.array.string_array_dashboard_tab_menu);
Bundle bundle = new Bundle();
bundle.putInt("setView", setView);
NumberOfTabs = Titles.length;
adapter = new DashboardTabAdapter(((FragmentActivity) context).getSupportFragmentManager(), Titles, NumberOfTabs, bundle);
top_view_pager.setAdapter(adapter);
top_view_tabs.setViewPager(top_view_pager);
/**
* Here visibility of Expand button is decided upon the tab selected.
*/
top_view_pager.addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
#Override
public void onPageSelected(int position) {
pos = position;
if (position > 0) {
btnExpand.setVisibility(View.VISIBLE);
initializeTab();
} else if (position == 0) {
btnExpand.setVisibility(View.GONE);
collapseView();
}
}
});
/**
* This will be used to give initial height for the top card.
*/
cardView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
cardView.getViewTreeObserver().removeOnPreDrawListener(this);
minHeight = cardView.getHeight();
ViewGroup.LayoutParams layoutParams = cardView.getLayoutParams();
layoutParams.height = minHeight;
cardView.setLayoutParams(layoutParams);
return true;
}
});
/**
* Here you can get the height of the screen
*/
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics dimension = new DisplayMetrics();
wm.getDefaultDisplay().getMetrics(dimension);
final int height = dimension.heightPixels;
/**
* Here you can add logic for expanding a card.
*/
btnExpand.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.i("Dashboard", "Current Fragment" + top_view_pager.getCurrentItem());
new ChildRecyclerView(context, true);
new ParentRecyclerView(context, false);
toggleCardViewnHeight(height);
}
});
}
/**
* This method will toggle expand and collapse of card view.
*/
private void toggleCardViewnHeight(int height) {
int ViewMinHeight = minHeight;
if (cardView.getHeight() == ViewMinHeight) {
// expand
expandView(height);
} else {
// collapse
collapseView();
}
}
/**
* This method will initialize tab adapter depending upon the page selected.
*/
public void initializeTab() {
switch (top_view_pager.getCurrentItem()) {
case INDICES:
((IndicesFragment) adapter.getRegisteredFragment(INDICES)).initUI();
break;
case COMMODITY:
((CommodityDashboardFragment) adapter.getRegisteredFragment(COMMODITY)).initUI();
break;
case CURRENCY:
((CurrencyDashboardFragment) adapter.getRegisteredFragment(CURRENCY)).initUI();
break;
}
}
/**
* Collapse View Animation
*/
public void collapseView() {
BaseFragment.isExpanded = 0;
ValueAnimator anim = ValueAnimator.ofInt(cardView.getMeasuredHeightAndState(),
minHeight);
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
int val = (Integer) valueAnimator.getAnimatedValue();
ViewGroup.LayoutParams layoutParams = cardView.getLayoutParams();
layoutParams.height = val;
cardView.setLayoutParams(layoutParams);
}
});
anim.addListener(new Animator.AnimatorListener() {
#Override
public void onAnimationStart(Animator animation) {
}
#Override
public void onAnimationEnd(Animator animation) {
initializeTab();
}
#Override
public void onAnimationCancel(Animator animation) {
}
#Override
public void onAnimationRepeat(Animator animation) {
}
});
anim.start();
}
/**
* Expand View Animation
*/
public void expandView(int height) {
BaseFragment.isExpanded = 1;
ValueAnimator anim = ValueAnimator.ofInt(cardView.getMeasuredHeightAndState(),
height);
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
int val = (Integer) valueAnimator.getAnimatedValue();
ViewGroup.LayoutParams layoutParams = cardView.getLayoutParams();
layoutParams.height = val;
cardView.setLayoutParams(layoutParams);
}
});
anim.addListener(new Animator.AnimatorListener() {
#Override
public void onAnimationStart(Animator animation) {
}
#Override
public void onAnimationEnd(Animator animation) {
initializeTab();
}
#Override
public void onAnimationCancel(Animator animation) {
}
#Override
public void onAnimationRepeat(Animator animation) {
}
});
anim.start();
}
}
/**
* This view holder is holding the view of MarketMovers card,
*/
static class MarketMoversViewHolder extends RecyclerView.ViewHolder {
#InjectView(R.id.btnExpand)
Button btnExpand;
#InjectView(R.id.card_view)
CardView cardView;
#InjectView(R.id.relativeLayout)
RelativeLayout relativeLayout;
int height;
String []menuItem={"MARKET MOVERS","DERIVATIVES","TOP NEWS"};
public MarketMoversViewHolder(View itemView, final Context context) {
super(itemView);
ButterKnife.inject(this, itemView);
relativeLayout.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
relativeLayout.getViewTreeObserver().removeGlobalOnLayoutListener(this);
height = relativeLayout.getHeight();
ViewGroup.LayoutParams layoutParams = cardView.getLayoutParams();
layoutParams.height = 400;
cardView.setLayoutParams(layoutParams);
}
});
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics dimension = new DisplayMetrics();
wm.getDefaultDisplay().getMetrics(dimension);
int width = dimension.widthPixels;
final int screenHeight = dimension.heightPixels;
final int fullHeight = screenHeight - 400;
btnExpand.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
toggleCardViewnHeight(fullHeight);
DashboardMarketAdapter DM =new DashboardMarketAdapter(context,menuItem);
DM.notifyDataSetChanged();
}
});
}
private void toggleCardViewnHeight(int height) {
int ViewMinHeight = 400;
if (cardView.getHeight() == ViewMinHeight) {
// expand
ValueAnimator anim = ValueAnimator.ofInt(cardView.getMeasuredHeightAndState(),
height);
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
int val = (Integer) valueAnimator.getAnimatedValue();
ViewGroup.LayoutParams layoutParams = cardView.getLayoutParams();
layoutParams.height = val;
cardView.setLayoutParams(layoutParams);
}
});
anim.start();
} else {
// collapse
ValueAnimator anim = ValueAnimator.ofInt(cardView.getMeasuredHeightAndState(),
ViewMinHeight);
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
int val = (Integer) valueAnimator.getAnimatedValue();
ViewGroup.LayoutParams layoutParams = cardView.getLayoutParams();
layoutParams.height = val;
cardView.setLayoutParams(layoutParams);
}
});
anim.start();
}
}
}
/**
* This view holder is holding the view of Derivatives card,
*/
private class MarketDerivativesViewHolder extends RecyclerView.ViewHolder {
public MarketDerivativesViewHolder(View v) {
super(v);
}
}
/**
* This view holder is holding the view of NEWS card,
*/
private class TopNewsViewHolder extends RecyclerView.ViewHolder {
public TopNewsViewHolder(View v) {
super(v);
}
}
}