/*
* ******************************************************************************
* 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 it.gmariotti.cardslib.library.view;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import java.util.HashMap;
import it.gmariotti.cardslib.library.R;
import it.gmariotti.cardslib.library.internal.Card;
import it.gmariotti.cardslib.library.internal.CardExpand;
import it.gmariotti.cardslib.library.internal.CardHeader;
import it.gmariotti.cardslib.library.internal.CardThumbnail;
import it.gmariotti.cardslib.library.internal.ViewToClickToExpand;
import it.gmariotti.cardslib.library.view.base.CardViewWrapper;
import it.gmariotti.cardslib.library.view.component.CardHeaderView;
import it.gmariotti.cardslib.library.view.component.CardThumbnailView;
import it.gmariotti.cardslib.library.view.listener.SwipeDismissViewTouchListener;
/**
* Card view
* </p>
* Use an XML layout file to display it.
* </p>
* First, you need an XML layout that will display the Card.
* <pre><code>
* <it.gmariotti.cardslib.library.view.CardView
* android:id="@+id/carddemo_example_card3"
* android:layout_width="match_parent"
* android:layout_height="wrap_content"
* android:layout_marginLeft="12dp"
* android:layout_marginRight="12dp"
* android:layout_marginTop="12dp"/>
* </code></pre>
* Then create a model:
* <pre><code>
*
* //Create a Card
* Card card = new Card(getContext());
*
* //Create a CardHeader
* CardHeader header = new CardHeader(getContext());
*
* //Add Header to card
* card.addCardHeader(header);
*
* </code></pre>
* Last get a reference to the `CardView` from your code, and set your `Card.
* <pre><code>
* //Set card in the cardView
* CardView cardView = (CardView) getActivity().findViewById(R.id.carddemo);
*
* cardView.setCard(card);
* </code></pre>
* You can easily build your layout.
* </p>
* The quickest way to start with this would be to copy one of this files and create your layout.
* Then you can inflate your layout in the `CardView` using the attr: `card:card_layout_resourceID="@layout/my_layout`
* Example:
* <pre><code>
* <it.gmariotti.cardslib.library.view.CardView
* android:id="@+id/carddemo_thumb_url"
* android:layout_width="match_parent"
* android:layout_height="wrap_content"
* android:layout_marginLeft="12dp"
* android:layout_marginRight="12dp"
* card:card_layout_resourceID="@layout/card_thumbnail_layout"
* android:layout_marginTop="12dp"/>
* </code></pre>
* </p>
* @author Gabriele Mariotti (gabri.mariotti@gmail.com)
*/
public class CardView extends BaseCardView implements CardViewWrapper {
//--------------------------------------------------------------------------
//
//--------------------------------------------------------------------------
/**
* {@link CardHeader} model
*/
protected CardHeader mCardHeader;
/**
* {@link CardThumbnail} model
*/
protected CardThumbnail mCardThumbnail;
/**
* {@link CardExpand} model
*/
protected CardExpand mCardExpand;
//--------------------------------------------------------------------------
// Layout
//--------------------------------------------------------------------------
/**
* Main Layout
*/
protected View mInternalMainCardLayout;
/**
* Content Layout
*/
protected View mInternalContentLayout;
/**
* Inner View.
*/
protected View mInternalInnerView;
/**
* Hidden layout used by expand/collapse action
*/
protected View mInternalExpandLayout;
/**
* Expand Inner view
*/
protected View mInternalExpandInnerView;
/** Animator to expand/collapse */
protected Animator mExpandAnimator;
/**
* Listener invoked when Expand Animator starts
* It is used internally
*/
protected OnExpandListAnimatorListener mOnExpandListAnimatorListener;
//--------------------------------------------------------------------------
// Constructor
//--------------------------------------------------------------------------
public CardView(Context context) {
super(context);
}
public CardView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CardView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
//--------------------------------------------------------------------------
// Init
//--------------------------------------------------------------------------
/**
* Init custom attrs.
*
* @param attrs
* @param defStyle
*/
protected void initAttrs(AttributeSet attrs, int defStyle) {
card_layout_resourceID = R.layout.card_layout;
TypedArray a = getContext().getTheme().obtainStyledAttributes(
attrs, R.styleable.card_options, defStyle, defStyle);
try {
card_layout_resourceID = a.getResourceId(R.styleable.card_options_card_layout_resourceID, this.card_layout_resourceID);
} finally {
a.recycle();
}
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
public void drawableHotspotChanged(float x, float y) {
super.drawableHotspotChanged(x, y);
if (mInternalMainCardLayout != null && mInternalMainCardLayout instanceof ForegroundLinearLayout) {
mInternalMainCardLayout.drawableHotspotChanged(x,y);
}
}
//--------------------------------------------------------------------------
// Card
//--------------------------------------------------------------------------
/**
* Add a {@link Card}.
* It is very important to set all values and all components before launch this method.
*
* @param card {@link Card} model
*/
@Override
public void setCard(Card card){
super.setCard(card);
if (card!=null){
mCardHeader=card.getCardHeader();
mCardThumbnail=card.getCardThumbnail();
mCardExpand=card.getCardExpand();
}
//Retrieve all IDs
if (!isRecycle()){
retrieveLayoutIDs();
}
//Build UI
buildUI();
}
/**
* Refreshes the card content (it doesn't inflate layouts again)
*
* @param card
*/
public void refreshCard(Card card) {
mIsRecycle=true;
setCard(card);
mIsRecycle=false;
}
/**
* Refreshes the card content and replaces the inner layout elements (it inflates layouts again!)
*
* @param card
*/
public void replaceCard(Card card) {
mForceReplaceInnerLayout=true;
refreshCard(card);
mForceReplaceInnerLayout=false;
}
//--------------------------------------------------------------------------
// Setup methods
//--------------------------------------------------------------------------
@Override
protected void buildUI() {
super.buildUI();
mCard.setCardView(this);
//Setup Header view
setupHeaderView();
//Setup Main View
setupMainView();
//setup Thumbnail
setupThumbnailView();
//Setup Expand View
setupExpandView();
//Setup Supplemental Actions
setupSupplementalActions();
//Setup Listeners
setupListeners();
//Setup Expand Action
setupExpandAction();
//Setup Drawable Resources
setupDrawableResources();
}
/**
* Retrieve all Layouts IDs
*/
@Override
protected void retrieveLayoutIDs(){
super.retrieveLayoutIDs();
//Main Layout
mInternalMainCardLayout = (View) findViewById(R.id.card_main_layout);
//Get HeaderLayout
mInternalHeaderLayout = (CardHeaderView) findViewById(R.id.card_header_layout);
//Get ExpandHiddenView
mInternalExpandLayout = (View) findViewById(R.id.card_content_expand_layout);
//Get ContentLayout
mInternalContentLayout = (View) findViewById(R.id.card_main_content_layout);
//Get ThumbnailLayout
mInternalThumbnailLayout = (CardThumbnailView) findViewById(R.id.card_thumbnail_layout);
}
/**
* Setup Header View
*/
protected void setupHeaderView(){
if (mCardHeader!=null){
if (mInternalHeaderLayout !=null){
mInternalHeaderLayout.setVisibility(VISIBLE);
//Set recycle value (very important in a ListView)
mInternalHeaderLayout.setRecycle(isRecycle());
mInternalHeaderLayout.setForceReplaceInnerLayout(isForceReplaceInnerLayout());
//Add Header View
mInternalHeaderLayout.addCardHeader(mCardHeader);
}
}else{
//No header. Hide layouts
if (mInternalHeaderLayout !=null){
mInternalHeaderLayout.setVisibility(GONE);
//mInternalExpandLayout.setVisibility(View.GONE);
if (isForceReplaceInnerLayout()){
mInternalHeaderLayout.addCardHeader(null);
//mInternalHeaderLayout.removeAllViews();
}
}
}
}
/**
* Setup the Main View
*/
protected void setupMainView(){
if (mInternalContentLayout !=null){
ViewGroup mParentGroup=null;
try{
mParentGroup = (ViewGroup) mInternalContentLayout;
}catch (Exception e){
setRecycle(false);
}
//Check if view can be recycled
//It can happen in a listView, and improves performances
if (!isRecycle() || isForceReplaceInnerLayout()){
if (isForceReplaceInnerLayout() && mInternalContentLayout!=null && mInternalInnerView!=null)
((ViewGroup)mInternalContentLayout).removeView(mInternalInnerView);
mInternalInnerView=mCard.getInnerView(getContext(), (ViewGroup) mInternalContentLayout);
}else{
//View can be recycled.
//Only setup Inner Elements
if (mCard.getInnerLayout()>-1)
mCard.setupInnerViewElements(mParentGroup,mInternalInnerView);
}
}
}
/**
* Setup the Thumbnail View
*/
protected void setupThumbnailView() {
if (mInternalThumbnailLayout!=null){
if (mCardThumbnail!=null){
mInternalThumbnailLayout.setVisibility(VISIBLE);
mInternalThumbnailLayout.setRecycle(isRecycle());
mInternalThumbnailLayout.setForceReplaceInnerLayout(isForceReplaceInnerLayout());
mInternalThumbnailLayout.addCardThumbnail(mCardThumbnail);
}else{
mInternalThumbnailLayout.setVisibility(GONE);
}
}
}
/**
* Setup Drawable Resources
*/
protected void setupDrawableResources() {
//Card
if (mCard!=null){
if (mCard.getBackgroundResourceId()!=0){
changeBackgroundResourceId(mCard.getBackgroundResourceId());
}else if (mCard.getBackgroundResource()!=null){
changeBackgroundResource(mCard.getBackgroundResource());
}
}
}
protected void setupSupplementalActions() {
if (mCard != null)
mCard.setupSupplementalActions();
}
//--------------------------------------------------------------------------
// Listeners
//--------------------------------------------------------------------------
protected void setupExpandAction(){
//Config ExpandLayout and its animation
if ( mInternalExpandLayout !=null && ( (mCardHeader!=null && mCardHeader.isButtonExpandVisible()) ||
mCard.getViewToClickToExpand()!=null) ){
//Create the expand/collapse animator
mInternalExpandLayout.getViewTreeObserver().addOnPreDrawListener(
new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
mInternalExpandLayout.getViewTreeObserver().removeOnPreDrawListener(this);
View parent = (View) mInternalExpandLayout.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);
mInternalExpandLayout.measure(widthSpec, heightSpec);
mExpandAnimator = ExpandCollapseHelper.createSlideAnimator((CardView)mCard.getCardView(), 0, mInternalExpandLayout.getMeasuredHeight());
return true;
}
});
}
//Setup action and callback
setupExpandCollapseActionListener();
}
/**
* Setup All listeners
*/
@SuppressWarnings("deprecation")
@SuppressLint("NewApi")
protected void setupListeners(){
//Swipe listener
if (mCard.isSwipeable()){
this.setOnTouchListener(new SwipeDismissViewTouchListener(this, mCard,new SwipeDismissViewTouchListener.DismissCallbacks() {
@Override
public boolean canDismiss(Card card) {
return card.isSwipeable();
}
@Override
public void onDismiss(CardViewWrapper cardView, Card card) {
final ViewGroup vg = (ViewGroup)(((View)cardView).getParent());
if (vg!=null){
vg.removeView((View)cardView);
card.onSwipeCard();
}
}
}));
}else{
this.setOnTouchListener(null);
}
//OnClick listeners and partial listener
//Reset Partial Listeners
resetPartialListeners();
if (mCard.isClickable()){
//Set the onClickListener
if(!mCard.isMultiChoiceEnabled()){
if (mCard.getOnClickListener() != null) {
this.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (mCard.getOnClickListener()!=null)
mCard.getOnClickListener().onClick(mCard,v);
}
});
//Prevent multiple events
//if (!mCard.isSwipeable() && mCard.getOnSwipeListener() == null) {
// this.setClickable(true);
//}
}else{
HashMap<Integer,Card.OnCardClickListener> mMultipleOnClickListner=mCard.getMultipleOnClickListener();
if (mMultipleOnClickListner!=null && !mMultipleOnClickListner.isEmpty()){
for (int key:mMultipleOnClickListner.keySet()){
View viewClickable= decodeAreaOnClickListener(key);
final Card.OnCardClickListener mListener=mMultipleOnClickListner.get(key);
if (viewClickable!=null){
//Add listener to this view
viewClickable.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
//Callback to card listener
if (mListener!=null)
mListener.onClick(mCard,v);
}
});
//Add Selector to this view
if (key > Card.CLICK_LISTENER_ALL_VIEW) {
mHelperImpl.setCardSelector(viewClickable, getResources().getDrawable(R.drawable.card_selector));
}
}
}
}else{
//There aren't listners
this.setClickable(false);
}
}
}
}else{
this.setClickable(false);
}
//LongClick listener
if(mCard.isLongClickable()){
this.setOnLongClickListener(new OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
if (mCard.getOnLongClickListener()!=null)
return mCard.getOnLongClickListener().onLongClick(mCard,v);
return false;
}
});
}else{
this.setLongClickable(false);
}
}
/**
* Reset all partial listeners
*/
protected void resetPartialListeners() {
View viewClickable= decodeAreaOnClickListener(Card.CLICK_LISTENER_HEADER_VIEW);
if (viewClickable!=null)
viewClickable.setClickable(false);
viewClickable= decodeAreaOnClickListener(Card.CLICK_LISTENER_THUMBNAIL_VIEW);
if (viewClickable!=null)
viewClickable.setClickable(false);
viewClickable= decodeAreaOnClickListener(Card.CLICK_LISTENER_CONTENT_VIEW);
if (viewClickable!=null)
viewClickable.setClickable(false);
}
/**
*
* @param area
* @return
*/
protected View decodeAreaOnClickListener(int area){
if (area<Card.CLICK_LISTENER_ALL_VIEW && area>Card.CLICK_LISTENER_CONTENT_VIEW)
return null;
View view = null;
switch (area){
case Card.CLICK_LISTENER_ALL_VIEW :
view=this;
break;
case Card.CLICK_LISTENER_HEADER_VIEW :
view=mInternalHeaderLayout;
break;
case Card.CLICK_LISTENER_THUMBNAIL_VIEW:
view=mInternalThumbnailLayout;
break;
case Card.CLICK_LISTENER_CONTENT_VIEW:
view=mInternalContentLayout;
break;
default:
break;
}
return view;
}
//--------------------------------------------------------------------------
// Expandable Actions and Listeners
//--------------------------------------------------------------------------
/**
* Add ClickListener to expand and collapse hidden view
*/
protected void setupExpandCollapseActionListener() {
if (mInternalExpandLayout != null) {
mInternalExpandLayout.setVisibility(View.GONE);
boolean internal_blockForLongClickOnImageButtonExpand = false;
ViewToClickToExpand viewToClickToExpand = null;
//ButtonExpandVisible has a priority to viewClickToExpand
if (mCardHeader != null && mCardHeader.isButtonExpandVisible()) {
viewToClickToExpand = ViewToClickToExpand.builder()
.setupView(mInternalHeaderLayout.getImageButtonExpand())
.highlightView(true);
internal_blockForLongClickOnImageButtonExpand = true;
} else if (mCard.getViewToClickToExpand() != null) {
viewToClickToExpand = mCard.getViewToClickToExpand();
}
if (viewToClickToExpand != null) {
TitleViewOnClickListener titleViewOnClickListener = new TitleViewOnClickListener(mInternalExpandLayout, mCard, viewToClickToExpand.isViewToSelect());
/*if (mCardHeader!=null && mCardHeader.isButtonExpandVisible() && mInternalHeaderLayout != null) {
mInternalHeaderLayout.setOnClickExpandCollapseActionListener(titleViewOnClickListener);
}*/
View viewToClick = viewToClickToExpand.getViewToClick();
if (viewToClick != null) {
if (internal_blockForLongClickOnImageButtonExpand) {
//The long click on Header button is now allowed
viewToClick.setOnClickListener(titleViewOnClickListener);
}else{
if (viewToClickToExpand.isUseLongClick()){
viewToClick.setOnLongClickListener(new TitleViewOnLongClickListener(titleViewOnClickListener));
}else{
viewToClick.setOnClickListener(titleViewOnClickListener);
}
}
}else{
ViewToClickToExpand.CardElementUI cardElementUI=viewToClickToExpand.getCardElementUIToClick();
if (cardElementUI!=null){
switch (cardElementUI){
case CARD:
viewToClick = this;
break;
case HEADER:
viewToClick = getInternalHeaderLayout();
break;
case THUMBNAIL:
viewToClick = getInternalThumbnailLayout();
break;
case MAIN_CONTENT:
viewToClick = getInternalContentLayout();
break;
}
if (viewToClick != null) {
if (viewToClickToExpand.isUseLongClick()){
viewToClick.setOnLongClickListener(new TitleViewOnLongClickListener(titleViewOnClickListener));
}else{
viewToClick.setOnClickListener(titleViewOnClickListener);
}
}
}
}
if (isExpanded()) {
//Make layout visible and button selected
mInternalExpandLayout.setVisibility(View.VISIBLE);
if (viewToClick != null) {
if (viewToClickToExpand.isViewToSelect())
viewToClick.setSelected(true);
}
} else {
//Make layout hidden and button not selected
mInternalExpandLayout.setVisibility(View.GONE);
if (viewToClick != null) {
if (viewToClickToExpand.isViewToSelect())
viewToClick.setSelected(false);
}
}
}
}
}
/**
* Setup Expand View
*/
protected void setupExpandView(){
if (mInternalExpandLayout!=null && mCardExpand!=null){
//Check if view can be recycled
//It can happen in a listView, and improves performances
if (!isRecycle() || isForceReplaceInnerLayout()){
if (isForceReplaceInnerLayout() && mInternalExpandLayout!=null && mInternalExpandInnerView!=null)
((ViewGroup)mInternalExpandLayout).removeView(mInternalExpandInnerView);
mInternalExpandInnerView=mCardExpand.getInnerView(getContext(),(ViewGroup) mInternalExpandLayout);
}else{
//View can be recycled.
//Only setup Inner Elements
if (mCardExpand.getInnerLayout()>-1)
mCardExpand.setupInnerViewElements((ViewGroup)mInternalExpandLayout,mInternalExpandInnerView);
}
ViewGroup.LayoutParams layoutParams = mInternalExpandLayout.getLayoutParams();
layoutParams.height = LayoutParams.WRAP_CONTENT;
mInternalExpandLayout.setLayoutParams(layoutParams);
}
}
public void doToggleExpand() {
if (mInternalExpandLayout != null) {
ExpandContainerHelper helper = new ExpandContainerHelper(mInternalExpandLayout, mCard, false);
boolean isVisible = mInternalExpandLayout.getVisibility() == View.VISIBLE;
if (isVisible) {
ExpandCollapseHelper.animateCollapsing(helper);
} else {
ExpandCollapseHelper.animateExpanding(helper);
}
}
}
public void doExpand() {
if (mInternalExpandLayout != null) {
ExpandContainerHelper helper = new ExpandContainerHelper(mInternalExpandLayout, mCard, false);
boolean isVisible = mInternalExpandLayout.getVisibility() == View.VISIBLE;
if (!isVisible) {
ExpandCollapseHelper.animateExpanding(helper);
}
}
}
public void doCollapse() {
if (mInternalExpandLayout != null) {
ExpandContainerHelper helper = new ExpandContainerHelper(mInternalExpandLayout, mCard, false);
boolean isVisible = mInternalExpandLayout.getVisibility() == View.VISIBLE;
if (isVisible) {
ExpandCollapseHelper.animateCollapsing(helper);
}
}
}
/**
* Listener to expand/collapse hidden Expand Layout
* It starts animation
*/
protected class TitleViewOnClickListener implements View.OnClickListener {
ExpandContainerHelper mExpandContainerHelper;
private TitleViewOnClickListener(View contentParent,Card card) {
this (contentParent, card,true);
}
private TitleViewOnClickListener(View contentParent,Card card,boolean viewToSelect) {
mExpandContainerHelper = new ExpandContainerHelper(contentParent, card, viewToSelect);
}
@Override
public void onClick(View view) {
boolean isVisible = mExpandContainerHelper.contentParent.getVisibility() == View.VISIBLE;
if (isVisible) {
ExpandCollapseHelper.animateCollapsing(mExpandContainerHelper);
if (mExpandContainerHelper.viewToSelect)
view.setSelected(false);
} else {
ExpandCollapseHelper.animateExpanding(mExpandContainerHelper);
if (mExpandContainerHelper.viewToSelect)
view.setSelected(true);
}
}
}
/**
* Listener to expand/collapse hidden Expand Layout
* It starts animation
*/
protected class TitleViewOnLongClickListener implements OnLongClickListener {
TitleViewOnClickListener mOnClickListener;
private TitleViewOnLongClickListener(TitleViewOnClickListener onClickListener) {
mOnClickListener = onClickListener;
}
@Override
public boolean onLongClick(View view) {
if (mOnClickListener != null){
mOnClickListener.onClick(view);
return true;
}
return false;
}
}
private class ExpandContainerHelper{
private View contentParent;
private Card card;
private boolean viewToSelect=true;
private ExpandContainerHelper(View contentParent, Card card, boolean viewToSelect) {
this.contentParent = contentParent;
this.card = card;
this.viewToSelect = viewToSelect;
}
public CardView getCardView() {
return (CardView) card.getCardView();
}
}
@Override
protected void onSizeChanged(int xNew, int yNew, int xOld, int yOld)
{
super.onSizeChanged(xNew, yNew, xOld, yOld);
}
private static class ExpandCollapseHelper {
/**
* Expanding animator.
*/
private static void animateExpanding(final ExpandContainerHelper helper) {
//Callback
if (helper.card.getOnExpandAnimatorStartListener() != null)
helper.card.getOnExpandAnimatorStartListener().onExpandStart(helper.card);
if (helper.getCardView().getOnExpandListAnimatorListener()!=null){
//List Animator
helper.getCardView().getOnExpandListAnimatorListener().onExpandStart(helper.getCardView(), helper.contentParent);
}else{
//Std animator
helper.contentParent.setVisibility(View.VISIBLE);
if (helper.getCardView().mExpandAnimator != null) {
helper.getCardView().mExpandAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
helper.card.setExpanded(true);
//Callback
if (helper.card.getOnExpandAnimatorEndListener() != null)
helper.card.getOnExpandAnimatorEndListener().onExpandEnd(helper.card);
}
});
helper.getCardView().mExpandAnimator.start();
}else{
if (helper.card.getOnExpandAnimatorEndListener() != null)
helper.card.getOnExpandAnimatorEndListener().onExpandEnd(helper.card);
Log.w(TAG,"Does the card have the ViewToClickToExpand?");
}
}
}
/**
* Collapse animator
*/
private static void animateCollapsing(final ExpandContainerHelper helper) {
//Callback
if (helper.card.getOnCollapseAnimatorStartListener() != null)
helper.card.getOnCollapseAnimatorStartListener().onCollapseStart(helper.card);
if (helper.getCardView().getOnExpandListAnimatorListener() != null) {
//There is a List Animator.
helper.getCardView().getOnExpandListAnimatorListener().onCollapseStart(helper.getCardView(), helper.contentParent);
}else{
//Std animator
int origHeight = helper.contentParent.getHeight();
ValueAnimator animator = createSlideAnimator(helper.getCardView(),origHeight, 0);
animator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animator) {
}
@Override
public void onAnimationEnd(Animator animator) {
helper.contentParent.setVisibility(View.GONE);
helper.card.setExpanded(false);
//Callback
if (helper.card.getOnCollapseAnimatorEndListener() != null)
helper.card.getOnCollapseAnimatorEndListener().onCollapseEnd(helper.card);
}
@Override
public void onAnimationCancel(Animator animator) {
}
@Override
public void onAnimationRepeat(Animator animator) {
}
});
animator.start();
}
}
/**
* Create the Slide Animator invoked when the expand/collapse button is clicked
*/
protected static ValueAnimator createSlideAnimator(final CardView cardView,int start, int end) {
ValueAnimator animator = ValueAnimator.ofInt(start, end);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
int value = (Integer) valueAnimator.getAnimatedValue();
ViewGroup.LayoutParams layoutParams = cardView.mInternalExpandLayout.getLayoutParams();
layoutParams.height = value;
cardView.mInternalExpandLayout.setLayoutParams(layoutParams);
}
});
return animator;
}
}
// -------------------------------------------------------------
// OnExpandListAnimator Interface and Listener
// -------------------------------------------------------------
/**
* Returns the listener invoked when expand/collpase animation starts
* It is used internally
*
* @return listener
*/
public OnExpandListAnimatorListener getOnExpandListAnimatorListener() {
return mOnExpandListAnimatorListener;
}
/**
* Sets the listener invoked when expand/collapse animation starts
* It is used internally. Don't override it.
*
* @param onExpandListAnimatorListener listener
*/
public void setOnExpandListAnimatorListener(OnExpandListAnimatorListener onExpandListAnimatorListener) {
this.mOnExpandListAnimatorListener = onExpandListAnimatorListener;
}
// -------------------------------------------------------------
// Bitmap export
// -------------------------------------------------------------
/**
* Create a {@link android.graphics.Bitmap} from CardView
* @return
*/
public Bitmap createBitmap(){
if (getWidth()<=0 && getHeight()<=0){
int spec = MeasureSpec.makeMeasureSpec( 0,MeasureSpec.UNSPECIFIED);
measure(spec,spec);
layout(0, 0, getMeasuredWidth(), getMeasuredHeight());
}
Bitmap b = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(b);
draw(c);
return b;
}
// -------------------------------------------------------------
// Getter and Setter
// -------------------------------------------------------------
/**
* Returns the view used by Expand Layout
*
* @return {@link View} used by Expand Layout
*/
public View getInternalExpandLayout() {
return mInternalExpandLayout;
}
/**
* FIXME
* @return
*/
public View getInternalContentLayout() {
return mInternalContentLayout;
}
/**
* FIXME
* @return
*/
public View getInternalInnerView() {
return mInternalInnerView;
}
/**
* Indicates if the card is expanded or collapsed
*
* @return <code>true</code> if the card is expanded
*/
public boolean isExpanded() {
if (mCard!=null){
return mCard.isExpanded();
}else
return false;
}
/**
* Sets the card as expanded or collapsed
*
* @param expanded <code>true</code> if the card is expanded
*/
public void setExpanded(boolean expanded) {
if (mCard!=null){
mCard.setExpanded(expanded);
}
}
@Override
public boolean isNative() {
return false;
}
/**
* Retrieves the InternalMainCardGlobalLayout.
* Background style is applied here.
*
* @return
*/
public View getInternalMainCardLayout() {
return mInternalMainCardLayout;
}
/**
* Changes dynamically the drawable resource to override the style of MainLayout.
*
* @param drawableResourceId drawable resource Id
*/
@Override
public void changeBackgroundResourceId(int drawableResourceId) {
if (drawableResourceId!=0){
if (mInternalMainCardLayout!=null){
mInternalMainCardLayout.setBackgroundResource(drawableResourceId);
}
}
}
/**
* Changes dynamically the drawable resource to override the style of MainLayout.
*
* @param drawableResource drawable resource
*/
@Override
public void changeBackgroundResource(Drawable drawableResource) {
if (drawableResource!=null){
if (mInternalMainCardLayout!=null){
mHelperImpl.setBackground(mInternalMainCardLayout, drawableResource);
}
}
}
@Override
public void changeBackgroundColorResourceId(int colorResourceId) {
//TODO : do nothing for now
}
}