3月 03

今回は2つの View の間でアイテムを Drag and Drop で行き来させるサンプルです。

まず、上下に2つの GridView ベースのカスタムビューを用意します。その中に予め5つのアイテムを配置します。これらのアイテムは Drag and Drop で上下の View を行き来させることができます。

device-1.png device-2.png

サンプルコード

このサンプルは明日の鍵さんのブログの記事を参考にさせてもらいました。

res/layout/main.xml

Drag and Drop をサポートした GridView ベースのカスタムビュー (sak.samples.DragAndDropView) を2つ配置します。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
    <sak.samples.DragAndDropView
        android:id="@+id/list"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_weight="1"
        android:background="#2f88"
        android:numColumns="5"
        android:layout_margin="10dp"
        android:padding="10dp"
        />
    <sak.samples.DragAndDropView
        android:id="@+id/list2"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_weight="1"
        android:background="#28f8"
        android:numColumns="5"
        android:layout_margin="10dp"
        android:padding="10dp"
        />
</LinearLayout>

res/layout/item.xml

View に表示するアイテムは ImageView と TextView をそれぞれ1つずつ持っています。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    >
    <ImageView
        android:id="@+id/icon"
        android:src="@drawable/icon"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:scaleType="center"
        />
    <TextView
        android:id="@+id/name"
        android:text="dummy"
        android:textSize="12dp"
        android:gravity="center_horizontal"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        />
</LinearLayout>

src/sak/samples/DragAndDropDemo

View の定義と表示を行います。アイテムは ArrayList で管理し、表示には ArrayAdapter ベースのカスタムクラスを用いています。

public class DragAndDropDemo extends Activity {
    private DragAndDropView mView1;
    private DragAndDropView mView2;
    private MyAdapter mAdapter1;
    private MyAdapter mAdapter2;
    private ArrayList<Item> mItems1 = new ArrayList<Item>();
    private ArrayList<Item> mItems2 = new ArrayList<Item>();
   
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        /*
         * 5つの要素を用意する。
         */

        mItems1.add(new Item(R.drawable.icon, "1"));
        mItems1.add(new Item(R.drawable.icon, "2"));
        mItems1.add(new Item(R.drawable.icon, "3"));
        mItems1.add(new Item(R.drawable.icon, "4"));
        mItems1.add(new Item(R.drawable.icon, "5"));

        /*
         * 上の View の定義
         */

        mAdapter1 = new MyAdapter(this, mItems1);
       
        mView1 = (DragAndDropView) findViewById(R.id.list);
        mView1.setAdapter(mAdapter1);
        mView1.setOnDragnDropListener(new DragAndDropListener() {
            @Override
            public void dropped(int from, int x, int y) {
                Rect rect = new Rect();
                mView2.getHitRect(rect);
                if (rect.contains(x, y)) {
                    Item item = mAdapter1.getItem(from);
                    mAdapter1.remove(item);
                    mAdapter2.add(item);
                }
            }
        });
       
        /*
         * 下の View の定義
         */

        mAdapter2 = new MyAdapter(this, mItems2);
       
