diff options
| author | Griffin Millender <griffinn.millender@gmail.com> | 2016-10-12 23:33:23 -0500 |
|---|---|---|
| committer | Griffin Millender <griffinn.millender@gmail.com> | 2016-10-13 22:03:44 -0500 |
| commit | 0127e9ab2e14c29de7234bb736c0aee33cf9caf3 (patch) | |
| tree | f6d77d3cd9c1eb58d7f9cd349e0516bd3fb7fb5d | |
| parent | 857c1b59f2015177bd1b34155c4434e8bd498790 (diff) | |
Add CardRecyclerView
Change-Id: Id77fad4845c259cfd3eeab5acb14b6b28fd0e773
| -rw-r--r-- | Android.mk | 3 | ||||
| -rw-r--r-- | res/layout/native_recyclerview_card_layout.xml | 34 | ||||
| -rw-r--r-- | res/layout/native_recyclerview_card_thumbnail_layout.xml | 31 | ||||
| -rw-r--r-- | res/values/styles.xml | 15 | ||||
| -rw-r--r-- | src/com/android/cards/recyclerview/internal/BaseRecyclerViewAdapter.java | 211 | ||||
| -rw-r--r-- | src/com/android/cards/recyclerview/internal/CardArrayRecyclerViewAdapter.java | 165 | ||||
| -rw-r--r-- | src/com/android/cards/recyclerview/internal/ViewAdapterImpl.java | 42 | ||||
| -rw-r--r-- | src/com/android/cards/recyclerview/view/CardRecyclerView.java | 325 |
8 files changed, 826 insertions, 0 deletions
@@ -19,6 +19,9 @@ LOCAL_STATIC_JAVA_LIBRARIES := \ android-support-v4 \ android-support-v7-cardview +LOCAL_JAVA_LIBRARIES := \ + android-support-v7-recyclerview + LOCAL_MODULE := android-opt-cards LOCAL_SDK_VERSION := 21 diff --git a/res/layout/native_recyclerview_card_layout.xml b/res/layout/native_recyclerview_card_layout.xml new file mode 100644 index 0000000..c5ecb9c --- /dev/null +++ b/res/layout/native_recyclerview_card_layout.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + ~ ******************************************************************************* + ~ Copyright (c) 2013-2014 Gabriele Mariotti. + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + ~ ***************************************************************************** + --> + +<!-- You can customize this layout. + You need to have in your layout a `CardView` with the ID `list_cardId` --> + + + <it.gmariotti.cardslib.library.view.CardViewNative + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:card="http://schemas.android.com/apk/res-auto" + android:id="@+id/list_cardId" + android:layout_width="match_parent" + android:layout_height="wrap_content" + style="@style/native_recyclerview_card.base" + card:card_layout_resourceID="@layout/native_card_layout"/> + + diff --git a/res/layout/native_recyclerview_card_thumbnail_layout.xml b/res/layout/native_recyclerview_card_thumbnail_layout.xml new file mode 100644 index 0000000..12c99cc --- /dev/null +++ b/res/layout/native_recyclerview_card_thumbnail_layout.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + ~ ******************************************************************************* + ~ Copyright (c) 2013-2014 Gabriele Mariotti. + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + ~ ***************************************************************************** + --> + +<!-- You can customize this layout. + You need to have in your layout a `CardView` with the ID `list_cardId` --> +<it.gmariotti.cardslib.library.view.CardViewNative + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:card="http://schemas.android.com/apk/res-auto" + android:id="@+id/list_cardId" + android:layout_width="match_parent" + android:layout_height="wrap_content" + style="@style/native_recyclerview_card.thumbnail" + card:card_layout_resourceID="@layout/native_card_thumbnail_layout" + /> diff --git a/res/values/styles.xml b/res/values/styles.xml index 638a8ec..2390f26 100644 --- a/res/values/styles.xml +++ b/res/values/styles.xml @@ -454,6 +454,21 @@ <item name="android:layout_marginBottom">@dimen/card_section_title_margin_top</item> </style> + <!--Recycler view--> + <style name="native_recyclerview_card"> + <item name="android:layout_marginStart">@dimen/native_recyclerview_card_margin_start</item> + <item name="android:layout_marginEnd">@dimen/native_recyclerview_card_margin_end</item> + <item name="android:layout_marginBottom">@dimen/native_recyclerview_card_margin_bottom</item> + <item name="android:layout_marginTop">@dimen/native_recyclerview_card_margin_top</item> + </style> + + <style name="native_recyclerview_card.base" > + + </style> + <!-- Thumbnail list --> + <style name="native_recyclerview_card.thumbnail"> + + </style> </resources> diff --git a/src/com/android/cards/recyclerview/internal/BaseRecyclerViewAdapter.java b/src/com/android/cards/recyclerview/internal/BaseRecyclerViewAdapter.java new file mode 100644 index 0000000..a6e6b00 --- /dev/null +++ b/src/com/android/cards/recyclerview/internal/BaseRecyclerViewAdapter.java @@ -0,0 +1,211 @@ +/* + * ****************************************************************************** + * Copyright (c) 2013-2014 Gabriele Mariotti. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ***************************************************************************** + */ + +package com.android.cards.recyclerview.internal; + +import android.content.Context; +import android.support.annotation.LayoutRes; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.android.cards.R; +import com.android.cards.internal.Card; +import com.android.cards.recyclerview.view.CardRecyclerView; +import com.android.cards.view.base.CardViewWrapper; + +/** + * Base RecyclerViewAdapter for RecyclerView and its implemetations. + * + * @author Gabriele Mariotti (gabri.mariotti@gmail.com) + */ +public abstract class BaseRecyclerViewAdapter extends RecyclerView.Adapter<BaseRecyclerViewAdapter.CardViewHolder> implements ViewAdapterImpl { + + /** + * Current context + */ + protected Context mContext; + + /** + * Default layout used for each row + */ + protected @LayoutRes int mRowLayoutId = R.layout.list_card_layout; + + /** + * Used to set the viewTypeCount + */ + protected int typeCardCount = 1; + + /** + * Array of layout resource ids + */ + protected @LayoutRes int[] mRowLayoutIds; + + /** + * {@link CardRecyclerView} + */ + protected CardRecyclerView mCardRecyclerView; + + // ------------------------------------------------------------- + // Constructors + // ------------------------------------------------------------- + + /** + * Constructor + * + * @param context The current context. + */ + public BaseRecyclerViewAdapter(Context context) { + super(); + mContext = context; + } + + // ------------------------------------------------------------- + // ViewHolder + // ------------------------------------------------------------- + + public static class CardViewHolder extends RecyclerView.ViewHolder { + + public final CardViewWrapper mCardView; + public boolean recycled = false; + + public CardViewHolder(View view) { + super(view); + mCardView = (CardViewWrapper) view.findViewById(R.id.list_cardId); + } + } + + @Override + public CardViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + + if (mRowLayoutIds == null || mRowLayoutIds.length == 0) { + final View view = LayoutInflater.from(mContext).inflate(mRowLayoutId, parent, false); + return new CardViewHolder(view); + } else { + final View view = LayoutInflater.from(mContext).inflate(mRowLayoutIds[viewType], parent, false); + return new CardViewHolder(view); + } + } + + @Override + public void onBindViewHolder(CardViewHolder cardViewHolder, int position) { + + CardViewWrapper mCardView = cardViewHolder.mCardView; + Card mCard = getItem(position); + + //Setup card + if (mCardView != null) { + //It is important to set recycle value for inner layout elements + mCardView.setForceReplaceInnerLayout(Card.equalsInnerLayout(mCardView.getCard(),mCard)); + + //It is important to set recycle value for performance issue + mCardView.setRecycle(cardViewHolder.recycled); + + //Save original swipeable to prevent cardSwipeListener (listView requires another cardSwipeListener) + boolean origianlSwipeable = mCard.isSwipeable(); + mCard.setSwipeable(false); + + mCardView.setCard(mCard); + + //Set originalValue + mCard.setSwipeable(origianlSwipeable); + + //If card has an expandable button override animation + if ((mCard.getCardHeader() != null && mCard.getCardHeader().isButtonExpandVisible()) || mCard.getViewToClickToExpand()!=null ){ + setupExpandCollapseListAnimation(mCardView); + } + + //Setup swipeable animation + //setupSwipeableAnimation(mCard, mCardView); + } + } + + /** + * Overrides the default collapse/expand animation in a List + * + * @param cardView {@link com.android.cards.view.base.CardViewWrapper} + */ + protected void setupExpandCollapseListAnimation(CardViewWrapper cardView) { + + if (cardView == null) return; + cardView.setOnExpandListAnimatorListener(mCardRecyclerView); + } + + // ------------------------------------------------------------- + // Views + // ------------------------------------------------------------- + + public int getTypeCardCount() { + return typeCardCount; + } + + @Override + public int getItemViewType(int position) { + Card card = (Card) getItem(position); + return card.getType(); + } + + /** + * Override this method to obtain a Card from a position + * + * @param position + * @return + */ + public abstract Card getItem(int position); + + /** + * Sets layout resource ID used by rows + * + * @param rowLayoutId layout resource id + */ + public void setRowLayoutId(@LayoutRes int rowLayoutId) { + this.mRowLayoutId = rowLayoutId; + } + + /** + * Sets layouts resource ID used by rows + * + * @param rowLayoutIds array of layout resource ids + */ + public void setRowLayoutIds(@LayoutRes int[] rowLayoutIds) { + mRowLayoutIds = rowLayoutIds; + if (rowLayoutIds != null) + typeCardCount = rowLayoutIds.length; + else + typeCardCount = 1; + } + + /** + * + * @return the RecyclerView + */ + public CardRecyclerView getCardRecyclerView() { + return mCardRecyclerView; + } + + /** + * Sets the RecyclerView + * + * @param cardRecyclerView + */ + public void setCardRecyclerView(CardRecyclerView cardRecyclerView) { + mCardRecyclerView = cardRecyclerView; + } + +} diff --git a/src/com/android/cards/recyclerview/internal/CardArrayRecyclerViewAdapter.java b/src/com/android/cards/recyclerview/internal/CardArrayRecyclerViewAdapter.java new file mode 100644 index 0000000..77c755f --- /dev/null +++ b/src/com/android/cards/recyclerview/internal/CardArrayRecyclerViewAdapter.java @@ -0,0 +1,165 @@ +/* + * ****************************************************************************** + * Copyright (c) 2013-2014 Gabriele Mariotti. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ***************************************************************************** + */ + +package com.android.cards.recyclerview.internal; + +import android.content.Context; +import android.support.annotation.NonNull; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import com.android.cards.internal.Card; + +/** + * RecyclerView with an ArrayAdapter. + * + * @author Gabriele Mariotti (gabri.mariotti@gmail.com) + */ +public class CardArrayRecyclerViewAdapter extends BaseRecyclerViewAdapter { + + /** + * Internal objects + */ + protected List<Card> mCards; + + // ------------------------------------------------------------- + // Constructors + // ------------------------------------------------------------- + + /** + * Constructor + * + * @param context The current context. + * @param cards The cards to represent in the ListView. + */ + public CardArrayRecyclerViewAdapter(Context context, List<Card> cards) { + super(context); + if (cards != null) { + mCards = cards; + } else { + mCards = new ArrayList<Card>(); + } + } + + // ------------------------------------------------------------- + // Methods + // ------------------------------------------------------------- + + @Override + public int getItemCount() { + return mCards.size(); + } + + + @Override + public Card getItem(int position) { + return mCards.get(position); + } + + /** + * Sets the card's list + * @param cards list + */ + public void setCards(List<Card> cards) { + mCards = cards; + } + + /** + * Appends the specified element to the end of the {@code List}. + * + * @param card the object to add. + * + * @return always true. + */ + @Override + public boolean add(@NonNull final Card card) { + boolean result = mCards.add(card); + notifyDataSetChanged(); + return result; + } + + /** + * Appends the specified element into the index specified {@code List}. + * @param index + * @param card + */ + @Override + public void add(final int index, @NonNull final Card card) { + mCards.add(index, card); + notifyItemInserted(index); + } + + /** + * Adds the objects in the specified collection to the end of this List. The objects are added in the order in which they are returned from the collection's iterator. + * + * @param collection the collection of objects. + * + * @return {@code true} if this {@code List} is modified, {@code false} otherwise. + */ + public boolean addAll(@NonNull final Collection<? extends Card> collection) { + boolean result = mCards.addAll(collection); + notifyDataSetChanged(); + return result; + } + + /** + * Check if the list contains the element + * @param card + * @return + */ + @Override + public boolean contains(final Card card) { + return mCards.contains(card); + } + + /** + * Clears the list + */ + @Override + public void clear() { + mCards.clear(); + notifyDataSetChanged(); + } + + /** + * Removes the specified element + * @param card + * @return + */ + @Override + public boolean remove(@NonNull final Card card) { + boolean result = mCards.remove(card); + notifyDataSetChanged(); + return result; + } + + /** + * Removes the element at position + * @param position + * @return + */ + @NonNull + @Override + public Card remove(final int position) { + Card result = mCards.remove(position); + notifyItemRemoved(position); + return result; + } +} diff --git a/src/com/android/cards/recyclerview/internal/ViewAdapterImpl.java b/src/com/android/cards/recyclerview/internal/ViewAdapterImpl.java new file mode 100644 index 0000000..40c93fa --- /dev/null +++ b/src/com/android/cards/recyclerview/internal/ViewAdapterImpl.java @@ -0,0 +1,42 @@ +/* + * ****************************************************************************** + * Copyright (c) 2013-2014 Gabriele Mariotti. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ***************************************************************************** + */ + +package com.android.cards.recyclerview.internal; + +import android.support.annotation.NonNull; + +import com.android.cards.internal.Card; + +/** + * @author Gabriele Mariotti (gabri.mariotti@gmail.com) + */ +public interface ViewAdapterImpl { + + public boolean add(@NonNull final Card card); + + public void add(final int index, @NonNull final Card card) ; + + public boolean remove(@NonNull final Card card); + + public Card remove(final int position); + + public boolean contains(final Card object); + + public void clear(); + +} diff --git a/src/com/android/cards/recyclerview/view/CardRecyclerView.java b/src/com/android/cards/recyclerview/view/CardRecyclerView.java new file mode 100644 index 0000000..f6f94d1 --- /dev/null +++ b/src/com/android/cards/recyclerview/view/CardRecyclerView.java @@ -0,0 +1,325 @@ +/* + * ****************************************************************************** + * Copyright (c) 2013-2014 Gabriele Mariotti. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ***************************************************************************** + */ + +package com.android.cards.recyclerview.view; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; +import android.content.Context; +import android.content.res.TypedArray; +import android.support.annotation.LayoutRes; +import android.support.v7.widget.RecyclerView; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; + +import com.android.cards.R; +import com.android.cards.internal.Card; +import com.android.cards.recyclerview.internal.BaseRecyclerViewAdapter; +import com.android.cards.view.base.CardViewWrapper; + + +/** + * + * RecyclerView for Cards. + * It uses a {@link BaseRecyclerViewAdapter} to populate items. + * </p> + * Usage: + * <pre><code> + * <com.android.cards.recyclerview.view.CardRecyclerView + * android:layout_width="match_parent" + * android:layout_height="match_parent" + * android:id="@+id/listId" + * card:list_card_layout_resourceID="@layout/list_card_thumbnail_layout" /> * + * </code></pre> + * It provides a default layout id for each row @layout/list_card_layout + * Use can easily customize it using card:list_card_layout_resourceID attr in your xml layout. + * </p> + * + * @author Gabriele Mariotti (gabri.mariotti@gmail.com) + */ +public class CardRecyclerView extends RecyclerView implements CardViewWrapper.OnExpandListAnimatorListener { + + /** + * Card Adapter + */ + protected BaseRecyclerViewAdapter mAdapter; + + //-------------------------------------------------------------------------- + // Custom Attrs + //-------------------------------------------------------------------------- + + /** + * Default layout to apply to card + */ + protected @LayoutRes + int list_card_layout_resourceID = R.layout.list_card_layout; + + /** + * Layouts to apply to card + */ + protected @LayoutRes int[] list_card_layout_resourceIDs; + + //-------------------------------------------------------------------------- + // Constructors + //-------------------------------------------------------------------------- + + public CardRecyclerView(Context context) { + super(context); + init(context, null, 0); + } + + public CardRecyclerView(Context context, AttributeSet attrs) { + super(context, attrs); + init(context, attrs, 0); + } + + public CardRecyclerView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + init(context, attrs, defStyle); + } + + //-------------------------------------------------------------------------- + // Init + //-------------------------------------------------------------------------- + + /** + * Initialize + * + * @param attrs + * @param defStyle + */ + protected void init(Context context,AttributeSet attrs, int defStyle){ + + //Init attrs + initAttrs(context,attrs,defStyle); + + } + + + /** + * Init custom attrs. + * + * @param attrs + * @param defStyle + */ + protected void initAttrs(Context context,AttributeSet attrs, int defStyle) { + + list_card_layout_resourceID = R.layout.list_card_layout; + + TypedArray a = context.getTheme().obtainStyledAttributes( + attrs, R.styleable.card_options, defStyle, defStyle); + + try { + list_card_layout_resourceID = a.getResourceId(R.styleable.card_options_list_card_layout_resourceID, this.list_card_layout_resourceID); + + int arrayIds = a.getResourceId(R.styleable.card_options_list_card_layout_resourceIDs, 0); + if (arrayIds > 0 ) { + TypedArray layouts = context.getResources().obtainTypedArray(arrayIds); + if (layouts != null){ + list_card_layout_resourceIDs = new int[layouts.length()]; + for (int i=0; i<layouts.length();i++){ + list_card_layout_resourceIDs[i] = layouts.getResourceId(i, R.layout.list_card_layout); + } + } + layouts.recycle(); + } + + + } finally { + a.recycle(); + } + } + + //-------------------------------------------------------------------------- + // Adapter + //-------------------------------------------------------------------------- + + /** + * Set {@link com.android.cards.recyclerview.internal.BaseRecyclerViewAdapter} and layout used by items in RecyclerView + * + * @param adapter {@link com.android.cards.recyclerview.internal.BaseRecyclerViewAdapter} + */ + public void setAdapter(BaseRecyclerViewAdapter adapter) { + super.setAdapter(adapter); + + //Set Layout used by items + adapter.setRowLayoutId(list_card_layout_resourceID); + adapter.setRowLayoutIds(list_card_layout_resourceIDs); + adapter.setCardRecyclerView(this); + + mAdapter=adapter; + + setRecyclerListener(new RecyclerView.RecyclerListener() { + @Override + public void onViewRecycled(RecyclerView.ViewHolder viewHolder) { + if (viewHolder instanceof BaseRecyclerViewAdapter.CardViewHolder){ + ((BaseRecyclerViewAdapter.CardViewHolder)viewHolder).recycled = true; + } + } + }); + } + + //-------------------------------------------------------------------------- + // Expand and Collapse animator + //-------------------------------------------------------------------------- + + @Override + public void onExpandStart(CardViewWrapper viewCard,View expandingLayout) { + ExpandCollapseHelper.animateExpanding(expandingLayout,viewCard,this); + } + + @Override + public void onCollapseStart(CardViewWrapper viewCard,View expandingLayout) { + ExpandCollapseHelper.animateCollapsing(expandingLayout,viewCard,this); + } + + /** + * Helper to animate collapse and expand animation + */ + private static class ExpandCollapseHelper { + + /** + * This method expandes the view that was clicked. + * + * @param expandingLayout layout to expand + * @param cardView cardView + * @param recyclerView recyclerView + */ + public static void animateCollapsing(final View expandingLayout, final CardViewWrapper cardView,final RecyclerView recyclerView) { + int origHeight = expandingLayout.getHeight(); + + ValueAnimator animator = createHeightAnimator(expandingLayout, origHeight, 0); + animator.addListener(new AnimatorListenerAdapter() { + + @Override + public void onAnimationEnd(final Animator animator) { + expandingLayout.setVisibility(View.GONE); + + cardView.setExpanded(false);//card.setExpanded(true); + + notifyAdapter(recyclerView,recyclerView.getLayoutManager().getPosition((View)cardView)); + + Card card = cardView.getCard(); + if (card.getOnCollapseAnimatorEndListener()!=null) + card.getOnCollapseAnimatorEndListener().onCollapseEnd(card); + } + }); + animator.start(); + } + + /** + * This method collapse the view that was clicked. + * + * @param expandingLayout layout to collapse + * @param cardView cardView + * @param recyclerView recyclerView + */ + public static void animateExpanding(final View expandingLayout, final CardViewWrapper cardView,final RecyclerView recyclerView) { + /* Update the layout so the extra content becomes visible.*/ + expandingLayout.setVisibility(View.VISIBLE); + + View parent = (View) expandingLayout.getParent(); + final int widthSpec = View.MeasureSpec.makeMeasureSpec(parent.getMeasuredWidth() - parent.getPaddingLeft() - parent.getPaddingRight(), View.MeasureSpec.AT_MOST); + final int heightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); + expandingLayout.measure(widthSpec, heightSpec); + + + ValueAnimator animator = createHeightAnimator(expandingLayout, 0, expandingLayout.getMeasuredHeight()); + animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + final int listViewHeight = recyclerView.getHeight(); + final int listViewBottomPadding = recyclerView.getPaddingBottom(); + + final View v = findDirectChild(expandingLayout, recyclerView); + + @Override + public void onAnimationUpdate(final ValueAnimator valueAnimator) { + if (recyclerView.getLayoutManager().canScrollVertically()) { + final int bottom = v.getBottom(); + if (bottom > listViewHeight) { + final int top = v.getTop(); + if (top > 0) { + //recyclerView.scrollBy(0,Math.min(bottom - listViewHeight + listViewBottomPadding, top)); + recyclerView.smoothScrollBy(0,Math.min(bottom - listViewHeight + listViewBottomPadding + 4, top)); + + } + } + } + } + }); + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + cardView.setExpanded(true);//card.setExpanded(true); + + notifyAdapter(recyclerView,recyclerView.getLayoutManager().getPosition((View)cardView)); + + Card card = cardView.getCard(); + if (card.getOnExpandAnimatorEndListener()!=null) + card.getOnExpandAnimatorEndListener().onExpandEnd(card); + + } + }); + animator.start(); + } + + private static View findDirectChild(final View view, final RecyclerView recyclerView) { + View result = view; + View parent = (View) result.getParent(); + while (parent != recyclerView) { + result = parent; + parent = (View) result.getParent(); + } + return result; + } + + public static ValueAnimator createHeightAnimator(final View view, final int start, final int end) { + ValueAnimator animator = ValueAnimator.ofInt(start, end); + animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + + @Override + public void onAnimationUpdate(final ValueAnimator valueAnimator) { + int value = (Integer) valueAnimator.getAnimatedValue(); + + ViewGroup.LayoutParams layoutParams = view.getLayoutParams(); + layoutParams.height = value; + view.setLayoutParams(layoutParams); + } + }); + return animator; + } + + /** + * This method notifies the adapter after setting expand value inside cards + * + * @param recyclerView + */ + public static void notifyAdapter(RecyclerView recyclerView,int position){ + + if (recyclerView instanceof CardRecyclerView){ + + CardRecyclerView cardRecyclerView = (CardRecyclerView) recyclerView; + if (cardRecyclerView.mAdapter!=null){ + cardRecyclerView.mAdapter.notifyItemChanged(position); + } + } + } + } +} |
