stacks) {
+ mStacks = stacks;
+ notifyDataSetChanged();
+ }
+
+ public void setSwipeable(boolean b) {
+ mSwipeable = b;
+ }
+
+ public void setItems(CardStack cardStack, int position) {
+ mStacks.set(position, cardStack);
+ }
+
+}
\ No newline at end of file
diff --git a/CardsUILib/src/com/fima/cardsui/SwipeDismissTouchListener.java b/CardsUILib/src/com/fima/cardsui/SwipeDismissTouchListener.java
new file mode 100644
index 0000000..34c62e6
--- /dev/null
+++ b/CardsUILib/src/com/fima/cardsui/SwipeDismissTouchListener.java
@@ -0,0 +1,290 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * 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.fima.cardsui;
+
+import static com.nineoldandroids.view.ViewHelper.setAlpha;
+import static com.nineoldandroids.view.ViewPropertyAnimator.animate;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewGroup;
+
+import com.nineoldandroids.animation.Animator;
+import com.nineoldandroids.animation.Animator.AnimatorListener;
+import com.nineoldandroids.animation.AnimatorListenerAdapter;
+import com.nineoldandroids.animation.ValueAnimator;
+import com.nineoldandroids.view.ViewHelper;
+
+/**
+ * A {@link android.view.View.OnTouchListener} that makes any {@link View}
+ * dismissable when the user swipes (drags her finger) horizontally across the
+ * view.
+ *
+ *
+ * For {@link android.widget.ListView} list items that don't manage their own touch events
+ * (i.e. you're using
+ * {@link android.widget.ListView#setOnItemClickListener(android.widget.AdapterView.OnItemClickListener)}
+ * or an equivalent listener on {@link android.app.ListActivity} or
+ * {@link android.app.ListFragment}, use {@link SwipeDismissListViewTouchListener} instead.
+ *
+ *
+ *
+ * Example usage:
+ *
+ *
+ *
+ * view.setOnTouchListener(new SwipeDismissTouchListener(view, null, // Optional
+ * // token/cookie
+ * // object
+ * new SwipeDismissTouchListener.OnDismissCallback() {
+ * public void onDismiss(View view, Object token) {
+ * parent.removeView(view);
+ * }
+ * }));
+ *
+ *
+ *
+ * This class Requires API level 12 or later due to use of
+ * {@link android.view.ViewPropertyAnimator}.
+ *
+ *
+ * @see SwipeDismissListViewTouchListener
+ */
+public class SwipeDismissTouchListener implements View.OnTouchListener {
+ // Cached ViewConfiguration and system-wide constant values
+ private int mSlop;
+ private int mMinFlingVelocity;
+ private int mMaxFlingVelocity;
+ private long mAnimationTime;
+
+ // Fixed properties
+ private View mView;
+ private OnDismissCallback mCallback;
+ private int mViewWidth = 1; // 1 and not 0 to prevent dividing by zero
+
+ // Transient properties
+ private float mDownX;
+ private boolean mSwiping;
+ private Object mToken;
+ private VelocityTracker mVelocityTracker;
+ private float mTranslationX;
+
+ /**
+ * The callback interface used by {@link SwipeDismissTouchListener} to
+ * inform its client about a successful dismissal of the view for which it
+ * was created.
+ */
+ public interface OnDismissCallback {
+ /**
+ * Called when the user has indicated they she would like to dismiss the
+ * view.
+ *
+ * @param view
+ * The originating {@link View} to be dismissed.
+ * @param token
+ * The optional token passed to this object's constructor.
+ */
+ public void onDismiss(View view, Object token);
+ }
+
+ /**
+ * Constructs a new swipe-to-dismiss touch listener for the given view.
+ *
+ * @param view
+ * The view to make dismissable.
+ * @param token
+ * An optional token/cookie object to be passed through to the
+ * callback.
+ * @param callback
+ * The callback to trigger when the user has indicated that she
+ * would like to dismiss this view.
+ */
+ public SwipeDismissTouchListener(View view, Object token,
+ OnDismissCallback callback) {
+ ViewConfiguration vc = ViewConfiguration.get(view.getContext());
+ mSlop = vc.getScaledTouchSlop() * 2;
+ mMinFlingVelocity = vc.getScaledMinimumFlingVelocity();
+ mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity();
+ mAnimationTime = view.getContext().getResources()
+ .getInteger(android.R.integer.config_shortAnimTime);
+ mView = view;
+ mToken = token;
+ mCallback = callback;
+ }
+
+ @Override
+ public boolean onTouch(View view, MotionEvent motionEvent) {
+ // offset because the view is translated during swipe
+ motionEvent.offsetLocation(mTranslationX, 0);
+
+ if (mViewWidth < 2) {
+ mViewWidth = mView.getWidth();
+ }
+
+ switch (motionEvent.getActionMasked()) {
+ case MotionEvent.ACTION_DOWN: {
+ // TODO: ensure this is a finger, and set a flag
+ mDownX = motionEvent.getRawX();
+ mVelocityTracker = VelocityTracker.obtain();
+ mVelocityTracker.addMovement(motionEvent);
+ //view.onTouchEvent(motionEvent);
+ return false;
+ }
+
+ case MotionEvent.ACTION_UP: {
+ if (mVelocityTracker == null) {
+ break;
+ }
+
+ float deltaX = motionEvent.getRawX() - mDownX;
+ mVelocityTracker.addMovement(motionEvent);
+ mVelocityTracker.computeCurrentVelocity(1000);
+ float velocityX = Math.abs(mVelocityTracker.getXVelocity());
+ float velocityY = Math.abs(mVelocityTracker.getYVelocity());
+ boolean dismiss = false;
+ boolean dismissRight = false;
+ if (Math.abs(deltaX) > mViewWidth / 2) {
+ dismiss = true;
+ dismissRight = deltaX > 0;
+ } else if (mMinFlingVelocity <= velocityX
+ && velocityX <= mMaxFlingVelocity && velocityY < velocityX) {
+ dismiss = true;
+ dismissRight = mVelocityTracker.getXVelocity() > 0;
+ }
+ if (dismiss) {
+ // dismiss
+ animate(mView)
+ .translationX(dismissRight ? mViewWidth : -mViewWidth)
+ .alpha(0).setDuration(mAnimationTime)
+ .setListener(new AnimatorListener() {
+
+ @Override
+ public void onAnimationStart(Animator arg0) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void onAnimationRepeat(Animator arg0) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void onAnimationEnd(Animator arg0) {
+ performDismiss();
+
+ }
+
+ @Override
+ public void onAnimationCancel(Animator arg0) {
+ // TODO Auto-generated method stub
+
+ }
+ });
+ } else {
+ // cancel
+ animate(mView).translationX(0).alpha(1)
+ .setDuration(mAnimationTime).setListener(null);
+
+ }
+ mVelocityTracker.recycle();
+ mVelocityTracker = null;
+ mTranslationX = 0;
+ mDownX = 0;
+ mSwiping = false;
+ break;
+ }
+
+ case MotionEvent.ACTION_MOVE: {
+ if (mVelocityTracker == null) {
+ break;
+ }
+
+ mVelocityTracker.addMovement(motionEvent);
+ float deltaX = motionEvent.getRawX() - mDownX;
+ if (Math.abs(deltaX) > mSlop) {
+ mSwiping = true;
+ mView.getParent().requestDisallowInterceptTouchEvent(true);
+
+ // Cancel listview's touch
+ MotionEvent cancelEvent = MotionEvent.obtain(motionEvent);
+ cancelEvent
+ .setAction(MotionEvent.ACTION_CANCEL
+ | (motionEvent.getActionIndex() << MotionEvent.ACTION_POINTER_INDEX_SHIFT));
+ mView.onTouchEvent(cancelEvent);
+ cancelEvent.recycle();
+ }
+
+ if (mSwiping) {
+ mTranslationX = deltaX;
+ ViewHelper.setTranslationX(mView, deltaX);
+
+ // TODO: use an ease-out interpolator or such
+ setAlpha(
+ mView,
+ Math.max(
+ 0f,
+ Math.min(1f, 1f - 2f * Math.abs(deltaX)
+ / mViewWidth)));
+ return true;
+ }
+ break;
+ }
+ }
+ return false;
+ }
+
+ private void performDismiss() {
+ // Animate the dismissed view to zero-height and then fire the dismiss
+ // callback.
+ // This triggers layout on each animation frame; in the future we may
+ // want to do something
+ // smarter and more performant.
+
+ final ViewGroup.LayoutParams lp = mView.getLayoutParams();
+ final int originalHeight = mView.getHeight();
+
+ ValueAnimator animator = ValueAnimator.ofInt(originalHeight, 1)
+ .setDuration(mAnimationTime);
+
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mCallback.onDismiss(mView, mToken);
+ // Reset view presentation
+ setAlpha(mView, 1f);
+ ViewHelper.setTranslationX(mView, 0);
+ // mView.setAlpha(1f);
+ // mView.setTranslationX(0);
+ lp.height = originalHeight;
+ mView.setLayoutParams(lp);
+ }
+ });
+
+ animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator valueAnimator) {
+ lp.height = (Integer) valueAnimator.getAnimatedValue();
+ mView.setLayoutParams(lp);
+ }
+ });
+
+ animator.start();
+ }
+}
diff --git a/CardsUILib/src/com/fima/cardsui/Utils.java b/CardsUILib/src/com/fima/cardsui/Utils.java
new file mode 100644
index 0000000..719d596
--- /dev/null
+++ b/CardsUILib/src/com/fima/cardsui/Utils.java
@@ -0,0 +1,40 @@
+package com.fima.cardsui;
+
+import android.content.Context;
+import android.util.DisplayMetrics;
+
+public class Utils {
+
+
+
+ /**
+ * This method converts device specific pixels to device independent pixels.
+ *
+ * @param px
+ * A value in px (pixels) unit. Which we need to convert into db
+ * @param context
+ * Context to get resources and device specific display metrics
+ * @return A float value to represent db equivalent to px value
+ */
+ public float convertPixelsToDp(Context ctx, float px) {
+ DisplayMetrics metrics = ctx.getResources().getDisplayMetrics();
+ float dp = px / (metrics.densityDpi / 160f);
+ return dp;
+
+ }
+
+ public static int convertDpToPixelInt(Context context, float dp) {
+
+ DisplayMetrics metrics = context.getResources().getDisplayMetrics();
+ int px = (int) (dp * (metrics.densityDpi / 160f));
+ return px;
+ }
+
+ public static float convertDpToPixel(Context context, float dp) {
+
+ DisplayMetrics metrics = context.getResources().getDisplayMetrics();
+ float px = (float) (dp * (metrics.densityDpi / 160f));
+ return px;
+ }
+
+}
diff --git a/CardsUILib/src/com/fima/cardsui/objects/AbstractCard.java b/CardsUILib/src/com/fima/cardsui/objects/AbstractCard.java
new file mode 100644
index 0000000..28ea42e
--- /dev/null
+++ b/CardsUILib/src/com/fima/cardsui/objects/AbstractCard.java
@@ -0,0 +1,72 @@
+package com.fima.cardsui.objects;
+
+import android.content.Context;
+import android.view.View;
+
+public abstract class AbstractCard {
+
+ protected int image;
+ protected String description, color, titleColor, desc, title, titlePlay;
+ protected Boolean hasOverflow, isClickable;
+ protected int imageRes;
+ protected Object data;
+
+ public abstract View getView(Context context);
+
+ public abstract View getView(Context context, boolean swipable);
+
+ public String getTitle() {
+ return title;
+ }
+
+ public String getTitlePlay() {
+ return titlePlay;
+ }
+
+ public String getDesc() {
+ return desc;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public int getImage() {
+ return image;
+ }
+
+ public String getColor() {
+ return color;
+ }
+
+ public String getTitleColor() {
+ return titleColor;
+ }
+
+ public Boolean getHasOverflow() {
+ return hasOverflow;
+ }
+
+ public Boolean getIsClickable() {
+ return isClickable;
+ }
+
+ public int getImageRes() {
+ return imageRes;
+ }
+
+ /**
+ * @return the data
+ */
+ public Object getData() {
+ return data;
+ }
+
+ /**
+ * @param data
+ * the data to set
+ */
+ public void setData(Object data) {
+ this.data = data;
+ }
+}
diff --git a/CardsUILib/src/com/fima/cardsui/objects/Card.java b/CardsUILib/src/com/fima/cardsui/objects/Card.java
new file mode 100644
index 0000000..1c05c9c
--- /dev/null
+++ b/CardsUILib/src/com/fima/cardsui/objects/Card.java
@@ -0,0 +1,205 @@
+package com.fima.cardsui.objects;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.FrameLayout;
+import android.widget.LinearLayout;
+
+import com.fima.cardsui.R;
+import com.fima.cardsui.Utils;
+
+public abstract class Card extends AbstractCard {
+
+ protected View mCardLayout;
+ private OnCardSwiped onCardSwipedListener;
+ private OnClickListener mListener;
+
+ public Card() {
+
+ }
+
+ public Card(String title) {
+ this.title = title;
+ }
+
+ public Card(String title, String desc) {
+ this.title = title;
+ this.desc = desc;
+ }
+
+ public Card(String title, int image) {
+ this.title = title;
+ this.image = image;
+ }
+
+ public Card(String title, String desc, int image) {
+ this.title = title;
+ this.desc = desc;
+ this.image = image;
+ }
+
+ public Card(String titlePlay, String description, String color,
+ String titleColor, Boolean hasOverflow, Boolean isClickable) {
+
+ this.titlePlay = titlePlay;
+ this.description = description;
+ this.color = color;
+ this.titleColor = titleColor;
+ this.hasOverflow = hasOverflow;
+ this.isClickable = isClickable;
+ }
+
+ public Card(String titlePlay, String description, int imageRes, String titleColor, Boolean hasOverflow,
+ Boolean isClickable) {
+
+ this.titlePlay = titlePlay;
+ this.description = description;
+ this.titleColor = titleColor;
+ this.hasOverflow = hasOverflow;
+ this.isClickable = isClickable;
+ this.imageRes = imageRes;
+ }
+
+ @Override
+ public View getView(Context context, boolean swipable) {
+ return getView(context, false);
+ }
+
+ @Override
+ public View getView(Context context) {
+
+ View view = LayoutInflater.from(context).inflate(getCardLayout(), null);
+
+ mCardLayout = view;
+
+ try {
+ ((FrameLayout) view.findViewById(R.id.cardContent))
+ .addView(getCardContent(context));
+ } catch (NullPointerException e) {
+ e.printStackTrace();
+ }
+
+ // ((TextView) view.findViewById(R.id.title)).setText(this.title);
+
+ LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
+ LinearLayout.LayoutParams.MATCH_PARENT,
+ LinearLayout.LayoutParams.WRAP_CONTENT);
+ int bottom = Utils.convertDpToPixelInt(context, 12);
+ lp.setMargins(0, 0, 0, bottom);
+
+ view.setLayoutParams(lp);
+
+ return view;
+ }
+
+ public View getViewLast(Context context) {
+
+ View view = LayoutInflater.from(context).inflate(getLastCardLayout(),
+ null);
+
+ mCardLayout = view;
+
+ try {
+ ((FrameLayout) view.findViewById(R.id.cardContent))
+ .addView(getCardContent(context));
+ } catch (NullPointerException e) {
+ e.printStackTrace();
+ }
+
+ // ((TextView) view.findViewById(R.id.title)).setText(this.title);
+
+ LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
+ LinearLayout.LayoutParams.MATCH_PARENT,
+ LinearLayout.LayoutParams.WRAP_CONTENT);
+ int bottom = Utils.convertDpToPixelInt(context, 12);
+ lp.setMargins(0, 0, 0, bottom);
+
+ view.setLayoutParams(lp);
+
+ return view;
+ }
+
+ public View getViewFirst(Context context) {
+
+ View view = LayoutInflater.from(context).inflate(getFirstCardLayout(),
+ null);
+
+ mCardLayout = view;
+
+ try {
+ ((FrameLayout) view.findViewById(R.id.cardContent))
+ .addView(getCardContent(context));
+ } catch (NullPointerException e) {
+ e.printStackTrace();
+ }
+
+ // ((TextView) view.findViewById(R.id.title)).setText(this.title);
+
+ LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
+ LinearLayout.LayoutParams.MATCH_PARENT,
+ LinearLayout.LayoutParams.WRAP_CONTENT);
+ int bottom = Utils.convertDpToPixelInt(context, 12);
+ lp.setMargins(0, 0, 0, bottom);
+
+ view.setLayoutParams(lp);
+
+ return view;
+ }
+
+ public abstract View getCardContent(Context context);
+
+ public OnClickListener getClickListener() {
+ return mListener;
+ }
+
+ public void setOnClickListener(OnClickListener listener) {
+ mListener = listener;
+ }
+
+ public void OnSwipeCard() {
+ if (onCardSwipedListener != null)
+ onCardSwipedListener.onCardSwiped(this, mCardLayout);
+ // TODO: find better implementation to get card-object's used content
+ // layout (=> implementing getCardContent());
+ }
+
+ public OnCardSwiped getOnCardSwipedListener() {
+ return onCardSwipedListener;
+ }
+
+ public void setOnCardSwipedListener(OnCardSwiped onEpisodeSwipedListener) {
+ this.onCardSwipedListener = onEpisodeSwipedListener;
+ }
+
+ protected int getCardLayout() {
+ return R.layout.item_card;
+ }
+
+ protected int getId() {
+ return R.id.cardContent;
+ }
+
+ protected int getLastCardLayout() {
+ return R.layout.item_card_empty_last;
+ }
+
+ protected int getFirstCardLayout() {
+ return R.layout.item_play_card_empty_first;
+ }
+
+ public interface OnCardSwiped {
+ public void onCardSwiped(Card card, View layout);
+ }
+
+ /**
+ * Attempt to reuse convertCardView. Should not modify convertCardView if it's
+ * not compatible. The implementer should check the card content part and
+ * verify that it matches.
+ * @param convertCardView the view to convert, with root Id equal to Card.getId()
+ * @return true on success, false if not compatible
+ */
+ public abstract boolean convert(View convertCardView);
+
+}
\ No newline at end of file
diff --git a/CardsUILib/src/com/fima/cardsui/objects/CardFactory.java b/CardsUILib/src/com/fima/cardsui/objects/CardFactory.java
new file mode 100644
index 0000000..4216492
--- /dev/null
+++ b/CardsUILib/src/com/fima/cardsui/objects/CardFactory.java
@@ -0,0 +1,135 @@
+package com.fima.cardsui.objects;
+
+import java.lang.reflect.Field;
+import java.util.HashMap;
+
+import android.util.Log;
+
+/**
+ * Contains method(s) to create a {@link Card}-family object from its
+ * serializable model.
+ *
+ *
+ * Note that any similarities to Card Factory Ltd
+ * are purely accidental.
+ *
+ * @author FLamparski
+ *
+ */
+public class CardFactory {
+ /**
+ * Uses Reflection to create a new {@link AbstractCard} from the given
+ * {@link CardModel} by copying fields.
+ *
+ * @param model
+ * The {@link CardModel} to "inflate" into an
+ * {@link AbstractCard}
+ * @return An {@link AbstractCard} that matches the model data
+ * @throws InstantiationException
+ * Thrown when the class specified by the model cannot be
+ * instantiated (no default ctor).
+ * @throws IllegalAccessException
+ * Thrown if I missed a setAccessible(true) somewhere, or if the
+ * default ctor for the target class is not visible.
+ */
+ public static AbstractCard createCard(CardModel model)
+ throws InstantiationException, IllegalAccessException {
+ /*
+ * Instantiate a new object (must be AbstractCard or extend it, makes
+ * sense for the CardsUI mechanic) from a Class descriptor.
+ */
+ AbstractCard newCard = model.cardClass.newInstance();
+ Log.i("CardFactory", "Creating a new card! We're making a new "
+ + model.cardClass.getName());
+
+ /*
+ * We will also need the Class descriptor for the object we just created
+ * in order to access its fields.
+ */
+ Class extends AbstractCard> newCardClazz = newCard.getClass();
+
+ /*
+ * Java objects do not flatten their hierarchy at runtime (that's good),
+ * which means that we actually need to collect all fields from all the
+ * ancestors. Here, we collect them into a HashMap for convenience.
+ */
+ HashMap destinationFields = new HashMap();
+ Class> clazzUnderInspection = newCardClazz;
+ while (clazzUnderInspection != null) {
+ /*
+ * Get all fields for the current point in the hierarchy, and
+ * collect them into the HashMap.
+ */
+ Field[] clazzFields = clazzUnderInspection.getDeclaredFields();
+ for (int i = 0; i < clazzFields.length; i++) {
+ Field f = clazzFields[i];
+ destinationFields.put(f.getName(), f);
+ }
+ /*
+ * Okay, now examine the ancestor (for java.lang.Object it is null,
+ * which means the loop will exit).
+ */
+ clazzUnderInspection = clazzUnderInspection.getSuperclass();
+ }
+
+ /*
+ * Obtain a list of fields within the model. As most of them match those
+ * in AbstractCard, the card's content will be preserved. Since the
+ * model inherits only from Object, all fields collected here will be
+ * what we need.
+ */
+ Field[] sourceFields = model.getClass().getDeclaredFields();
+
+ /*
+ * Now iterate over the fields in the model.
+ */
+ for (int i = 0; i < sourceFields.length; i++) {
+ // Just a reference for the field we're copying in this pass
+ Field curField = sourceFields[i];
+
+ /*
+ * This is to prevent IllegalAccessExceptions when accessing the
+ * field. Yes, this violates the visibility set by the field
+ * declarations, but that's the whole point of this routine.
+ */
+ curField.setAccessible(true);
+
+ String fieldName = curField.getName();
+
+ Log.d("CardFactory", " > Now copying field: " + fieldName);
+
+ Field destField = destinationFields.get(fieldName);
+
+ if (destField != null) {
+ /*
+ * We need to get the specific field that matches the one in the
+ * model and set it to the same value as the corresponding field
+ * in the model.
+ */
+ destField.setAccessible(true); // "Trust me."
+ destField.set(newCard, curField.get(model));
+ Log.d("CardFactory", String.format(
+ " > Field %s (= %s) -> Field %s (=%s)", curField,
+ curField.get(model), destField.getName(),
+ destField.get(newCard)));
+ } else {
+ /*
+ * We have encountered a field (CardModel.data,
+ * CardModel.cardClass, ...) in the model that does not exist in
+ * the view that can represent it, so we just skip it. Yes, this
+ * breaks the "for loop is fixed" convention, but this really is
+ * the easiest way.
+ *
+ * "Go to Next Iteration. Go directly to Next Iteration. Do not
+ * pass Go. Do not collect $200."
+ */
+ Log.d("CardFactory", String.format(
+ " > Skipping over an unmapped Field %s/%s (= %s)",
+ model.cardClass.getName(), curField,
+ curField.get(model)));
+ }
+ }
+
+ return newCard;
+ }
+}
diff --git a/CardsUILib/src/com/fima/cardsui/objects/CardModel.java b/CardsUILib/src/com/fima/cardsui/objects/CardModel.java
new file mode 100644
index 0000000..f31fcb4
--- /dev/null
+++ b/CardsUILib/src/com/fima/cardsui/objects/CardModel.java
@@ -0,0 +1,296 @@
+package com.fima.cardsui.objects;
+
+import java.io.Serializable;
+
+/**
+ * A card model that represents all the basic information about a {@link Card}
+ * (actually, it's a concrete copy of {@link AbstractCard} that's
+ * {@link Serializable}). CardModel objects can be used to store information
+ * about cards across configuration changes, or even saved to USB storage.
+ *
+ *
+ * To turn a {@link CardModel} into a Card, use {@link CardFactory}.
+ *
+ * @author FLamparski
+ * @see {@link AbstractCard}, {@link CardFactory}
+ */
+public class CardModel implements Serializable {
+ private static final long serialVersionUID = 0xDEADBEEFl;
+ protected int image;
+ protected String description, color, titleColor, desc, title, titlePlay;
+ protected Boolean hasOverflow, isClickable;
+ protected int imageRes;
+
+ protected Class extends AbstractCard> cardClass;
+ protected Object data;
+
+ /**
+ * A very minimal and customizable constructor
+ *
+ * @param cardClass
+ */
+ public CardModel(Class extends AbstractCard> cardClass) {
+ this.cardClass = cardClass;
+ }
+
+ /**
+ * For basic cards
+ *
+ * @param description
+ * @param title
+ */
+ public CardModel(String description, String title,
+ Class extends AbstractCard> cardClass) {
+ this.description = description;
+ this.desc = description;
+ this.title = title;
+ this.cardClass = cardClass;
+ }
+
+ /**
+ * For basic cards w/ data
+ *
+ * @param description
+ * @param title
+ * @param data
+ */
+ public CardModel(String description, String title, Object data,
+ Class extends AbstractCard> cardClass) {
+ this.description = description;
+ this.desc = description;
+ this.title = title;
+ this.data = data;
+ this.cardClass = cardClass;
+ }
+
+ /**
+ * For Play cards
+ *
+ * @param titlePlay
+ * @param description
+ * @param color
+ * @param titleColor
+ * @param hasOverflow
+ * @param isClickable
+ */
+ public CardModel(String titlePlay, String description, String color,
+ String titleColor, Boolean hasOverflow, Boolean isClickable,
+ Class extends AbstractCard> cardClass) {
+ this.titlePlay = titlePlay;
+ this.description = description;
+ this.color = color;
+ this.titleColor = titleColor;
+ this.hasOverflow = hasOverflow;
+ this.isClickable = isClickable;
+ this.cardClass = cardClass;
+ }
+
+ /**
+ * For Play cards w/ data
+ *
+ * @param titlePlay
+ * @param description
+ * @param color
+ * @param titleColor
+ * @param hasOverflow
+ * @param isClickable
+ * @param data
+ */
+ public CardModel(String titlePlay, String description, String color,
+ String titleColor, Boolean hasOverflow, Boolean isClickable,
+ Object data, Class extends AbstractCard> cardClass) {
+ this.titlePlay = titlePlay;
+ this.description = description;
+ this.color = color;
+ this.titleColor = titleColor;
+ this.hasOverflow = hasOverflow;
+ this.isClickable = isClickable;
+ this.data = data;
+ this.cardClass = cardClass;
+ }
+
+ /**
+ * The full monty constructor.
+ *
+ * @param image
+ * @param description
+ * @param color
+ * @param titleColor
+ * @param desc
+ * @param title
+ * @param titlePlay
+ * @param hasOverflow
+ * @param isClickable
+ * @param imageRes
+ * @param cardClass
+ * @param data
+ */
+ public CardModel(int image, String description, String color,
+ String titleColor, String desc, String title, String titlePlay,
+ Boolean hasOverflow, Boolean isClickable, int imageRes,
+ Class extends AbstractCard> cardClass, Object data) {
+ this.image = image;
+ this.description = description;
+ this.color = color;
+ this.titleColor = titleColor;
+ this.desc = desc;
+ this.title = title;
+ this.titlePlay = titlePlay;
+ this.hasOverflow = hasOverflow;
+ this.isClickable = isClickable;
+ this.imageRes = imageRes;
+ this.cardClass = cardClass;
+ this.data = data;
+ }
+
+ public String getColor() {
+ return color;
+ }
+
+ /**
+ * @return Arbitrary data associated with this card model
+ */
+ public Object getData() {
+ return data;
+ }
+
+ public String getDesc() {
+ return desc;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public Boolean getHasOverflow() {
+ return hasOverflow;
+ }
+
+ public int getImage() {
+ return image;
+ }
+
+ public int getImageRes() {
+ return imageRes;
+ }
+
+ public Boolean getIsClickable() {
+ return isClickable;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public String getTitleColor() {
+ return titleColor;
+ }
+
+ public String getTitlePlay() {
+ return titlePlay;
+ }
+
+ /**
+ * @return the card's type
+ */
+ public Class extends AbstractCard> getType() {
+ return cardClass;
+ }
+
+ /**
+ * @param color
+ * the color to set
+ */
+ public void setColor(String color) {
+ this.color = color;
+ }
+
+ /**
+ * @param Arbitrary
+ * data associated with this card model (POJOs plx, no context
+ * leaks)
+ */
+ public void setData(Object data) {
+ this.data = data;
+ }
+
+ /**
+ * @param desc
+ * the desc to set
+ */
+ public void setDesc(String desc) {
+ this.desc = desc;
+ }
+
+ /**
+ * @param description
+ * the description to set
+ */
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ /**
+ * @param hasOverflow
+ * the hasOverflow to set
+ */
+ public void setHasOverflow(Boolean hasOverflow) {
+ this.hasOverflow = hasOverflow;
+ }
+
+ /**
+ * @param image
+ * the image to set
+ */
+ public void setImage(int image) {
+ this.image = image;
+ }
+
+ /**
+ * @param imageRes
+ * the imageRes to set
+ */
+ public void setImageRes(int imageRes) {
+ this.imageRes = imageRes;
+ }
+
+ /**
+ * @param isClickable
+ * the isClickable to set
+ */
+ public void setIsClickable(Boolean isClickable) {
+ this.isClickable = isClickable;
+ }
+
+ /**
+ * @param title
+ * the title to set
+ */
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ /**
+ * @param titleColor
+ * the titleColor to set
+ */
+ public void setTitleColor(String titleColor) {
+ this.titleColor = titleColor;
+ }
+
+ /**
+ * @param titlePlay
+ * the titlePlay to set
+ */
+ public void setTitlePlay(String titlePlay) {
+ this.titlePlay = titlePlay;
+ }
+
+ /**
+ * @param type
+ * the type to set
+ */
+ public void setType(Class extends AbstractCard> type) {
+ this.cardClass = type;
+ }
+}
diff --git a/CardsUILib/src/com/fima/cardsui/objects/CardStack.java b/CardsUILib/src/com/fima/cardsui/objects/CardStack.java
new file mode 100644
index 0000000..bd3b874
--- /dev/null
+++ b/CardsUILib/src/com/fima/cardsui/objects/CardStack.java
@@ -0,0 +1,439 @@
+package com.fima.cardsui.objects;
+
+import java.util.ArrayList;
+
+import android.content.Context;
+import android.graphics.Color;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import com.fima.cardsui.R;
+import com.fima.cardsui.StackAdapter;
+import com.fima.cardsui.SwipeDismissTouchListener;
+import com.fima.cardsui.SwipeDismissTouchListener.OnDismissCallback;
+import com.fima.cardsui.Utils;
+import com.nineoldandroids.animation.Animator;
+import com.nineoldandroids.animation.Animator.AnimatorListener;
+import com.nineoldandroids.animation.ObjectAnimator;
+
+public class CardStack extends AbstractCard {
+ private static final float _12F = 12f;
+ private static final float _45F = 45f;
+ private static final String NINE_OLD_TRANSLATION_Y = "translationY";
+ private ArrayList cards;
+ private String title, stackTitleColor;
+
+ private StackAdapter mAdapter;
+ private int mPosition;
+ private Context mContext;
+ private CardStack mStack;
+
+ public CardStack() {
+ cards = new ArrayList();
+ mStack = this;
+ }
+
+ public CardStack(String title) {
+ cards = new ArrayList();
+ mStack = this;
+
+ setTitle(title);
+ }
+ public ArrayList getCards() {
+ return cards;
+ }
+
+ public void add(Card newCard) {
+ cards.add(newCard);
+
+ }
+
+ @Override
+ public View getView(Context context) {
+ return getView(context, null, false);
+ }
+
+ @Override
+ public View getView(Context context, boolean swipable) {
+ return getView(context, null, swipable);
+ }
+
+ public View getView(Context context, View convertView, boolean swipable) {
+
+ mContext = context;
+
+ // try to recycle views if possible
+ Log.d("CardStack", String.format("Checking to recycle view. convertView is %s", (convertView == null ? "null" : "not null")));
+ if (convertView != null) {
+ Log.d("CardStack", String.format("Checking types. convertView is %d, need %d", convertView.getId(), R.id.stackRoot));
+ // can only convert something with the correct root element
+ if (convertView.getId() == R.id.stackRoot)
+ if (convert(convertView))
+ return convertView;
+ }
+
+ final View view = LayoutInflater.from(context).inflate(
+ R.layout.item_stack, null);
+
+ assert view != null;
+ final RelativeLayout container = (RelativeLayout) view
+ .findViewById(R.id.stackContainer);
+ final TextView title = (TextView) view.findViewById(R.id.stackTitle);
+
+ if (!TextUtils.isEmpty(this.title)) {
+ if (stackTitleColor == null)
+ stackTitleColor = context.getResources().getString(R.color.card_title_text);
+
+ title.setTextColor(Color.parseColor(stackTitleColor));
+ title.setText(this.title);
+ title.setVisibility(View.VISIBLE);
+ }
+
+ final int cardsArraySize = cards.size();
+ final int lastCardPosition = cardsArraySize - 1;
+
+ Card card;
+ View cardView;
+ for (int i = 0; i < cardsArraySize; i++) {
+ card = cards.get(i);
+ RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(
+ RelativeLayout.LayoutParams.MATCH_PARENT,
+ RelativeLayout.LayoutParams.WRAP_CONTENT);
+
+ int topPx = 0;
+
+ // handle the view
+ if (i == 0) {
+ cardView = card.getViewFirst(context);
+ }
+ else if (i == lastCardPosition) {
+ cardView = card.getViewLast(context);
+ }
+ else {
+ cardView = card.getView(context);
+ }
+
+ // handle the listener
+ if (i == lastCardPosition) {
+ cardView.setOnClickListener(card.getClickListener());
+ }
+ else {
+ cardView.setOnClickListener(getClickListener(this, container, i));
+ }
+
+ if (i > 0) {
+ float dp = (_45F * i) - _12F;
+ topPx = Utils.convertDpToPixelInt(context, dp);
+ }
+
+ lp.setMargins(0, topPx, 0, 0);
+
+ cardView.setLayoutParams(lp);
+
+ if (swipable) {
+ cardView.setOnTouchListener(new SwipeDismissTouchListener(
+ cardView, card, new OnDismissCallback() {
+
+ @Override
+ public void onDismiss(View view, Object token) {
+ Card c = (Card) token;
+ // call onCardSwiped() listener
+ c.OnSwipeCard();
+ cards.remove(c);
+
+ mAdapter.setItems(mStack, getPosition());
+
+ // refresh();
+ mAdapter.notifyDataSetChanged();
+
+ }
+ }));
+ }
+
+ container.addView(cardView);
+ }
+
+ return view;
+ }
+
+ /**
+ * Attempt to modify the convertView instead of inflating a new View for this CardStack.
+ * If convertView isn't compatible, it isn't modified.
+ * @param convertView view to try reusing
+ * @return true on success, false if the convertView is not compatible
+ */
+ private boolean convert(View convertView) {
+ // only convert singleton stacks
+ if (cards.size() != 1) {
+ Log.d("CardStack", "Can't convert view: num cards is " + cards.size());
+ return false;
+ }
+
+ RelativeLayout container = (RelativeLayout) convertView.findViewById(R.id.stackContainer);
+ if (container == null) {
+ Log.d("CardStack", "Can't convert view: can't find stackContainer");
+ return false;
+ }
+
+ if (container.getChildCount() != 1) {
+ Log.d("CardStack", "Can't convert view: child count is " + container.getChildCount());
+ return false;
+ }
+
+ // check to see if they're compatible Card types
+ Card card = cards.get(0);
+ View convertCardView = container.getChildAt(0);
+
+ if (convertCardView == null || convertCardView.getId() != card.getId()) {
+ Log.d("CardStack", String.format("Can't convert view: child Id is 0x%x, card Id is 0x%x", convertCardView != null ? convertCardView.getId() : 0, card.getId()));
+ return false;
+ }
+
+ if (card.convert(convertCardView))
+ return true;
+
+ return false;
+ }
+
+ public Card remove(int index) {
+ return cards.remove(index);
+ }
+
+ public Card get(int i) {
+ return cards.get(i);
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public String setColor(String color) {
+ return this.stackTitleColor = color;
+ }
+
+ private OnClickListener getClickListener(final CardStack cardStack,
+ final RelativeLayout container, final int index) {
+ return new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+
+ // init views array
+ View[] views = new View[container.getChildCount()];
+
+ for (int i = 0; i < views.length; i++) {
+ views[i] = container.getChildAt(i);
+
+ }
+
+ int last = views.length - 1;
+
+ if (index != last) {
+
+ if (index == 0) {
+ onClickFirstCard(cardStack, container, index, views);
+ } else if (index < last) {
+ onClickOtherCard(cardStack, container, index, views,
+ last);
+ }
+
+ }
+
+ }
+
+ public void onClickFirstCard(final CardStack cardStack,
+ final RelativeLayout frameLayout, final int index,
+ View[] views) {
+ // run through all the cards
+ for (int i = 0; i < views.length; i++) {
+ ObjectAnimator anim = null;
+
+ if (i == 0) {
+ // the first goes all the way down
+ float downFactor = 0;
+ if (views.length > 2) {
+ downFactor = convertDpToPixel((_45F)
+ * (views.length - 1) - 1);
+ } else {
+ downFactor = convertDpToPixel(_45F);
+ }
+
+ anim = ObjectAnimator.ofFloat(views[i],
+ NINE_OLD_TRANSLATION_Y, 0, downFactor);
+ anim.addListener(getAnimationListener(cardStack,
+ frameLayout, index, views[index]));
+
+ } else if (i == 1) {
+ // the second goes up just a bit
+
+ float upFactor = convertDpToPixel(-17f);
+ anim = ObjectAnimator.ofFloat(views[i],
+ NINE_OLD_TRANSLATION_Y, 0, upFactor);
+
+ } else {
+ // the rest go up by one card
+ float upFactor = convertDpToPixel(-1 * _45F);
+ anim = ObjectAnimator.ofFloat(views[i],
+ NINE_OLD_TRANSLATION_Y, 0, upFactor);
+ }
+
+ if (anim != null)
+ anim.start();
+
+ }
+ }
+
+ public void onClickOtherCard(final CardStack cardStack,
+ final RelativeLayout frameLayout, final int index,
+ View[] views, int last) {
+ // if clicked card is in middle
+ for (int i = index; i <= last; i++) {
+ // run through the cards from the clicked position
+ // and on until the end
+ ObjectAnimator anim = null;
+
+ if (i == index) {
+ // the selected card goes all the way down
+ float downFactor = convertDpToPixel(_45F * (last - i)
+ + _12F);
+ anim = ObjectAnimator.ofFloat(views[i],
+ NINE_OLD_TRANSLATION_Y, 0, downFactor);
+ anim.addListener(getAnimationListener(cardStack,
+ frameLayout, index, views[index]));
+ } else {
+ // the rest go up by one
+ float upFactor = convertDpToPixel(_45F * -1);
+ anim = ObjectAnimator.ofFloat(views[i],
+ NINE_OLD_TRANSLATION_Y, 0, upFactor);
+ }
+
+ if (anim != null)
+ anim.start();
+ }
+ }
+ };
+ }
+
+ protected float convertDpToPixel(float dp) {
+
+ return Utils.convertDpToPixel(mContext, dp);
+ }
+
+ private AnimatorListener getAnimationListener(final CardStack cardStack,
+ final RelativeLayout frameLayout, final int index,
+ final View clickedCard) {
+ return new AnimatorListener() {
+
+ @Override
+ public void onAnimationStart(Animator animation) {
+ if (index == 0) {
+
+ View newFirstCard = frameLayout.getChildAt(1);
+ handleFirstCard(newFirstCard);
+
+ // clickedCard.setBackgroundResource(com.fima.cardsui.R.drawable.);
+
+ // newFirstCard.setBackgroundResource(com.fima.cardsui.R.drawable.card_background);
+ // FrameLayout.LayoutParams lp = new
+ // FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT,
+ // FrameLayout.LayoutParams.WRAP_CONTENT);
+ //
+ // if (index > 0) {
+ // top = convertDpToPixelInt(8) + (convertDpToPixelInt(36f)
+ // * index);
+ // bottom = 24;
+ //
+ // }
+ //
+ // else if (0 == index) {
+ // top = 2 * convertDpToPixelInt(8) +
+ // convertDpToPixelInt(1);
+ // bottom = convertDpToPixelInt(12);
+ // }
+ //
+ // lp.setMargins(0, top, 0, bottom);
+ //
+ // clickedCard.setLayoutParams(lp);
+ // clickedCard.setPadding(0, convertDpToPixelInt(20), 0, 0);
+
+ } else {
+ clickedCard
+ .setBackgroundResource(com.fima.cardsui.R.drawable.card_background);
+ }
+ frameLayout.removeView(clickedCard);
+ frameLayout.addView(clickedCard);
+
+ }
+
+ private void handleFirstCard(View newFirstCard) {
+ newFirstCard
+ .setBackgroundResource(com.fima.cardsui.R.drawable.card_background);
+ RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(
+ RelativeLayout.LayoutParams.MATCH_PARENT,
+ RelativeLayout.LayoutParams.WRAP_CONTENT);
+
+ int top = 0;
+ int bottom = 0;
+
+ top = 2 * Utils.convertDpToPixelInt(mContext, 8)
+ + Utils.convertDpToPixelInt(mContext, 1);
+ bottom = Utils.convertDpToPixelInt(mContext, 12);
+
+ lp.setMargins(0, top, 0, bottom);
+ newFirstCard.setLayoutParams(lp);
+ newFirstCard.setPadding(0,
+ Utils.convertDpToPixelInt(mContext, 8), 0, 0);
+
+ }
+
+ @Override
+ public void onAnimationRepeat(Animator animation) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+
+ Card card = cardStack.remove(index);
+ cardStack.add(card);
+
+ mAdapter.setItems(cardStack, cardStack.getPosition());
+
+ // refresh();
+ mAdapter.notifyDataSetChanged();
+ Log.v("CardsUI", "Notify Adapter");
+
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ // TODO Auto-generated method stub
+
+ }
+ };
+ }
+
+ public void setAdapter(StackAdapter stackAdapter) {
+ mAdapter = stackAdapter;
+
+ }
+
+ public void setPosition(int position) {
+ mPosition = position;
+ }
+
+ public int getPosition() {
+ return mPosition;
+ }
+
+}
\ No newline at end of file
diff --git a/CardsUILib/src/com/fima/cardsui/objects/RecyclableCard.java b/CardsUILib/src/com/fima/cardsui/objects/RecyclableCard.java
new file mode 100644
index 0000000..44c0526
--- /dev/null
+++ b/CardsUILib/src/com/fima/cardsui/objects/RecyclableCard.java
@@ -0,0 +1,76 @@
+package com.fima.cardsui.objects;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+
+/**
+ * A card that can be recycled when scrolled off screen.
+ */
+public abstract class RecyclableCard extends Card {
+
+ public RecyclableCard() {
+ super();
+ }
+
+ public RecyclableCard(String title, int image) {
+ super(title, image);
+ }
+
+ public RecyclableCard(String titlePlay, String description, int imageRes,
+ String titleColor, Boolean hasOverflow, Boolean isClickable) {
+ super(titlePlay, description, imageRes, titleColor, hasOverflow, isClickable);
+ }
+
+ public RecyclableCard(String title, String desc, int image) {
+ super(title, desc, image);
+ }
+
+ public RecyclableCard(String titlePlay, String description, String color,
+ String titleColor, Boolean hasOverflow, Boolean isClickable) {
+ super(titlePlay, description, color, titleColor, hasOverflow, isClickable);
+ }
+
+ public RecyclableCard(String title, String desc) {
+ super(title, desc);
+ }
+
+ public RecyclableCard(String title) {
+ super(title);
+ }
+
+ /**
+ * Set the values of child views. The view will not be null and
+ * is guaranteed to have the layout of the root element equal to
+ * the layout resource ID from getCardLayoutId().
+ * @param convertView non-null view to modify
+ */
+ protected abstract void applyTo(View convertView);
+
+ /**
+ * Get the R.layout ID of the root element of the content of the card.
+ * This value will be used to inflate the card and check whether an
+ * old card's View can be recycled.
+ * @return layout ID of the card content
+ */
+ protected abstract int getCardLayoutId();
+
+ @Override
+ public View getCardContent(Context context) {
+ View view = LayoutInflater.from(context).inflate(getCardLayoutId(), null);
+ applyTo(view);
+ return view;
+ }
+
+ @Override
+ public boolean convert(View convertCardView) {
+ View view = convertCardView.findViewById(getCardLayoutId());
+ if (view == null) {
+ return false;
+ }
+
+ applyTo(view);
+ return true;
+ }
+
+}
\ No newline at end of file
diff --git a/CardsUILib/src/com/fima/cardsui/views/CardUI.java b/CardsUILib/src/com/fima/cardsui/views/CardUI.java
new file mode 100644
index 0000000..0869940
--- /dev/null
+++ b/CardsUILib/src/com/fima/cardsui/views/CardUI.java
@@ -0,0 +1,394 @@
+package com.fima.cardsui.views;
+
+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.fima.cardsui.R;
+import com.fima.cardsui.StackAdapter;
+import com.fima.cardsui.objects.AbstractCard;
+import com.fima.cardsui.objects.Card;
+import com.fima.cardsui.objects.CardStack;
+
+import java.util.ArrayList;
+
+public class CardUI extends FrameLayout {
+
+ /**
+ * Constants
+ */
+
+ private static final int STATE_ONSCREEN = 0;
+ private static final int STATE_OFFSCREEN = 1;
+ private static final int STATE_RETURNING = 2;
+ protected int renderedCardsStacks = 0;
+ protected int mScrollY;
+ /**
+ * *****************************
+ * Fields
+ *
+ * ******************************
+ */
+
+ private ArrayList mStacks;
+ private Context mContext;
+ 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;
+ private View mPlaceholderView;
+ private QuickReturnListView mListView;
+ private int mMinRawY = 0;
+ private int mState = STATE_ONSCREEN;
+ private int mQuickReturnHeight;
+ private int mCachedVerticalScrollRange;
+ private boolean mSwipeable = false;
+ private OnRenderedListener onRenderedListener;
+ private StackAdapter mAdapter;
+ private View mHeader;
+
+ /**
+ * Constructor
+ */
+ 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
+ */
+ 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
+ */
+ public CardUI(Context context) {
+ super(context);
+ initData(context);
+ }
+
+ private void initData(Context context) {
+ mContext = context;
+ LayoutInflater inflater = LayoutInflater.from(context);
+ mStacks = new ArrayList();
+ //inflate a different layout, depending on the number of columns
+ if (mColumnNumber == 1) {
+ inflater.inflate(R.layout.cards_view, this);
+ // init observable scrollview
+ mListView = (QuickReturnListView) findViewById(R.id.listView);
+ } else {
+ //initialize the mulitcolumn view
+ inflater.inflate(R.layout.cards_view_multicolumn, this);
+ mTableLayout = (TableLayout) findViewById(R.id.tableLayout);
+ }
+ // mListView.setCallbacks(this);
+
+ mHeader = inflater.inflate(R.layout.header, null);
+ mQuickReturnView = (ViewGroup) findViewById(R.id.sticky);
+ mPlaceholderView = mHeader.findViewById(R.id.placeholder);
+
+ }
+
+ public void setSwipeable(boolean b) {
+ mSwipeable = b;
+ }
+
+ 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);
+ }
+
+ }
+
+ public void scrollToCard(int pos) {
+ // int y = 0;
+ try {
+ // y = getY(pos);
+
+ mListView.smoothScrollToPosition(pos);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void scrollToY(int y) {
+
+ try {
+
+ mListView.scrollTo(0, y);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public QuickReturnListView getScrollView() {
+ return mListView;
+ }
+
+ public int getLastCardStackPosition() {
+
+ return mStacks.size() - 1;
+ }
+
+ public void addSeparateCards(Card[] cards) {
+
+ addSeparateCards(cards, false);
+
+ }
+
+ public void addSeparateCards(Card[] cards, boolean refresh) {
+
+ for (int i = 0; i < cards.length; i++) {
+ CardStack stack = new CardStack();
+ stack.add(cards[i]);
+ mStacks.add(stack);
+ }
+
+ if (refresh)
+ refresh();
+ }
+
+ public void addCard(Card card) {
+
+ addCard(card, false);
+
+ }
+
+ public void addCard(Card card, boolean refresh) {
+
+ CardStack stack = new CardStack();
+ stack.add(card);
+ mStacks.add(stack);
+ if (refresh)
+ refresh();
+
+ }
+
+ public void addCardToLastStack(Card card) {
+ addCardToLastStack(card, false);
+
+ }
+
+ 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();
+
+ }
+
+ public void addStack(CardStack stack) {
+ addStack(stack, false);
+
+ }
+
+ 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
+ @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);
+
+ }
+
+ }
+
+ public void clearCards() {
+ mStacks = new ArrayList();
+ renderedCardsStacks = 0;
+ refresh();
+ }
+
+ public void setCurrentStackTitle(String title) {
+ CardStack cardStack = (CardStack) mStacks
+ .get(getLastCardStackPosition());
+ cardStack.setTitle(title);
+
+ }
+
+ public OnRenderedListener getOnRenderedListener() {
+ return onRenderedListener;
+ }
+
+ public void setOnRenderedListener(OnRenderedListener onRenderedListener) {
+ this.onRenderedListener = onRenderedListener;
+ }
+
+ public interface OnRenderedListener {
+ public void onRendered();
+ }
+
+}
diff --git a/CardsUILib/src/com/fima/cardsui/views/QuickReturnListView.java b/CardsUILib/src/com/fima/cardsui/views/QuickReturnListView.java
new file mode 100644
index 0000000..280b945
--- /dev/null
+++ b/CardsUILib/src/com/fima/cardsui/views/QuickReturnListView.java
@@ -0,0 +1,62 @@
+package com.fima.cardsui.views;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.ListView;
+
+public class QuickReturnListView extends ListView {
+
+ private int mItemCount;
+ private int mItemOffsetY[];
+ private boolean scrollIsComputed = false;
+ private int mHeight;
+
+ public QuickReturnListView(Context context) {
+ super(context);
+ }
+
+ public QuickReturnListView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public int getListHeight() {
+ return mHeight;
+ }
+
+ public void computeScrollY() {
+ mHeight = 0;
+ try {
+ mItemCount = getAdapter().getCount();
+ if (mItemOffsetY == null) {
+ mItemOffsetY = new int[mItemCount];
+ }
+ for (int i = 0; i < mItemCount; ++i) {
+ View view = getAdapter().getView(i, null, this);
+ view.measure(
+ MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
+ MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
+ mItemOffsetY[i] = mHeight;
+ mHeight += view.getMeasuredHeight();
+ }
+ scrollIsComputed = true;
+ } catch (NullPointerException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ public boolean scrollYIsComputed() {
+ return scrollIsComputed;
+ }
+
+ public int getComputedScrollY() {
+ int pos, nScrollY, nItemY;
+ View view = null;
+ pos = getFirstVisiblePosition();
+ view = getChildAt(0);
+ nItemY = view.getTop();
+ nScrollY = mItemOffsetY[pos] - nItemY;
+ return nScrollY;
+ }
+}
\ No newline at end of file
diff --git a/Mashpro/.settings/org.eclipse.jdt.core.prefs b/Mashpro/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..48ab4c6
--- /dev/null
+++ b/Mashpro/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,4 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.source=1.6
diff --git a/Mashpro/AndroidManifest.xml b/Mashpro/AndroidManifest.xml
new file mode 100644
index 0000000..24ffe58
--- /dev/null
+++ b/Mashpro/AndroidManifest.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Mashpro/ic_launcher-web.png b/Mashpro/ic_launcher-web.png
new file mode 100644
index 0000000..709257e
Binary files /dev/null and b/Mashpro/ic_launcher-web.png differ
diff --git a/Mashpro/libs/android-support-v4.jar b/Mashpro/libs/android-support-v4.jar
new file mode 100644
index 0000000..9056828
Binary files /dev/null and b/Mashpro/libs/android-support-v4.jar differ
diff --git a/Mashpro/proguard-project.txt b/Mashpro/proguard-project.txt
new file mode 100644
index 0000000..f2fe155
--- /dev/null
+++ b/Mashpro/proguard-project.txt
@@ -0,0 +1,20 @@
+# To enable ProGuard in your project, edit project.properties
+# to define the proguard.config property as described in that file.
+#
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in ${sdk.dir}/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the ProGuard
+# include property in project.properties.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
diff --git a/Mashpro/project.properties b/Mashpro/project.properties
new file mode 100644
index 0000000..4ab1256
--- /dev/null
+++ b/Mashpro/project.properties
@@ -0,0 +1,14 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
+
+# Project target.
+target=android-19
diff --git a/Mashpro/res/drawable-hdpi/ic_launcher.png b/Mashpro/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..a467bcb
Binary files /dev/null and b/Mashpro/res/drawable-hdpi/ic_launcher.png differ
diff --git a/Mashpro/res/drawable-mdpi/ic_launcher.png b/Mashpro/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..c88700c
Binary files /dev/null and b/Mashpro/res/drawable-mdpi/ic_launcher.png differ
diff --git a/Mashpro/res/drawable-xhdpi/ic_launcher.png b/Mashpro/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..d93573e
Binary files /dev/null and b/Mashpro/res/drawable-xhdpi/ic_launcher.png differ
diff --git a/Mashpro/res/drawable-xxhdpi/ic_launcher.png b/Mashpro/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..0ac9260
Binary files /dev/null and b/Mashpro/res/drawable-xxhdpi/ic_launcher.png differ
diff --git a/Mashpro/res/layout/activity_main.xml b/Mashpro/res/layout/activity_main.xml
new file mode 100644
index 0000000..a201ca6
--- /dev/null
+++ b/Mashpro/res/layout/activity_main.xml
@@ -0,0 +1,8 @@
+
+
diff --git a/Mashpro/res/layout/fragment_main_dummy.xml b/Mashpro/res/layout/fragment_main_dummy.xml
new file mode 100644
index 0000000..57dc8ce
--- /dev/null
+++ b/Mashpro/res/layout/fragment_main_dummy.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
diff --git a/Mashpro/res/layout/fragment_main_dummy2.xml b/Mashpro/res/layout/fragment_main_dummy2.xml
new file mode 100644
index 0000000..b05f2af
--- /dev/null
+++ b/Mashpro/res/layout/fragment_main_dummy2.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
diff --git a/Mashpro/res/menu/main.xml b/Mashpro/res/menu/main.xml
new file mode 100644
index 0000000..d122a4b
--- /dev/null
+++ b/Mashpro/res/menu/main.xml
@@ -0,0 +1,9 @@
+
diff --git a/Mashpro/res/values-sw600dp/dimens.xml b/Mashpro/res/values-sw600dp/dimens.xml
new file mode 100644
index 0000000..c876987
--- /dev/null
+++ b/Mashpro/res/values-sw600dp/dimens.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
diff --git a/Mashpro/res/values-sw720dp-land/dimens.xml b/Mashpro/res/values-sw720dp-land/dimens.xml
new file mode 100644
index 0000000..0df3067
--- /dev/null
+++ b/Mashpro/res/values-sw720dp-land/dimens.xml
@@ -0,0 +1,9 @@
+
+
+
+ 128dp
+
+
diff --git a/Mashpro/res/values-v11/styles.xml b/Mashpro/res/values-v11/styles.xml
new file mode 100644
index 0000000..e3ef53d
--- /dev/null
+++ b/Mashpro/res/values-v11/styles.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
diff --git a/Mashpro/res/values-v14/styles.xml b/Mashpro/res/values-v14/styles.xml
new file mode 100644
index 0000000..94dd245
--- /dev/null
+++ b/Mashpro/res/values-v14/styles.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
diff --git a/Mashpro/res/values/dimens.xml b/Mashpro/res/values/dimens.xml
new file mode 100644
index 0000000..2e0e2ae
--- /dev/null
+++ b/Mashpro/res/values/dimens.xml
@@ -0,0 +1,7 @@
+
+
+
+ 16dp
+ 16dp
+
+
diff --git a/Mashpro/res/values/strings.xml b/Mashpro/res/values/strings.xml
new file mode 100644
index 0000000..2947d20
--- /dev/null
+++ b/Mashpro/res/values/strings.xml
@@ -0,0 +1,10 @@
+
+
+
+ Mashpro
+ Settings
+ I have
+ I want
+ Section 3
+
+
diff --git a/Mashpro/res/values/styles.xml b/Mashpro/res/values/styles.xml
new file mode 100644
index 0000000..4ea9326
--- /dev/null
+++ b/Mashpro/res/values/styles.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
diff --git a/Mashpro/src/com/mashpro/MainActivity.java b/Mashpro/src/com/mashpro/MainActivity.java
new file mode 100644
index 0000000..003d9ca
--- /dev/null
+++ b/Mashpro/src/com/mashpro/MainActivity.java
@@ -0,0 +1,155 @@
+package com.mashpro;
+
+import android.annotation.TargetApi;
+import android.app.ActionBar;
+import android.os.Bundle;
+import android.content.Context;
+import android.os.Build;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentActivity;
+import android.support.v4.app.NavUtils;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.TextView;
+
+public class MainActivity extends FragmentActivity implements
+ ActionBar.OnNavigationListener {
+
+ /**
+ * The serialization (saved instance state) Bundle key representing the
+ * current dropdown position.
+ */
+ private static final String STATE_SELECTED_NAVIGATION_ITEM = "selected_navigation_item";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+
+ // Set up the action bar to show a dropdown list.
+ final ActionBar actionBar = getActionBar();
+ actionBar.setDisplayShowTitleEnabled(false);
+ actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
+
+ // Set up the dropdown list navigation in the action bar.
+ actionBar.setListNavigationCallbacks(
+ // Specify a SpinnerAdapter to populate the dropdown list.
+ new ArrayAdapter(getActionBarThemedContextCompat(),
+ android.R.layout.simple_list_item_1,
+ android.R.id.text1, new String[] {
+ getString(R.string.title_section1),
+ getString(R.string.title_section2),
+ }), this);
+ }
+
+ /**
+ * Backward-compatible version of {@link ActionBar#getThemedContext()} that
+ * simply returns the {@link android.app.Activity} if
+ * getThemedContext is unavailable.
+ */
+ @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
+ private Context getActionBarThemedContextCompat() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+ return getActionBar().getThemedContext();
+ } else {
+ return this;
+ }
+ }
+
+ @Override
+ public void onRestoreInstanceState(Bundle savedInstanceState) {
+ // Restore the previously serialized current dropdown position.
+ if (savedInstanceState.containsKey(STATE_SELECTED_NAVIGATION_ITEM)) {
+ getActionBar().setSelectedNavigationItem(
+ savedInstanceState.getInt(STATE_SELECTED_NAVIGATION_ITEM));
+ }
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ // Serialize the current dropdown position.
+ outState.putInt(STATE_SELECTED_NAVIGATION_ITEM, getActionBar()
+ .getSelectedNavigationIndex());
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ // Inflate the menu; this adds items to the action bar if it is present.
+ getMenuInflater().inflate(R.menu.main, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onNavigationItemSelected(int position, long id) {
+ // When the given dropdown item is selected, show its contents in the
+ // container view.
+ Fragment fragment=null;
+ if(position==0)
+ {
+ fragment = new DummySectionFragment();
+ }
+ else if(position==1)
+ {
+ fragment = new DummySectionFragment2();
+ }
+ Bundle args = new Bundle();
+ args.putInt(DummySectionFragment.ARG_SECTION_NUMBER, position + 1);
+ fragment.setArguments(args);
+ getSupportFragmentManager().beginTransaction()
+ .replace(R.id.container, fragment).commit();
+ return true;
+ }
+
+ /**
+ * A dummy fragment representing a section of the app, but that simply
+ * displays dummy text.
+ */
+ public static class DummySectionFragment extends Fragment {
+ /**
+ * The fragment argument representing the section number for this
+ * fragment.
+ */
+ public static final String ARG_SECTION_NUMBER = "section_number";
+
+ public DummySectionFragment() {
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View rootView = inflater.inflate(R.layout.fragment_main_dummy,
+ container, false);
+ //TextView dummyTextView = (TextView) rootView
+ // .findViewById(R.id.section_label);
+ //dummyTextView.setText(Integer.toString(getArguments().getInt(
+ // ARG_SECTION_NUMBER)));
+ return rootView;
+ }
+ }
+ public static class DummySectionFragment2 extends Fragment {
+ /**
+ * The fragment argument representing the section number for this
+ * fragment.
+ */
+ public static final String ARG_SECTION_NUMBER = "section_number";
+
+ public DummySectionFragment2() {
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View rootView = inflater.inflate(R.layout.fragment_main_dummy2,
+ container, false);
+
+ return rootView;
+ }
+ }
+
+
+}