网络上关于GridView可拖动的例子已经不少了,包括带动画不带动画的都有一堆,但几乎都是通过继承Android原生控件GridView来扩展的,当然这种实现方式是很容易联想到的,也是最容易实现的。我随便百度了一下,就有一个:http://zhangcb666.blog.163.com/blog/static/4696352920124221043837/,大家可以参考一下,我这里就不赘述了。
今天跟大家分享另外一种方式,通过继承ViewGroup来实现,我们都知道,ViewGroup可以填充很多个View,因此,我觉得可以类似把GridView的每一个Item填充到我们自定义的ViewGroup中,然后监听长按时间,实现拖动的效果,同时加上动画效果,个人感觉比网上其他实现方式更加简洁和美观,唯一的缺点就是:没有setAdapter的函数,添加的item,需要我们手动add到ViewGroup中,如果item不是特别复杂和繁多,个人觉得也不算什么问题。好了,废话不多说,我们先来看看效果图,第一张是静态效果,第二张是拖动时的效果图。
其实代码也是很简单的,总共就两个类:一个自定义控件DragGridView,还有一个使用的例子MainActivity。
我们先来看看DragGridView的代码部分:
/**
* 另外一种方式实现动画可拖动item的GridView
*
* @author way
*
*/
public class DragGridView extends ViewGroup implements View.OnTouchListener,
View.OnClickListener, View.OnLongClickListener {
// layout vars
public static float childRatio = .9f;
protected int colCount, childSize, padding, dpi, scroll = 0;
protected float lastDelta = 0;
protected Handler handler = new Handler();
// dragging vars
protected int dragged = -1, lastX = -1, lastY = -1, lastTarget = -1;
protected boolean enabled = true, touching = false;
// anim vars
public static int animT = 150;
protected ArrayList<Integer> newPositions = new ArrayList<Integer>();
// listeners
protected OnRearrangeListener onRearrangeListener;
protected OnClickListener secondaryOnClickListener;
private OnItemClickListener onItemClickListener;
/**
* 拖动item的接口
*/
public interface OnRearrangeListener {
public abstract void onRearrange(int oldIndex, int newIndex);
}
// CONSTRUCTOR AND HELPERS
public DragGridView(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;
}
protected void setListeners() {
setOnTouchListener(this);
super.setOnClickListener(this);
setOnLongClickListener(this);
}
@Override
public void setOnClickListener(OnClickListener l) {
secondaryOnClickListener = l;
}
protected Runnable updateTask = new Runnable() {
public void run() {
if (dragged != -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);
}
};
@Override
public void addView(View child) {
super.addView(child);
newPositions.add(-1);
};
@Override
public void removeViewAt(int index) {
super.removeViewAt(index);
newPositions.remove(index);
};
// LAYOUT
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
// compute width of view, in dp
float w = (r - l) / (dpi / 160f);
// determine number of columns, at least 2
colCount = 2;
int sub = 240;
w -= 280;
while (w > 0) {
colCount++;
w -= sub;
sub += 40;
}
// determine childSize and padding, in px
childSize = (r - l) / colCount;
childSize = Math.round(childSize * childRatio);
padding = ((r - l) - (childSize * colCount)) / (colCount + 1);
for (int i = 0; i < getChildCount(); i++)
if (i != dragged) {
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 (dragged == -1)
return i;
else if (i == childCount - 1)
return dragged;
else if (i >= dragged)
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) // touch is between columns or rows
return -1;
int index = row * colCount + col;
if (index >= getChildCount())
return -1;
return index;
}
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) // touch is between rows
return -1;
// if (getIndexFromCoor(x, y) != -1) //touch on top of another visual
// return -1;
int leftPos = getIndexFromCoor(x - (childSize / 4), y);
int rightPos = getIndexFromCoor(x + (childSize / 4), y);
if (leftPos == -1 && rightPos == -1) // touch is in the middle of
// nowhere
return -1;
if (leftPos == rightPos) // touch is in the middle of a visual
return -1;
int target = -1;
if (rightPos > -1)
target = rightPos;
else if (leftPos > -1)
target = leftPos + 1;
if (dragged < target)
return target - 1;
// Toast.makeText(getContext(), "Target: " + target + ".",
// Toast.LENGTH_SHORT).show();
return target;
}
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;
}
// EVENT HANDLERS
public void onClick(View view) {
if (enabled) {
if (secondaryOnClickListener != null)
secondaryOnClickListener.onClick(view);
if (onItemClickListener != null && getLastIndex() != -1)
onItemClickListener.onItemClick(null,
getChildAt(getLastIndex()), getLastIndex(),
getLastIndex() / colCount);
}
}
public boolean onLongClick(View view) {
if (!enabled)
return false;
int index = getLastIndex();
if (index != -1) {
dragged = index;
animateDragged();
return true;
}
return false;
}
public boolean onTouch(View view, MotionEvent event) {
int action = event.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
enabled = true;
lastX = (int) event.getX();
lastY = (int) event.getY();
touching = true;
break;
case MotionEvent.ACTION_MOVE:
int delta = lastY - (int) event.getY();
if (dragged != -1) {
// change draw location of dragged visual
int x = (int) event.getX(), y = (int) event.getY();
int l = x - (3 * childSize / 4), t = y - (3 * childSize / 4);
getChildAt(dragged).layout(l, t, l + (childSize * 3 / 2),
t + (childSize * 3 / 2));
// check for new target hover
int target = getTargetFromCoor(x, y);
if (lastTarget != target) {
if (target != -1) {
animateGap(target);
lastTarget = target;
}
}
} else {
scroll += delta;
clampScroll();
if (Math.abs(delta) > 2)
enabled = false;
onLayout(true, getLeft(), getTop(), getRight(), getBottom());
}
lastX = (int) event.getX();
lastY = (int) event.getY();
lastDelta = delta;
break;
case MotionEvent.ACTION_UP:
if (dragged != -1) {
View v = getChildAt(dragged);
if (lastTarget != -1)
reorderChildren();
else {
Point xy = getCoorFromIndex(dragged);
v.layout(xy.x, xy.y, xy.x + childSize, xy.y + childSize);
}
v.clearAnimation();
if (v instanceof ImageView)
((ImageView) v).setAlpha(255);
lastTarget = -1;
dragged = -1;
}
touching = false;
break;
}
if (dragged != -1)
return true;
return false;
}
// EVENT HELPERS
protected void animateDragged() {
View v = getChildAt(dragged);
int x = getCoorFromIndex(dragged).x + childSize / 2, y = getCoorFromIndex(dragged).y
+ childSize / 2;
int l = x - (3 * childSize / 4), t = y - (3 * childSize / 4);
v.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);
v.clearAnimation();
v.startAnimation(animSet);
}
protected void animateGap(int target) {
for (int i = 0; i < getChildCount(); i++) {
View v = getChildAt(i);
if (i == dragged)
continue;
int newPos = i;
if (dragged < target && i >= dragged + 1 && i <= target)
newPos--;
else if (target < dragged && i >= target && i < dragged)
newPos++;
// animate
int oldPos = i;
if (newPositions.get(i) != -1)
oldPos = newPositions.get(i);
if (oldPos == newPos)
continue;
Point oldXY = getCoorFromIndex(oldPos);
Point newXY = getCoorFromIndex(newPos);
Point oldOffset = new Point(oldXY.x - v.getLeft(), oldXY.y
- v.getTop());
Point newOffset = new Point(newXY.x - v.getLeft(), newXY.y
- v.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);
v.clearAnimation();
v.startAnimation(translate);
newPositions.set(i, newPos);
}
}
protected void reorderChildren() {
// FIGURE OUT HOW TO REORDER CHILDREN WITHOUT REMOVING THEM ALL AND
// RECONSTRUCTING THE LIST!!!
if (onRearrangeListener != null)
onRearrangeListener.onRearrange(dragged, lastTarget);
ArrayList<View> children = new ArrayList<View>();
for (int i = 0; i < getChildCount(); i++) {
getChildAt(i).clearAnimation();
children.add(getChildAt(i));
}
removeAllViews();
while (dragged != lastTarget)
if (lastTarget == children.size()) // dragged and dropped to the
// right of the last element
{
children.add(children.remove(dragged));
dragged = lastTarget;
} else if (dragged < lastTarget) // shift to the right
{
Collections.swap(children, dragged, dragged + 1);
dragged++;
} else if (dragged > lastTarget) // shift to the left
{
Collections.swap(children, dragged, dragged - 1);
dragged--;
}
for (int i = 0; i < children.size(); i++) {
newPositions.set(i, -1);
addView(children.get(i));
}
onLayout(true, getLeft(), getTop(), getRight(), getBottom());
}
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);
}
// OTHER METHODS
public void setOnRearrangeListener(OnRearrangeListener l) {
this.onRearrangeListener = l;
}
public void setOnItemClickListener(OnItemClickListener l) {
this.onItemClickListener = l;
}
}
然后是在布局文件中声明:main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<com.way.view.DragGridView
android:id="@+id/vgv"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true" />
<Button
android:id="@+id/add_item_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:text="@string/button1Text" />
<Button
android:id="@+id/view_poem_item"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_toRightOf="@+id/add_item_btn"
android:text="@string/button2Text" />
</RelativeLayout>
最后就可以直接在代码中调用了MainActivity:
/**
* MainActivity
*
* @author way
*
*/
public class MainActivity extends Activity {
static Random random = new Random();
static String[] words = "我 是 一 只 大 笨 猪".split(" ");
DragGridView mDragGridView;
Button mAddBtn, mViewBtn;
ArrayList<String> poem = new ArrayList<String>();
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mDragGridView = ((DragGridView) findViewById(R.id.vgv));
mAddBtn = ((Button) findViewById(R.id.add_item_btn));
mViewBtn = ((Button) findViewById(R.id.view_poem_item));
setListeners();
}
private void setListeners() {
mDragGridView.setOnRearrangeListener(new OnRearrangeListener() {
public void onRearrange(int oldIndex, int newIndex) {
String word = poem.remove(oldIndex);
if (oldIndex < newIndex)
poem.add(newIndex, word);
else
poem.add(newIndex, word);
}
});
mDragGridView.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> arg0, View arg1, int arg2,
long arg3) {
mDragGridView.removeViewAt(arg2);
poem.remove(arg2);
}
});
mAddBtn.setOnClickListener(new OnClickListener() {
public void onClick(View arg0) {
String word = words[random.nextInt(words.length)];
ImageView view = new ImageView(MainActivity.this);
view.setImageBitmap(getThumb(word));
mDragGridView.addView(view);
poem.add(word);
}
});
mViewBtn.setOnClickListener(new OnClickListener() {
public void onClick(View arg0) {
String finishedPoem = "";
for (String s : poem)
finishedPoem += s + " ";
new AlertDialog.Builder(MainActivity.this).setTitle("这是你选择的")
.setMessage(finishedPoem).show();
}
});
}
private Bitmap getThumb(String s) {
Bitmap bmp = Bitmap.createBitmap(150, 150, Bitmap.Config.RGB_565);
Canvas canvas = new Canvas(bmp);
Paint paint = new Paint();
paint.setColor(Color.rgb(random.nextInt(128), random.nextInt(128),
random.nextInt(128)));
paint.setTextSize(24);
paint.setFlags(Paint.ANTI_ALIAS_FLAG);
canvas.drawRect(new Rect(0, 0, 150, 150), paint);
paint.setColor(Color.WHITE);
paint.setTextAlign(Paint.Align.CENTER);
canvas.drawText(s, 75, 75, paint);
return bmp;
}
}
很简单的几段代码,相信不用我说,大家都能看明白,最后,附上源码: