package com.animoto.android.views;
import android.app.Activity;
import android.content.Context;
import android.graphics.Point;
import android.os.Handler;
import android.os.SystemClock;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.*;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ImageView;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class DraggableGridView extends ViewGroup implements View.OnTouchListener, View.OnClickListener, View.OnLongClickListener {
private static final String TAG = "DraggableGridView";
public static float childRatio = .9f;
public static int animT = 150;
protected int colCount;
protected int childSize;
protected int padding;
protected int dpi;
protected int scroll = 0;
protected float lastDelta = 0;
protected Handler handler = new Handler();
/** * 正在拖动的项目index */
protected int mDragged = -1;
protected int lastX = -1;
protected int lastY = -1;
protected int mLastTarget = -1;
protected boolean mEnabled = true;
protected boolean touching = false;
protected ArrayList<Integer> mNewPositions = new ArrayList<Integer>();
protected OnRearrangeListener onRearrangeListener;
protected OnClickListener secondaryOnClickListener;
protected Runnable updateTask = new Runnable() {
public void run() {
if (mDragged != -1) {
if (lastY < padding * 3 && scroll > 0)
scroll -= 20;
else if (lastY > getBottom() - getTop() - (padding * 3) && scroll < getMaxScroll())
scroll += 20;
} else if (lastDelta != 0 && !touching) {
scroll += lastDelta;
lastDelta *= .9;
if (Math.abs(lastDelta) < .25)
lastDelta = 0;
}
clampScroll();
onLayout(true, getLeft(), getTop(), getRight(), getBottom());
handler.postDelayed(this, 25);
}
};
/** * 不可更改的item */
private List<Integer> mImmutableItemIndex = new ArrayList<Integer>();
private OnItemClickListener onItemClickListener;
public DraggableGridView(Context context, AttributeSet attrs) {
super(context, attrs);
setListeners();
handler.removeCallbacks(updateTask);
handler.postAtTime(updateTask, SystemClock.uptimeMillis() + 500);
setChildrenDrawingOrderEnabled(true);
DisplayMetrics metrics = new DisplayMetrics();
((Activity) context).getWindowManager().getDefaultDisplay().getMetrics(metrics);
dpi = metrics.densityDpi;
}
public void setImmutableItems(Integer... indexs) {
for (Integer index : indexs) {
mImmutableItemIndex.add(index);
}
}
protected void setListeners() {
setOnTouchListener(this);
super.setOnClickListener(this);
setOnLongClickListener(this);
}
@Override
public void setOnClickListener(OnClickListener l) {
secondaryOnClickListener = l;
}
@Override
public void addView(View child) {
super.addView(child);
mNewPositions.add(-1);
}
;
@Override
public void removeViewAt(int index) {
super.removeViewAt(index);
mNewPositions.remove(index);
}
;
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
float w = (right - left) / (dpi / 160f);
colCount = 2;
int sub = 240;
w -= 280;
while (w > 0) {
colCount++;
w -= sub;
sub += 40;
}
childSize = (right - left) / colCount;
childSize = Math.round(childSize * childRatio);
padding = ((right - left) - (childSize * colCount)) / (colCount + 1);
for (int i = 0; i < getChildCount(); i++)
if (i != mDragged) {
Point xy = getCoorFromIndex(i);
getChildAt(i).layout(xy.x, xy.y, xy.x + childSize, xy.y + childSize);
}
}
@Override
protected int getChildDrawingOrder(int childCount, int i) {
if (mDragged == -1)
return i;
else if (i == childCount - 1)
return mDragged;
else if (i >= mDragged)
return i + 1;
return i;
}
public int getIndexFromCoor(int x, int y) {
int col = getColOrRowFromCoor(x), row = getColOrRowFromCoor(y + scroll);
if (col == -1 || row == -1)
return -1;
int index = row * colCount + col;
if (index >= getChildCount())
return -1;
return index;
}
/** * 根据所在区域,获取其所在行或列 * * @param coor * @return */
protected int getColOrRowFromCoor(int coor) {
coor -= padding;
for (int i = 0; coor > 0; i++) {
if (coor < childSize)
return i;
coor -= (childSize + padding);
}
return -1;
}
/** * 根据所在坐标,得到正在操作的是哪个项目 */
protected int getTargetFromCoor(int x, int y) {
if (getColOrRowFromCoor(y + scroll) == -1)
return -1;
int leftPos = getIndexFromCoor(x - (childSize / 4), y);
int rightPos = getIndexFromCoor(x + (childSize / 4), y);
if (leftPos == -1 && rightPos == -1)
return -1;
if (leftPos == rightPos)
return -1;
int target = -1;
if (rightPos > -1)
target = rightPos;
else if (leftPos > -1)
target = leftPos + 1;
if (mDragged < target)
return target - 1;
return target;
}
/** * 从所在的index,得到当前所在的区域 */
protected Point getCoorFromIndex(int index) {
int col = index % colCount;
int row = index / colCount;
return new Point(padding + (childSize + padding) * col,
padding + (childSize + padding) * row - scroll);
}
public int getIndexOf(View child) {
for (int i = 0; i < getChildCount(); i++)
if (getChildAt(i) == child)
return i;
return -1;
}
public void onClick(View view) {
if (mEnabled) {
if (secondaryOnClickListener != null)
secondaryOnClickListener.onClick(view);
if (onItemClickListener != null && getLastIndex() != -1)
onItemClickListener.onItemClick(null, getChildAt(getLastIndex()), getLastIndex(), getLastIndex() / colCount);
}
}
/** * 判断是否是 不可更改的 */
private boolean isImmutable(int index) {
if (mImmutableItemIndex == null || mImmutableItemIndex.size() == 0) {
return false;
}
if (mImmutableItemIndex.contains(index)) {
return true;
} else {
return false;
}
}
public boolean onLongClick(View view) {
if (!mEnabled)
return false;
int index = getLastIndex();
if (index != -1) {
Log.d(TAG, "长按的index" + index);
if (!isImmutable(index)) {
mDragged = index;
animateDragged();
return true;
}
return false;
}
return false;
}
public boolean onTouch(View view, MotionEvent event) {
int action = event.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
mEnabled = true;
lastX = (int) event.getX();
lastY = (int) event.getY();
touching = true;
break;
case MotionEvent.ACTION_MOVE:
int delta = lastY - (int) event.getY();
if (mDragged != -1) {
int x = (int) event.getX();
int y = (int) event.getY();
int left = x - (3 * childSize / 4);
int top = y - (3 * childSize / 4);
getChildAt(mDragged).layout(left, top, left + (childSize * 3 / 2), top + (childSize * 3 / 2));
int target = getTargetFromCoor(x, y);
Log.d(TAG, "target>>>" + target);
if (mLastTarget != target) {
if (target != -1) {
animateGap(target);
}
}
Log.d(TAG, "target>>" + target);
Log.d(TAG, "lastTargets>>" + mLastTarget);
} else {
scroll += delta;
clampScroll();
if (Math.abs(delta) > 2)
mEnabled = false;
onLayout(true, getLeft(), getTop(), getRight(), getBottom());
}
lastX = (int) event.getX();
lastY = (int) event.getY();
lastDelta = delta;
break;
case MotionEvent.ACTION_UP:
if (mDragged != -1) {
View v = getChildAt(mDragged);
if (mLastTarget != -1)
reorderChildren();
else {
Point xy = getCoorFromIndex(mDragged);
v.layout(xy.x, xy.y, xy.x + childSize, xy.y + childSize);
}
v.clearAnimation();
if (v instanceof ImageView)
((ImageView) v).setAlpha(255);
mLastTarget = -1;
mDragged = -1;
}
touching = false;
break;
}
if (mDragged != -1)
return true;
return false;
}
protected void animateDragged() {
View draggingView = getChildAt(mDragged);
int x = getCoorFromIndex(mDragged).x + childSize;
int y = getCoorFromIndex(mDragged).y + childSize / 2;
int l = x - (3 * childSize / 4);
int t = y - (3 * childSize / 4);
draggingView.layout(l, t, l + (childSize * 3 / 2), t + (childSize * 3 / 2));
AnimationSet animSet = new AnimationSet(true);
ScaleAnimation scale = new ScaleAnimation(.667f, 1, .667f, 1, childSize * 3 / 4, childSize * 3 / 4);
scale.setDuration(animT);
AlphaAnimation alpha = new AlphaAnimation(1, .5f);
alpha.setDuration(animT);
animSet.addAnimation(scale);
animSet.addAnimation(alpha);
animSet.setFillEnabled(true);
animSet.setFillAfter(true);
draggingView.clearAnimation();
draggingView.startAnimation(animSet);
}
/** * 动态排列视图的顺序 */
protected void animateGap(int target) {
mLastTarget = target;
for (int i = 0; i < getChildCount(); i++) {
View childrenView = getChildAt(i);
if (i == mDragged || isImmutable(i))
continue;
int newPos = i;
if (mDragged < target && i >= mDragged + 1 && i <= target)
newPos--;
else if (target < mDragged && i >= target && i < mDragged)
newPos++;
int oldPos = i;
if (mNewPositions.get(i) != -1)
oldPos = mNewPositions.get(i);
if (oldPos == newPos )
continue;
if (isImmutable(newPos)) {
if (newPos > i) {
newPos++;
}else if (newPos < i) {
newPos--;
}
}
Point oldXY = getCoorFromIndex(oldPos);
Point newXY = getCoorFromIndex(newPos);
Point oldOffset = new Point(oldXY.x - childrenView.getLeft(), oldXY.y - childrenView.getTop());
Point newOffset = new Point(newXY.x - childrenView.getLeft(), newXY.y - childrenView.getTop());
TranslateAnimation translate = new TranslateAnimation(Animation.ABSOLUTE, oldOffset.x,
Animation.ABSOLUTE, newOffset.x,
Animation.ABSOLUTE, oldOffset.y,
Animation.ABSOLUTE, newOffset.y);
translate.setDuration(animT);
translate.setFillEnabled(true);
translate.setFillAfter(true);
childrenView.clearAnimation();
childrenView.startAnimation(translate);
mNewPositions.set(i, newPos);
}
}
/** * 动画结束后,重新排列item(将视图数据列表中的位置更改) */
protected void reorderChildren() {
if (isImmutable(mLastTarget) || isImmutable(mDragged)) {
return;
}
ArrayList<View> children = new ArrayList<View>();
for (int i = 0; i < getChildCount(); i++) {
getChildAt(i).clearAnimation();
children.add(getChildAt(i));
}
removeAllViews();
while (mDragged != mLastTarget){
if (mLastTarget == children.size()) {
children.add(children.remove(mDragged));
mDragged = mLastTarget;
} else if (mDragged < mLastTarget) {
swapWithNext(children, mDragged, 1);
} else if (mDragged > mLastTarget) {
swapWithPrev(children, mDragged, 1);
}
if (mDragged < 0) {
break;
}
}
for (int i = 0; i < children.size(); i++) {
mNewPositions.set(i, -1);
addView(children.get(i));
}
onLayout(true, getLeft(), getTop(), getRight(), getBottom());
}
private void swapWithNext(ArrayList<View> childrenViews,int itemIndex, int offset) {
if (isImmutable(itemIndex + offset)) {
offset++;
swapWithNext(childrenViews, mDragged, offset);
return;
}
swap(childrenViews, itemIndex, itemIndex + offset);
for (int i = 0; i < offset; i++) {
mDragged++;
}
}
private void swapWithPrev(ArrayList<View> childrenViews,int itemIndex, int offset) {
if (isImmutable(itemIndex - offset) && (itemIndex - offset) >= 0) {
offset++;
swapWithPrev(childrenViews, mDragged, offset);
return;
}
swap(childrenViews, itemIndex, itemIndex - offset);
for (int i = 0; i < offset; i++) {
mDragged--;
}
}
private void swap(ArrayList<View> childrenViews, int firstItem, int secondItem) {
int childViewSize = childrenViews.size();
if (firstItem < 0 || secondItem < 0) {
Log.d("swap", "first>" + firstItem + "; second>>" + secondItem);
return;
}
if (firstItem >= childViewSize || secondItem >= childViewSize) {
Log.d("swap", "size>>" + childViewSize + ";然而first>" + firstItem + "; second>>" + secondItem);
return;
}
Collections.swap(childrenViews, firstItem, secondItem);
if (onRearrangeListener != null)
onRearrangeListener.onRearrange(firstItem, secondItem);
}
public void scrollToTop() {
scroll = 0;
}
public void scrollToBottom() {
scroll = Integer.MAX_VALUE;
clampScroll();
}
protected void clampScroll() {
int stretch = 3, overreach = getHeight() / 2;
int max = getMaxScroll();
max = Math.max(max, 0);
if (scroll < -overreach) {
scroll = -overreach;
lastDelta = 0;
} else if (scroll > max + overreach) {
scroll = max + overreach;
lastDelta = 0;
} else if (scroll < 0) {
if (scroll >= -stretch)
scroll = 0;
else if (!touching)
scroll -= scroll / stretch;
} else if (scroll > max) {
if (scroll <= max + stretch)
scroll = max;
else if (!touching)
scroll += (max - scroll) / stretch;
}
}
protected int getMaxScroll() {
int rowCount = (int) Math.ceil((double) getChildCount() / colCount), max = rowCount * childSize + (rowCount + 1) * padding - getHeight();
return max;
}
public int getLastIndex() {
return getIndexFromCoor(lastX, lastY);
}
public void setOnRearrangeListener(OnRearrangeListener l) {
this.onRearrangeListener = l;
}
public void setOnItemClickListener(OnItemClickListener l) {
this.onItemClickListener = l;
}
}