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