package com.fima.cardsui.views; import java.util.ArrayList; import android.annotation.SuppressLint; import android.content.Context; import android.os.Build; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.ViewTreeObserver; import android.view.animation.TranslateAnimation; import android.widget.AbsListView; import android.widget.AbsListView.OnScrollListener; import android.widget.FrameLayout; import android.widget.Space; import android.widget.TableLayout; import android.widget.TableRow; import com.actionbarsherlock.R; import com.fima.cardsui.StackAdapter; import com.fima.cardsui.objects.AbstractCard; import com.fima.cardsui.objects.Card; import com.fima.cardsui.objects.CardStack; // TODO: Auto-generated Javadoc /** * The Class CardUI. */ public class CardUI extends FrameLayout { /** Constants. */ private static final int STATE_ONSCREEN = 0; /** The Constant STATE_OFFSCREEN. */ private static final int STATE_OFFSCREEN = 1; /** The Constant STATE_RETURNING. */ private static final int STATE_RETURNING = 2; /** * The listener interface for receiving onRendered events. * The class that is interested in processing a onRendered * event implements this interface, and the object created * with that class is registered with a component using the * component's <code>addOnRenderedListener<code> method. When * the onRendered event occurs, that object's appropriate * method is invoked. * * @see OnRenderedEvent */ public interface OnRenderedListener { /** * On rendered. */ public void onRendered(); } /** ****************************** Fields ******************************. */ private ArrayList<AbstractCard> mStacks; /** The m context. */ private Context mContext; /** The m quick return view. */ private ViewGroup mQuickReturnView; /** The table layout to be used for multiple columns. */ private TableLayout mTableLayout; /** The number of columns, 1 by default. */ private int mColumnNumber = 1; /** The m placeholder view. */ private View mPlaceholderView; /** The m list view. */ private QuickReturnListView mListView; /** The m min raw y. */ private int mMinRawY = 0; /** The m state. */ private int mState = STATE_ONSCREEN; /** The m quick return height. */ private int mQuickReturnHeight; /** The m cached vertical scroll range. */ private int mCachedVerticalScrollRange; /** The m swipeable. */ private boolean mSwipeable = false; /** The on rendered listener. */ private OnRenderedListener onRenderedListener; /** The rendered cards stacks. */ protected int renderedCardsStacks = 0; /** The m scroll y. */ protected int mScrollY; /** The m adapter. */ private StackAdapter mAdapter; /** The m header. */ private View mHeader; /** * Constructor. * * @param context the context * @param attrs the attrs * @param defStyle the def style */ public CardUI(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); //read the number of columns from the attributes mColumnNumber = attrs.getAttributeIntValue(null, "columnCount", 1); initData(context); } /** * Constructor. * * @param context the context * @param attrs the attrs */ public CardUI(Context context, AttributeSet attrs) { super(context, attrs); //read the number of columns from the attributes mColumnNumber = attrs.getAttributeIntValue(null, "columnCount", 1); initData(context); } /** * Constructor. * * @param context the context */ public CardUI(Context context) { super(context); initData(context); } /** * Inits the data. * * @param context the context */ private void initData(Context context) { mContext = context; LayoutInflater inflater = LayoutInflater.from(context); mStacks = new ArrayList<AbstractCard>(); //inflate a different layout, depending on the number of columns if (mColumnNumber == 1) { inflater.inflate(R.layout.cui_cards_view, this); // init observable scrollview mListView = (QuickReturnListView) findViewById(R.id.listView); } else { //initialize the mulitcolumn view inflater.inflate(R.layout.cui_cards_view_multicolumn, this); mTableLayout = (TableLayout) findViewById(R.id.tableLayout); } // mListView.setCallbacks(this); mHeader = inflater.inflate(R.layout.cui_header, null); mQuickReturnView = (ViewGroup) findViewById(R.id.sticky); mPlaceholderView = mHeader.findViewById(R.id.placeholder); } /** * Sets the swipeable. * * @param b the new swipeable */ public void setSwipeable(boolean b) { mSwipeable = b; } /** * Sets the header. * * @param header the new header */ public void setHeader(View header) { mPlaceholderView.setVisibility(View.VISIBLE); mListView.getViewTreeObserver().addOnGlobalLayoutListener( new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { mQuickReturnHeight = mQuickReturnView.getHeight(); mListView.computeScrollY(); mCachedVerticalScrollRange = mListView.getListHeight(); } }); mListView.setOnScrollListener(new OnScrollListener() { @SuppressLint("NewApi") @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { mScrollY = 0; int translationY = 0; if (mListView.scrollYIsComputed()) { mScrollY = mListView.getComputedScrollY(); } int rawY = mPlaceholderView.getTop() - Math.min( mCachedVerticalScrollRange - mListView.getHeight(), mScrollY); switch (mState) { case STATE_OFFSCREEN: if (rawY <= mMinRawY) { mMinRawY = rawY; } else { mState = STATE_RETURNING; } translationY = rawY; break; case STATE_ONSCREEN: if (rawY < -mQuickReturnHeight) { mState = STATE_OFFSCREEN; mMinRawY = rawY; } translationY = rawY; break; case STATE_RETURNING: translationY = (rawY - mMinRawY) - mQuickReturnHeight; if (translationY > 0) { translationY = 0; mMinRawY = rawY - mQuickReturnHeight; } if (rawY > 0) { mState = STATE_ONSCREEN; translationY = rawY; } if (translationY < -mQuickReturnHeight) { mState = STATE_OFFSCREEN; mMinRawY = rawY; } break; } /** this can be used if the build is below honeycomb **/ if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.HONEYCOMB) { TranslateAnimation anim = new TranslateAnimation(0, 0, translationY, translationY); anim.setFillAfter(true); anim.setDuration(0); mQuickReturnView.startAnimation(anim); } else { mQuickReturnView.setTranslationY(translationY); } } @Override public void onScrollStateChanged(AbsListView view, int scrollState) { } }); if (header != null) { try { mQuickReturnView.removeAllViews(); } catch (Exception e) { e.printStackTrace(); } mQuickReturnView.addView(header); } } /** * Scroll to card. * * @param pos the pos */ public void scrollToCard(int pos) { // int y = 0; try { // y = getY(pos); mListView.smoothScrollToPosition(pos); } catch (Exception e) { e.printStackTrace(); } } /** * Scroll to y. * * @param y the y */ public void scrollToY(int y) { try { mListView.scrollTo(0, y); } catch (Exception e) { e.printStackTrace(); } } /** * Gets the scroll view. * * @return the scroll view */ public QuickReturnListView getScrollView() { return mListView; } /** * Gets the last card stack position. * * @return the last card stack position */ public int getLastCardStackPosition() { return mStacks.size() - 1; } /** * Adds the card. * * @param card the card */ public void addCard(Card card) { addCard(card, false); } /** * Adds the card. * * @param card the card * @param refresh the refresh */ public void addCard(Card card, boolean refresh) { CardStack stack = new CardStack(); stack.add(card); mStacks.add(stack); if (refresh) refresh(); } /** * Adds the card to last stack. * * @param card the card */ public void addCardToLastStack(Card card) { addCardToLastStack(card, false); } /** * Adds the card to last stack. * * @param card the card * @param refresh the refresh */ public void addCardToLastStack(Card card, boolean refresh) { if (mStacks.isEmpty()) { addCard(card, refresh); return; } int lastItemPos = mStacks.size() - 1; CardStack cardStack = (CardStack) mStacks.get(lastItemPos); cardStack.add(card); mStacks.set(lastItemPos, cardStack); if (refresh) refresh(); } /** * Adds the stack. * * @param stack the stack */ public void addStack(CardStack stack) { addStack(stack, false); } /** * Adds the stack. * * @param stack the stack * @param refresh the refresh */ public void addStack(CardStack stack, boolean refresh) { mStacks.add(stack); if (refresh) refresh(); } //suppress this error message to be able to use spaces in higher api levels /** * Refresh. */ @SuppressLint("NewApi") public void refresh() { if (mAdapter == null) { mAdapter = new StackAdapter(mContext, mStacks, mSwipeable); if (mListView != null) { mListView.setAdapter(mAdapter); } else if (mTableLayout != null) { TableRow tr = null; for (int i = 0; i < mAdapter.getCount(); i += mColumnNumber) { //add a new table row with the current context tr = (TableRow) new TableRow(mTableLayout.getContext()); tr.setOrientation(TableRow.HORIZONTAL); tr.setLayoutParams(new TableRow.LayoutParams(TableRow.LayoutParams.MATCH_PARENT, TableRow.LayoutParams.WRAP_CONTENT)); //add as many cards as the number of columns indicates per row for (int j = 0; j < mColumnNumber; j++) { if (i + j < mAdapter.getCount()) { View card = mAdapter.getView(i + j, null, tr); if(card.getLayoutParams() != null) { card.setLayoutParams(new TableRow.LayoutParams(card.getLayoutParams().width, card.getLayoutParams().height, 1f)); } else { card.setLayoutParams(new TableRow.LayoutParams(TableRow.LayoutParams.MATCH_PARENT, TableRow.LayoutParams.WRAP_CONTENT, 1f)); } tr.addView(card); } } mTableLayout.addView(tr); } if(tr != null) { //fill the empty space with spacers for (int j = mAdapter.getCount() % mColumnNumber; j > 0; j--) { View space = null; if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { space = new Space(tr.getContext()) ; } else { space = new View(tr.getContext()) ; } space.setLayoutParams(new TableRow.LayoutParams(TableRow.LayoutParams.MATCH_PARENT, TableRow.LayoutParams.WRAP_CONTENT, 1f)); tr.addView(space); } } } } else { mAdapter.setSwipeable(mSwipeable); // in case swipeable changed; mAdapter.setItems(mStacks); } } /** * Clear cards. */ public void clearCards() { mStacks = new ArrayList<AbstractCard>(); renderedCardsStacks = 0; refresh(); } /** * Sets the current stack title. * * @param title the new current stack title */ public void setCurrentStackTitle(String title) { CardStack cardStack = (CardStack) mStacks .get(getLastCardStackPosition()); cardStack.setTitle(title); } /** * Gets the on rendered listener. * * @return the on rendered listener */ public OnRenderedListener getOnRenderedListener() { return onRenderedListener; } /** * Sets the on rendered listener. * * @param onRenderedListener the new on rendered listener */ public void setOnRenderedListener(OnRenderedListener onRenderedListener) { this.onRenderedListener = onRenderedListener; } }