目指すもの
グリッド表示されたアプリ一覧から、アイコン長押しでドラッグ&ドロップし、任意の場所へアプリを移動する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 件のコメント:
コメントを投稿