diff --git a/.gitignore b/.gitignore index c21fa8d..605e2ac 100644 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,9 @@ gen/ build/ *.iml +# Gradle +.gradle/ + dynamicgrid/proguard-project.txt dynamicgrid/build.xml diff --git a/dynamicgrid/src/org/askerov/dynamicgrid/BaseDynamicGridAdapter.java b/dynamicgrid/src/org/askerov/dynamicgrid/BaseDynamicGridAdapter.java index 167102a..9317083 100644 --- a/dynamicgrid/src/org/askerov/dynamicgrid/BaseDynamicGridAdapter.java +++ b/dynamicgrid/src/org/askerov/dynamicgrid/BaseDynamicGridAdapter.java @@ -15,6 +15,7 @@ public abstract class BaseDynamicGridAdapter extends AbstractDynamicGridAdapter private ArrayList mItems = new ArrayList(); private int mColumnCount; + private int mReorderType = DynamicGridView.REORDER_TYPE_SNAKE; protected BaseDynamicGridAdapter(Context context, int columnCount) { this.mContext = context; @@ -39,6 +40,10 @@ public void set(List items) { notifyDataSetChanged(); } + public void setReorderType(int reorderType) { + this.mReorderType = reorderType; + } + public void clear() { clearStableIdMap(); mItems.clear(); @@ -94,7 +99,11 @@ public void setColumnCount(int columnCount) { @Override public void reorderItems(int originalPosition, int newPosition) { if (newPosition < getCount()) { - DynamicGridUtils.reorder(mItems, originalPosition, newPosition); + if(mReorderType == DynamicGridView.REORDER_TYPE_SNAKE) { + DynamicGridUtils.reorder(mItems, originalPosition, newPosition); + } else if(mReorderType == DynamicGridView.REORDER_TYPE_SWAP) { + DynamicGridUtils.swap(mItems, originalPosition, newPosition); + } notifyDataSetChanged(); } } @@ -104,6 +113,12 @@ public boolean canReorder(int position) { return true; } + @Override + public int getReorderType() + { + return mReorderType; + } + public List getItems() { return mItems; } diff --git a/dynamicgrid/src/org/askerov/dynamicgrid/DynamicGridAdapterInterface.java b/dynamicgrid/src/org/askerov/dynamicgrid/DynamicGridAdapterInterface.java index 76d0579..daa5089 100644 --- a/dynamicgrid/src/org/askerov/dynamicgrid/DynamicGridAdapterInterface.java +++ b/dynamicgrid/src/org/askerov/dynamicgrid/DynamicGridAdapterInterface.java @@ -30,4 +30,9 @@ public interface DynamicGridAdapterInterface { */ boolean canReorder(int position); + /** + * Determines reorder type. + */ + int getReorderType(); + } diff --git a/dynamicgrid/src/org/askerov/dynamicgrid/DynamicGridView.java b/dynamicgrid/src/org/askerov/dynamicgrid/DynamicGridView.java index e7e61f0..8d6d528 100644 --- a/dynamicgrid/src/org/askerov/dynamicgrid/DynamicGridView.java +++ b/dynamicgrid/src/org/askerov/dynamicgrid/DynamicGridView.java @@ -1,6 +1,11 @@ package org.askerov.dynamicgrid; -import android.animation.*; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; +import android.animation.TypeEvaluator; +import android.animation.ValueAnimator; import android.annotation.TargetApi; import android.content.Context; import android.graphics.Bitmap; @@ -21,7 +26,11 @@ import android.widget.GridView; import android.widget.ListAdapter; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Stack; /** * Author: alex askerov @@ -29,6 +38,8 @@ * Time: 12:31 PM */ public class DynamicGridView extends GridView { + public final static int REORDER_TYPE_SNAKE = 1; + public final static int REORDER_TYPE_SWAP = 2; private static final int INVALID_ID = -1; private static final int MOVE_DURATION = 300; @@ -855,7 +866,7 @@ public boolean onPreDraw() { mTotalOffsetY += mDeltaY; mTotalOffsetX += mDeltaX; - animateReorder(mOriginalPosition, mTargetPosition); + animateSnakeReorder(mOriginalPosition, mTargetPosition); assert mMobileView != null; mMobileView.setVisibility(View.VISIBLE); @@ -913,50 +924,38 @@ private long getId(int position) { @TargetApi(Build.VERSION_CODES.HONEYCOMB) private void animateReorder(final int oldPosition, final int newPosition) { + if(getAdapterInterface().getReorderType() == REORDER_TYPE_SNAKE) { + animateSnakeReorder(oldPosition, newPosition); + } else if(getAdapterInterface().getReorderType() == REORDER_TYPE_SWAP) { + animateSwapReorder(oldPosition, newPosition); + } + } + + @TargetApi(Build.VERSION_CODES.HONEYCOMB) + private void animateSnakeReorder(final int oldPosition, final int newPosition) { boolean isForward = newPosition > oldPosition; List resultList = new LinkedList(); - if (isForward) { - for (int pos = Math.min(oldPosition, newPosition); pos < Math.max(oldPosition, newPosition); pos++) { - View view = getViewForId(getId(pos)); - if ((pos + 1) % getColumnCount() == 0) { - resultList.add(createTranslationAnimations(view, -view.getWidth() * (getColumnCount() - 1), 0, - view.getHeight(), 0)); - } else { - resultList.add(createTranslationAnimations(view, view.getWidth(), 0, 0, 0)); - } - } - } else { - for (int pos = Math.max(oldPosition, newPosition); pos > Math.min(oldPosition, newPosition); pos--) { - View view = getViewForId(getId(pos)); - if ((pos + getColumnCount()) % getColumnCount() == 0) { - resultList.add(createTranslationAnimations(view, view.getWidth() * (getColumnCount() - 1), 0, - -view.getHeight(), 0)); - } else { - resultList.add(createTranslationAnimations(view, -view.getWidth(), 0, 0, 0)); - } - } - } - AnimatorSet resultSet = new AnimatorSet(); - resultSet.playTogether(resultList); - resultSet.setDuration(MOVE_DURATION); - resultSet.setInterpolator(new AccelerateDecelerateInterpolator()); - resultSet.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationStart(Animator animation) { - mReorderAnimation = true; - updateEnableState(); - } + for (int pos = Math.min(oldPosition, newPosition); pos < Math.max(oldPosition, newPosition); pos++) { - @Override - public void onAnimationEnd(Animator animation) { - mReorderAnimation = false; - updateEnableState(); - } - }); - resultSet.start(); + int oldViewPosition = isForward ? pos : pos+1; + int newViewPosition = isForward ? pos+1 : pos; + + resultList.add(createViewSwapAnimations(oldViewPosition, newViewPosition)); + } + + startAnimations(resultList); } + @TargetApi(Build.VERSION_CODES.HONEYCOMB) + private void animateSwapReorder(final int oldPosition, final int newPosition) { + + List resultList = new LinkedList(); + + resultList.add(createViewSwapAnimations(oldPosition, newPosition)); + + startAnimations(resultList); + } @TargetApi(Build.VERSION_CODES.HONEYCOMB) private AnimatorSet createTranslationAnimations(View view, float startX, float endX, float startY, float endY) { @@ -975,6 +974,44 @@ protected void dispatchDraw(Canvas canvas) { } } + /** + * Basically the old view is set to the new position and + * animates it's way from the old position to the new position. + * @param oldViewPosition Position of the old view. + * @param newViewPosition Position of the new view. + * @return + */ + private AnimatorSet createViewSwapAnimations(int oldViewPosition, int newViewPosition) { + View viewOld = getViewForId(getId(oldViewPosition)); + View viewNew = getViewForId(getId(newViewPosition)); + + return createTranslationAnimations(viewOld, viewNew.getX()-viewOld.getX(), 0, viewNew.getY()-viewOld.getY(), 0); + } + + /** + * + * @param resultList + */ + private void startAnimations(List resultList) { + AnimatorSet resultSet = new AnimatorSet(); + resultSet.playTogether(resultList); + resultSet.setDuration(MOVE_DURATION); + resultSet.setInterpolator(new AccelerateDecelerateInterpolator()); + resultSet.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animation) { + mReorderAnimation = true; + updateEnableState(); + } + + @Override + public void onAnimationEnd(Animator animation) { + mReorderAnimation = false; + updateEnableState(); + } + }); + resultSet.start(); + } public interface OnDropListener { void onActionDrop();