package com.marshalchen.common.uimodule.cooldraganddrop;
import android.content.Context;
import android.content.res.TypedArray;
import android.database.DataSetObserver;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.TransitionDrawable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.Animation;
import android.view.animation.TranslateAnimation;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import com.marshalchen.common.commonUtils.logUtils.Logs;
import com.marshalchen.ultimateandroiduicomponent.R;
import java.util.LinkedList;
import java.util.List;
public class SpanVariableGridView extends AdapterView<BaseAdapter> {
/**
* If the last item can be drag or it counld be used as other function
*/
boolean isLastItemCouldDrag = true;
private static final int NOT_DEFINED_VALUE = -1;
public static interface CalculateChildrenPosition {
void onCalculatePosition(final View view, final int position, final int row, final int column);
}
;
private static final int INVALID_POSITION = NOT_DEFINED_VALUE;
private static final int TOUCH_STATE_RESTING = 0;
private static final int TOUCH_STATE_CLICK = 1;
private static final int TOUCH_STATE_LONG_CLICK = 2;
private int mTouchStartX;
private int mTouchStartY;
private int mTouchStartItemPosition;
private Runnable mLongPressRunnable;
private int mColCount = 2;
private int mItemMargin = 0;
protected int mControlHeight = 0;
private Rect mRect = new Rect();
protected boolean mPopulating = false;
private int mTouchState = TOUCH_STATE_RESTING;
protected BaseAdapter mAdapter = null;
private TransitionDrawable mItemTransitionDrawable = null;
private List<CalculateChildrenPosition> mCalculateChildrenPositionList = new LinkedList<CalculateChildrenPosition>();
private final DataSetObserver mObserver = new DataSetObserver() {
@Override
public void onChanged() {
mPopulating = false;
removeAllViewsInLayout();
requestLayout();
}
@Override
public void onInvalidated() {
}
};
public static class LayoutParams extends AdapterView.LayoutParams {
private static final int[] LAYOUT_ATTRS = new int[]{android.R.attr.layout_span};
public static final int SPAN_INDEX = 0;
public static final int ALL_COLUMNS = NOT_DEFINED_VALUE;
public int span = 1;
public int position = NOT_DEFINED_VALUE;
public int row = NOT_DEFINED_VALUE;
public int column = NOT_DEFINED_VALUE;
long id = NOT_DEFINED_VALUE;
public LayoutParams(int height) {
super(MATCH_PARENT, height);
if (this.height == MATCH_PARENT) {
this.height = WRAP_CONTENT;
}
}
public LayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
setupWidthAndHeight();
TypedArray a = c.obtainStyledAttributes(attrs, LAYOUT_ATTRS);
span = a.getInteger(SPAN_INDEX, 1);
a.recycle();
}
public LayoutParams(AdapterView.LayoutParams other) {
super(other);
setupWidthAndHeight();
}
private void setupWidthAndHeight() {
if (this.width != MATCH_PARENT) {
this.width = MATCH_PARENT;
}
if (this.height == MATCH_PARENT) {
this.height = WRAP_CONTENT;
}
}
}
public SpanVariableGridView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initialize(attrs);
}
public SpanVariableGridView(Context context, AttributeSet attrs) {
super(context, attrs);
initialize(attrs);
}
public SpanVariableGridView(Context context) {
super(context);
}
private void initialize(final AttributeSet attrs) {
if (attrs != null) {
TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.SpanVariableGridView);
try {
mColCount = a.getInteger(R.styleable.SpanVariableGridView_SpanVariableGridView_numColumns, 2);
mItemMargin = a.getDimensionPixelSize(R.styleable.SpanVariableGridView_SpanVariableGridView_itemMargin, 0);
} finally {
a.recycle();
}
} else {
mColCount = 2;
mItemMargin = 0;
}
}
public boolean addCalculateChildrenPositionListener(final CalculateChildrenPosition listener) {
return mCalculateChildrenPositionList.add(listener);
}
public boolean removeCalculateChildrenPositionListener(final CalculateChildrenPosition listener) {
return mCalculateChildrenPositionList.remove(listener);
}
@Override
public BaseAdapter getAdapter() {
return mAdapter;
}
@Override
public View getSelectedView() {
return null;
}
@Override
public void setAdapter(BaseAdapter adapter) {
if (mAdapter != null) {
mAdapter.unregisterDataSetObserver(mObserver);
}
mAdapter = adapter;
if (mAdapter != null) {
mAdapter.registerDataSetObserver(mObserver);
}
removeAllViewsInLayout();
requestLayout();
}
@Override
public void setSelection(int position) {
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
if (mAdapter == null) {
return;
}
layoutChildrens(INVALID_POSITION, false);
}
protected void performDragAndDropSwapping(int from, int to) {
mPopulating = true;
Logs.d("drag----" + from + " " + to + " " + mAdapter.getCount());
if (from != to && isLastItemCouldDrag ? true : to != mAdapter.getCount() - 1) {
final View removedChild = getChildAt(from);
removedChild.clearAnimation();
removeViewInLayout(removedChild);
addViewInLayout(removedChild, to, removedChild.getLayoutParams());
}
mControlHeight = measureChildrens(false);
layoutChildrens(to, true);
mPopulating = false;
}
@Override
public void requestLayout() {
if (!mPopulating) {
super.requestLayout();
}
}
protected Rect layoutChildrens(final int draggedChild, final boolean animate) {
int row = 0;
int rowHeight = 0;
int fullHeight = mItemMargin;
int width = getMeasuredWidth() - 2 * mItemMargin;
final int colWidth = (width - (mColCount - 1) * mItemMargin) / mColCount;
Rect draggedChildRect = null;
for (int position = 0; position < mAdapter.getCount(); position++) {
final View childView = getChildAt(position);
final Point prev = new Point(childView.getLeft(), childView.getTop());
final LayoutParams lp = (LayoutParams) childView.getLayoutParams();
final int column = lp.column;
if (row != lp.row) {
fullHeight += (rowHeight + mItemMargin);
rowHeight = 0;
}
rowHeight = Math.max(rowHeight, childView.getMeasuredHeight());
row = lp.row;
final int width_ = column == LayoutParams.ALL_COLUMNS ? width : (lp.span * (colWidth + mItemMargin) - mItemMargin);
final int left_ = mItemMargin + (column == LayoutParams.ALL_COLUMNS ? 0 : column * (colWidth + mItemMargin));
final int top_ = fullHeight;
final int right_ = left_ + width_;
final int bottom_ = top_ + childView.getMeasuredHeight();
measureChildren(childView, width_, lp.height);
if (position != draggedChild) {
final Point now = new Point(left_, top_);
childView.layout(left_, top_, right_, bottom_);
if (animate) {
translateChild(childView, prev, now);
}
} else {
draggedChildRect = new Rect(left_, top_, right_, bottom_);
}
}
return draggedChildRect;
}
protected final void translateChild(View v, Point prev, Point now) {
TranslateAnimation translate = new TranslateAnimation(Animation.ABSOLUTE, -now.x + prev.x, Animation.ABSOLUTE, 0, Animation.ABSOLUTE, -now.y
+ prev.y, Animation.ABSOLUTE, 0);
translate.setInterpolator(new AccelerateInterpolator(4f));
translate.setDuration(350);
translate.setFillEnabled(false);
translate.setFillAfter(false);
v.clearAnimation();
v.startAnimation(translate);
}
protected void measureChildren(View child, final int width, final int height) {
final int heightSpec;
if (height == LayoutParams.WRAP_CONTENT) {
heightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
} else {
heightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
}
final int widthSpec;
if (width == LayoutParams.WRAP_CONTENT) {
widthSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
} else {
widthSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY);
}
child.measure(widthSpec, heightSpec);
}
protected int pointToPosition(final int draggedChild, final int x, final int y) {
for (int index = 0; index < getChildCount(); index++) {
if (index == draggedChild) {
continue;
}
getChildAt(index).getHitRect(mRect);
if (mRect.contains(x, y)) {
return index;
}
}
return INVALID_POSITION;
}
private void clickChildAt() {
final int index = pointToPosition(INVALID_POSITION, mTouchStartX, mTouchStartY);
if (index != INVALID_POSITION && index == mTouchStartItemPosition) {
final View itemView = getChildAt(index);
final int position = index;
final long id = mAdapter.getItemId(position);
performItemClick(itemView, position, id);
}
}
private void longClickChild(final int index) {
final View itemView = getChildAt(index);
final long id = mAdapter.getItemId(index);
final OnItemLongClickListener listener = getOnItemLongClickListener();
if (listener != null) {
listener.onItemLongClick(this, itemView, index, id);
}
}
private void startLongPressCheck() {
if (mLongPressRunnable == null) {
mLongPressRunnable = new Runnable() {
public void run() {
if (mTouchState == TOUCH_STATE_CLICK) {
final int index = pointToPosition(INVALID_POSITION, mTouchStartX, mTouchStartY);
if (index != INVALID_POSITION && index == mTouchStartItemPosition) {
longClickChild(index);
mTouchState = TOUCH_STATE_LONG_CLICK;
}
}
}
};
}
postDelayed(mLongPressRunnable, ViewConfiguration.getLongPressTimeout());
}
protected void startLongClickTransition(final View clickedChild) {
if (clickedChild != null && mItemTransitionDrawable == null) {
if (clickedChild.getBackground().getCurrent() instanceof TransitionDrawable) {
mItemTransitionDrawable = (TransitionDrawable) clickedChild.getBackground().getCurrent();
mItemTransitionDrawable.startTransition(ViewConfiguration.getLongPressTimeout());
}
}
}
protected void resetLongClickTransition() {
if (mItemTransitionDrawable != null) {
mItemTransitionDrawable.resetTransition();
mItemTransitionDrawable = null;
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (isInEditMode() || mAdapter == null) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
return;
}
mControlHeight = measureChildrens(false);
final int measuredWidth = MeasureSpec.getSize(widthMeasureSpec);
setMeasuredDimension(measuredWidth, mControlHeight);
}
private void fireCalculateChildrenPositionEvent(final View view, final int position, final int row, final int column) {
for (CalculateChildrenPosition listener : mCalculateChildrenPositionList) {
listener.onCalculatePosition(view, position, row, column);
}
}
protected int measureChildrens(final boolean justMeasure) {
int row = 0;
int col = 0;
int rowHeight = 0;
int spansFilled = 0;
int fullHeight = mItemMargin;
for (int position = 0; position < mAdapter.getCount(); position++) {
View childView = getChildAt(position);
if (childView == null) {
childView = mAdapter.getView(position, null, this);
LayoutParams params = (LayoutParams) childView.getLayoutParams();
if (params == null) {
params = new LayoutParams(new AdapterView.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
}
if (!justMeasure) {
addViewInLayout(childView, NOT_DEFINED_VALUE, params);
}
}
final LayoutParams lp = (LayoutParams) childView.getLayoutParams();
measureChildren(childView, lp.width, lp.height);
lp.position = position;
spansFilled += lp.span;
while (true) {
if (spansFilled <= mColCount) {
lp.row = row;
lp.column = lp.span == mColCount ? LayoutParams.ALL_COLUMNS : col;
col = spansFilled;
if (justMeasure) {
fireCalculateChildrenPositionEvent(childView, lp.position, lp.row, lp.column);
} else {
childView.setLayoutParams(lp);
}
rowHeight = Math.max(rowHeight, mItemMargin + childView.getMeasuredHeight());
}
if (spansFilled >= mColCount) {
fullHeight += rowHeight;
row++;
col = 0;
rowHeight = 0;
if (spansFilled != mColCount) {
spansFilled = lp.span;
continue;
}
spansFilled = 0;
}
break;
}
}
fullHeight += rowHeight;
return fullHeight;
}
public void requestCalculateChildrenPositions() {
measureChildrens(true);
}
private void startTouch(final MotionEvent event) {
mTouchStartX = (int) event.getX();
mTouchStartY = (int) event.getY();
mTouchStartItemPosition = pointToPosition(INVALID_POSITION, mTouchStartX, mTouchStartY);
startLongPressCheck();
mTouchState = TOUCH_STATE_CLICK;
}
@Override
public void childDrawableStateChanged(View child) {
startLongClickTransition(child);
super.childDrawableStateChanged(child);
}
private void endTouch() {
resetLongClickTransition();
removeCallbacks(mLongPressRunnable);
mTouchState = TOUCH_STATE_RESTING;
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
final boolean result = super.dispatchTouchEvent(event);
if (getChildCount() == 0) {
return result;
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
startTouch(event);
break;
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP:
if (mTouchState == TOUCH_STATE_CLICK) {
clickChildAt();
}
endTouch();
break;
default:
endTouch();
break;
}
return result;
}
}