code: package io.virtualapp.widgets;
  
  import android.annotation.TargetApi;
  import android.content.Context;
  import android.content.res.TypedArray;
  import android.os.Build;
  import android.util.AttributeSet;
  import android.view.View;
  import android.widget.FrameLayout;
  
  import io.virtualapp.R;
  
  /**
   * Displays a list of cards as a stack on the screen.
   * <p>
   * <b>XML attributes</b>
   * <p>
   * See {@link R.styleable#CardStackLayout CardStackLayout Attributes}
   * <p>
   * {@link R.styleable#CardStackLayout_showInitAnimation}
   * {@link R.styleable#CardStackLayout_card_gap}
   * {@link R.styleable#CardStackLayout_card_gap_bottom}
   * {@link R.styleable#CardStackLayout_parallax_enabled}
   * {@link R.styleable#CardStackLayout_parallax_scale}
   */
  public class CardStackLayout extends FrameLayout {
  	public static final boolean PARALLAX_ENABLED_DEFAULT = false;
  	public static final boolean SHOW_INIT_ANIMATION_DEFAULT = true;
  
  	private float mCardGapBottom;
  	private float mCardGap;
  	private boolean mShowInitAnimation;
  	private boolean mParallaxEnabled;
  	private int mParallaxScale;
  	private OnCardSelected mOnCardSelectedListener = null;
  
  	private CardStackAdapter mAdapter = null;
  
  	public CardStackLayout(Context context) {
  		super(context);
  		resetDefaults();
  	}
  
  	public CardStackLayout(Context context, AttributeSet attrs) {
  		this(context, attrs, 0);
  	}
  
  	public CardStackLayout(Context context, AttributeSet attrs, int defStyleAttr) {
  		super(context, attrs, defStyleAttr);
  		handleArgs(context, attrs, defStyleAttr, 0);
  	}
  
  	@TargetApi(Build.VERSION_CODES.LOLLIPOP)
  	public CardStackLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
  		super(context, attrs, defStyleAttr, defStyleRes);
  		handleArgs(context, attrs, defStyleAttr, defStyleRes);
  	}
  
  	/**
  	 * package restricted
  	 */
  	OnCardSelected getOnCardSelectedListener() {
  		return mOnCardSelectedListener;
  	}
  
  	/**
  	 * Listen on card selection events for {@link CardStackLayout}. Sends
  	 * clicked view and it's corresponding position in the callback.
  	 *
  	 * @param onCardSelectedListener
  	 *            listener
  	 */
  	public void setOnCardSelectedListener(OnCardSelected onCardSelectedListener) {
  		this.mOnCardSelectedListener = onCardSelectedListener;
  	}
  
  	private void resetDefaults() {
  		mOnCardSelectedListener = null;
  	}
  
  	private void handleArgs(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
  		resetDefaults();
  
  		final TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CardStackLayout, defStyleAttr,
  				defStyleRes);
  		mParallaxEnabled = a.getBoolean(R.styleable.CardStackLayout_parallax_enabled, PARALLAX_ENABLED_DEFAULT);
  		mShowInitAnimation = a.getBoolean(R.styleable.CardStackLayout_showInitAnimation, SHOW_INIT_ANIMATION_DEFAULT);
  		mParallaxScale = a.getInteger(R.styleable.CardStackLayout_parallax_scale,
  				getResources().getInteger(R.integer.parallax_scale_default));
  		mCardGap = a.getDimension(R.styleable.CardStackLayout_card_gap, getResources().getDimension(R.dimen.card_gap));
  		mCardGapBottom = a.getDimension(R.styleable.CardStackLayout_card_gap_bottom,
  				getResources().getDimension(R.dimen.card_gap_bottom));
  
  		a.recycle();
  	}
  
  	/**
  	 * @return adapter of type {@link CardStackAdapter} that is set for this
  	 *         view.
  	 */
  	public CardStackAdapter getAdapter() {
  		return mAdapter;
  	}
  
  	/**
  	 * Set the adapter for this {@link CardStackLayout}
  	 *
  	 * @param adapter
  	 *            Should extend {@link CardStackAdapter}
  	 */
  	public void setAdapter(CardStackAdapter adapter) {
  		this.mAdapter = adapter;
  		mAdapter.setAdapterParams(this);
  		for (int i = 0; i < mAdapter.getCount(); i++) {
  			mAdapter.addView(i);
  		}
  
  		if (mShowInitAnimation) {
  			postDelayed(this::restoreCards, 500);
  		}
  	}
  
  	/**
  	 * @return currently set parallax scale value.
  	 */
  	public int getParallaxScale() {
  		return mParallaxScale;
  	}
  
  	/**
  	 * Sets the value of parallax scale. Parallax scale is the factor which
  	 * decides how much distance a card will scroll when the user drags it down.
  	 */
  	public void setParallaxScale(int mParallaxScale) {
  		this.mParallaxScale = mParallaxScale;
  	}
  
  	public boolean isParallaxEnabled() {
  		return mParallaxEnabled;
  	}
  
  	public void setParallaxEnabled(boolean mParallaxEnabled) {
  		this.mParallaxEnabled = mParallaxEnabled;
  	}
  
  	public boolean isShowInitAnimation() {
  		return mShowInitAnimation;
  	}
  
  	public void setShowInitAnimation(boolean mShowInitAnimation) {
  		this.mShowInitAnimation = mShowInitAnimation;
  	}
  
  	/**
  	 * @return the gap (in pixels) between two consecutive cards
  	 */
  	public float getCardGap() {
  		return mCardGap;
  	}
  
  	/**
  	 * Set the gap (in pixels) between two consecutive cards
  	 */
  	public void setCardGap(float mCardGap) {
  		this.mCardGap = mCardGap;
  	}
  
  	/**
  	 * @return gap between the two consecutive cards when collapsed to the
  	 *         bottom of the screen
  	 */
  	public float getCardGapBottom() {
  		return mCardGapBottom;
  	}
  
  	public void setCardGapBottom(float mCardGapBottom) {
  		this.mCardGapBottom = mCardGapBottom;
  	}
  
  	/**
  	 * @return true if a card is selected, false otherwise
  	 */
  	public boolean isCardSelected() {
  		return mAdapter.isCardSelected();
  	}
  
  	/**
  	 * Removes the adapter that was previously set using
  	 * {@link #setAdapter(CardStackAdapter)}
  	 */
  	public void removeAdapter() {
  		if (getChildCount() > 0)
  			removeAllViews();
  		mAdapter = null;
  		mOnCardSelectedListener = null;
  	}
  
  	/**
  	 * Animates the cards to their initial position in the layout.
  	 */
  	public void restoreCards() {
  		mAdapter.resetCards();
  	}
  
  	/**
  	 * Intimates the implementing class about the selection of a card
  	 */
  	public interface OnCardSelected {
  		void onCardSelected(View v, int position);
  	}
  
  }