        mView2 = (DragAndDropView) findViewById(R.id.list2);
        mView2.setAdapter(mAdapter2);
        mView2.setOnDragnDropListener(new DragAndDropListener() {
            @Override
            public void dropped(int from, int x, int y) {
                Rect rect = new Rect();
                mView1.getHitRect(rect);
                if (rect.contains(x, y)) {
                    Item item = mAdapter2.getItem(from);
                    mAdapter2.remove(item);
                    mAdapter1.add(item);
                }
            }
        });
    }

    /*
     * 要素
     */

    private class Item {
        public int icon;        // リソースID
        public String name;
        Item(int icon, String name) {
            this.icon = icon;
            this.name = name;
        }
    }
   
    /*
     * 表示用アダプタ
     */

    private class MyAdapter extends ArrayAdapter<Item> {
        private LayoutInflater mInflater;
        private ArrayList<Item> items;
       
        public MyAdapter(Context context, ArrayList<Item> items) {
            super(context, R.layout.item, items);
            mInflater = LayoutInflater.from(context);
            this.items = items;
        }

        public View getView(int position, View convertView, ViewGroup parent) {
            ViewHolder holder = null;
            if (convertView == null) {
                convertView = mInflater.inflate(R.layout.item, null);
                holder = new ViewHolder();
                holder.ivIcon = (ImageView) convertView.findViewById(R.id.icon);
                holder.tvName = (TextView) convertView.findViewById(R.id.name);
                convertView.setTag(holder);
            } else {
                holder = (ViewHolder) convertView.getTag();
            }
            holder.ivIcon.setImageResource(items.get(position).icon);
            holder.tvName.setText(items.get(position).name);
            return convertView;
        }
    }

    class ViewHolder {
        ImageView ivIcon;
        TextView tvName;
    }
}

src/sak/samples/DragAndDropView

Drag And Drop の要のクラスです。GridView を継承したカスタムクラスを作成します。

public class DragAndDropView extends GridView {
     
    private DragAndDropListener mListener;
    private WindowManager mWm;
    private WindowManager.LayoutParams mWindowParams;
    private ImageView mDragView = null;
    private Bitmap mDragBitmap = null;
    private int mFromPosition = AdapterView.INVALID_POSITION;

    public DragAndDropView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);

        mWm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
       
        mWindowParams = new WindowManager.LayoutParams();
        mWindowParams.gravity = Gravity.TOP | Gravity.LEFT;
        mWindowParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
        mWindowParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
        mWindowParams.flags =
            WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
            WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE |
            WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON |
            WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
        mWindowParams.format = PixelFormat.TRANSLUCENT;
        mWindowParams.windowAnimations = 0;
        mWindowParams.x = 0;
        mWindowParams.y = 0;
    }

    public DragAndDropView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    public void setOnDragnDropListener(DragAndDropListener listener) {
        mListener = listener;
    }
   
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        final int x = (int) event.getX();
        final int y = (int) event.getY();

        int action = event.getAction();

        if (action == MotionEvent.ACTION_DOWN) {
            mFromPosition = pointToPosition(x, y);
            if (mFromPosition == AdapterView.INVALID_POSITION)
                return false;
            startDrag();
            updateLayout(x, y);
            return true;
         
        } else if (action == MotionEvent.ACTION_MOVE) {
            updateLayout(x, y);
            return true;

        } else if (action == MotionEvent.ACTION_UP) {
            if (mListener != null) {
                mListener.dropped(mFromPosition, (int)event.getRawX(), (int)event.getRawY());   // Callback
            }
            endDrag();
            return true;
         
        } else if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_OUTSIDE) {
            endDrag();
            return true;
        }

        return super.onTouchEvent(event);
    }
   
    private void updateLayout(int x, int y) {
        mWindowParams.x = getLeft() - getPaddingLeft() + x;
        mWindowParams.y = getTop() - getPaddingTop() + y;

        mWm.updateViewLayout(mDragView, mWindowParams);
    }

    private void startDrag() {
        View view = getChildByPosition(mFromPosition);
        mDragBitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas();
        canvas.setBitmap(mDragBitmap);
        view.draw(canvas);

        if (mDragView != null) {
            mWm.removeView(mDragView);
        }

        mDragView = new ImageView(getContext());
        mDragView.setImageBitmap(mDragBitmap);

        mWm.addView(mDragView, mWindowParams);
    }

    private void endDrag() {
        mWm.removeView(mDragView);
        mDragView = null;
        mDragBitmap = null;
    }

    private View getChildByPosition(int position) {
        return getChildAt(position - getFirstVisiblePosition());
    }
}

src/sak/samples/DragAndDropListener

カスタムビューで発生したイベントを定義しています。

public interface DragAndDropListener {
    public void dropped(int from, int x, int y);
}


Posted by sak+

Tagged with:
preload preload preload