viewgroup实现item拖动效果

网络上关于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 
[html]  view plaincopy
<?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;  
    }  
}  

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏非著名程序员

Android学习笔记(一)之仿正点闹钟时间齿轮滑动的效果

看到正点闹钟上的设置时间的滑动效果非常好看,自己就想做一个那样的,在网上就开始搜资料了,看到网上有的齿轮效果的代码非常多,也非常难懂,我就决定自己研究一下,现在...

247100
来自专栏向治洪

android drawable

android.graphics.drawable.Drawable是个虚类。 它的直接子类有 BitmapDrawable, ClipDrawable, Co...

225100
来自专栏向治洪

android 实现倒影

首先,文章中出现的Gallery 已经不再适用,替代方法请看我的另一篇文章http://blog.csdn.net/xiangzhihong8/article...

24850
来自专栏向治洪

android自定义listview实现header悬浮框效果

之前在使用iOS时,看到过一种分组的View,每一组都有一个Header,在上下滑动的时候,会有一个悬浮的Header,这种体验觉得很不错,请看下图: ? ...

75860
来自专栏Android干货

安卓开发_慕课网_Fragment实现Tab(App主界面)

377100
来自专栏Android干货

安卓开发_深入学习ViewPager控件

39380
来自专栏Android开发指南

通过代码定义shape/selector

36190
来自专栏懒人开发

鸿洋AutoLayout代码分析(三):入口类分析

我们可以发现,只是做了name的判断, 如果是属性中的3中Layout, 会自动替换成 对应的AutoXXXLayout 如果不是,直接调用父类的 View...

12020
来自专栏Android干货

安卓开发_浅谈ListView(自定义适配器)

32960
来自专栏向治洪

android沉浸式状态栏的实现

在style.xml中添加 <style name="Theme.Timetodo" parent="@android:style/Theme.Holo.L...

27470

扫码关注云+社区

领取腾讯云代金券