目指すもの
グリッド表示されたアプリ一覧から、アイコン長押しでドラッグ&ドロップし、任意の場所へアプリを移動するUI
動画をとってみた
アプリの表示
まずは、アプリアイコンの表示を実装する。アプリアイコンは、TextViewへsetCompoundDrawablesWithIntrinsicBounds()メソッドでアイコン画像を指定することで実現する。
layout/appicon.xml
- <TextView xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingTop="4dp"
- android:textSize="12dp"
- android:singleLine="true"
- android:ellipsize="end"
- android:textColor="@android:color/white"
- android:gravity="center_horizontal|center_vertical" />
Javaコード
アプリアイコンを作るところ
- // アイコン画像をロード(とりあえず電話っぽいアイコンにしとく)
- Drawable icon = getResources().getDrawable(android.R.drawable.ic_menu_call);
- // TextViewをXMLから生成
- TextView appIcon
- = (TextView) getLayoutInflater().inflate(R.layout.appicon, null);
- // アプリ名称を設定
- appIcon.setText("でんわ");
- // アイコンを設定(left, top, right, bottomの順で指定)
- appIcon.setCompoundDrawables(null, icon, null, null);
アプリアイコンをコンテナ(ここではRelativeLayout)へ追加する
- // コンテナへ追加
- RelativeLayout c = (RelativeLayout) findViewById(R.id.container);
- c.addView(appIcon);
Viewをドラッグ可能にする
次に、アプリアイコンをドラッグできるようにする。TextViewのstartDrag()メソッドを呼び出せば良く、長押しでドラッグ開始とするため、OnLongClickListenerを実装してアプリアイコンへセットする。
特筆すべき点として、ドロップ先へデータを渡すためstartDrag()の第3引数にViewそのものをセットしておく。
- /**
- * Viewの長押しイベントをハンドリングするリスナークラス
- */
- private static class MyLongClickListener implements View.OnLongClickListener
- {
- /**
- * Viewが長押しされた際にシステムから呼び出される。
- */
- @Override
- public boolean onLongClick(View v)
- {
- // ドラッグ&ドロップで受け渡しするデータ(使わないのでダミー)
- ClipData tmpData = ClipData.newPlainText("dummy", "dummy");
- // ドラッグ中に表示するイメージのビルダー
- View.DragShadowBuilder shadow = new View.DragShadowBuilder(v);
- // ドラッグを開始
- v.startDrag(tmpData, shadow, v, 0);
- return true;
- }
- }
アプリアイコンへリスナーをセットする
- MyLongClickListener listener = new MyLongClickListener();
- appIcon.setOnLongClickListener(listener);
以上でドラッグできるようになっている、が、実行しても見た感じだと出来てるのか分からない。次項の「ドラッグ中に表示するイメージの制御」が必要。
ドラッグ中に表示するイメージの制御
基本的に、ドラッグ中に表示するイメージはDragShadowBuilderで制御しており、デフォルト実装(View#draw(Canvas)を呼び出す)で問題ない。
しかし、TextViewをドラッグ対象とした場合には意図した動作とならない。DragShadowBuilderクラスを継承して、TextViewのキャプチャを表示するようにカスタマイズする。
Javaコード
- /**
- * ドラッグ&ドロップ中に表示するイメージを制御するクラス
- */
- private static class MyDragShadowBuilder extends DragShadowBuilder
- {
- /**
- * コンストラクタ
- */
- public MyDragShadowBuilder(View v) {
- super(v);
- }
- /**
- * ドラッグ中のイメージを描画する際にシステムが呼び出すメソッド
- */
- @Override
- public void onDrawShadow(Canvas canvas)
- {
- // ドラッグ対象View
- View view = getView();
- // Viewのキャプチャを取得する準備
- view.setDrawingCacheEnabled(true);
- view.destroyDrawingCache();
- // キャプチャを取得し、キャンバスへ描画する
- Bitmap bitmap = view.getDrawingCache();
- canvas.drawBitmap(bitmap, 0f, 0f, null);
- }
- }
前述のOnLongClickListenerで実装していたイメージビルダーを作成したイメージビルダーへ置き換える。
- ...前略...
- // これを
- View.DragShadowBuilder shadow = new View.DragShadowBuilder(v);
- ↓
- // こうする
- View.DragShadowBuilder shadow = new MyDragShadowBuilder(v);
- ...後略...
ドロップ先を用意する
最後に、ドロップ先としてViewGroup(LinearLayoutとか)を用意しておく。ViewGroupには、ドラッグイベントをハンドリングするためのOnDragListenerをセットすればOK。
OnDragListenerの実装は以下の通り
- ACTION_DRAG_STARTEDを受けたらtrueを返す
- ACTION_DROPを受けたらアプリアイコンを作ってViewGroupに追加する
Javaコード
- /**
- * ドラッグイベントをハンドリングするリスナークラス
- */
- private static class MyDragListener implements View.OnDragListener
- {
- private LayoutInflater mInflater;
- /**
- * コンストラクタ
- */
- public MyDragListener(Context ctx)
- {
- mInflater = LayoutInflater.from(ctx);
- }
- /**
- * ドラッグイベントが発生した際に、システムから呼び出されるメソッド
- */
- @Override
- public boolean onDrag(View v, DragEvent event)
- {
- switch (event.getAction())
- {
- case DragEvent.ACTION_DRAG_STARTED:
- case DragEvent.ACTION_DRAG_ENTERED:
- case DragEvent.ACTION_DRAG_LOCATION:
- case DragEvent.ACTION_DRAG_EXITED:
- return true;
- case DragEvent.ACTION_DROP:
- //
- // ドラッグ元の情報を取得
- //
- // startDrag()の第3引数で渡したデータを取得
- TextView src = (TextView) event.getLocalState();
- // アプリ名
- CharSequence name = src.getText();
- // 画像
- Drawable[] imgs = src.getCompoundDrawables();
- //
- // 新しくドロップ先に設置するViewを生成
- //
- // アプリアイコンを新規作成(TextViewをXMLから生成)
- TextView appIcon
- = (TextView) mInflater.inflate(R.layout.appicon, null);
- // アプリ名称を設定
- appIcon.setText(name);
- // 画像を設定
- appIcon.setCompoundDrawables(
- imgs[0], imgs[1], imgs[2], imgs[3]);
- // ViewGroupへ追加
- ViewGroup c = (ViewGroup) v;
- c.addView(appIcon);
- return true;
- default:
- break;
- }
- return false;
- }
- }
ドロップ先(ここではLinearLayout)へOnDragListenerをセットする
- MyDragListener listener = new MyDragListener(this);
- LinearLayout dropPlace = (LinearLayout) findViewById(R.id.drop_place);
- dropPlace.setOnDragListener(mDragListener);
ドロップ先の背景
ドラッグしている際に、ドロップ先の背景が変わるようにしておく。以下のようにXMLで背景を作成し、ドロップ先に設定してあげるだけでOK
drawable/drop_place_background.xml
- <selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_drag_hovered="true">
- <shape>
- <solid android:color="#0affffff"></solid>
- <stroke
- android:color="#ffd700"
- android:dashgap="10dp"
- android:dashwidth="15dp"
- android:width="3dp">
- </stroke></shape>
- </item>
- <item>
- <shape>
- <solid android:color="#0affffff"></solid>
- </shape>
- </item>
- </selector>
おまけ:アプリ一覧の取得
アプリ一覧を取得するコード例は以下の通り。
- PackageManager manager = ctx.getPackageManager();
- // アプリ一覧を取得する条件
- Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
- mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
- // アプリ一覧を取得
- final List<ResolveInfo> apps = manager.queryIntentActivities(mainIntent, 0);
- Collections.sort(apps, new ResolveInfo.DisplayNameComparator(manager));
- // アプリ情報一覧を作成
- for (ResolveInfo each : apps)
- {
- // アプリ名を取得
- CharSequence label = each.loadLable(manager);
- // アイコンを取得
- Drawable icon = each.activityInfo.loadIcon(manager);
- ...略
- }
0 件のコメント:
コメントを投稿