スマートフォンアプリでよく見られるスワイプによる削除操作は、SwipeListViewライブラリを使用することで容易に実現できます。本稿では、このライブラリの導入と実装手順を解説します。
1. ライブラリの準備
GitHubから「com.fortysevendeg.swipelistview」プロジェクトをダウンロードします(現在はGitHub上から削除されていますが、他のソースから入手可能です)。Eclipseにインポート後、プロジェクトを右クリックし、「Properties」→「Android」を選択、「Library」セクションで「IsLibrary」にチェックを入れてライブラリ化します。
2. 新規プロジェクトの作成とライブラリの組み込み
「MySwipeListView」という新規Androidプロジェクトを作成し、先ほど準備したSwipeListViewライブラリをプロジェクトに追加します(ビルドパスを通す)。
3. レイアウトファイルの設定
メインアクティビティのレイアウトファイル(例: activity_main.xml)に、SwipeListViewウィジェットを配置します。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity">
<com.fortysevendeg.swipelistview.SwipeListView
xmlns:swipe="http://schemas.android.com/apk/res-auto"
android:id="@+id/swipe_list"
android:listSelector="@android:color/transparent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
swipe:swipeBackView="@+id/back_panel"
swipe:swipeCloseAllItemsWhenMoveList="true"
swipe:swipeDrawableChecked="@drawable/choice_selected"
swipe:swipeDrawableUnchecked="@drawable/choice_unselected"
swipe:swipeFrontView="@+id/front_panel"
swipe:swipeMode="both"
swipe:swipeActionLeft="reveal"
swipe:swipeActionRight="dismiss"
swipe:swipeOpenOnLongPress="true" />
</LinearLayout>
上記の属性のうち、特に重要なものを説明します。
swipeFrontView: スワイプしていない状態で表示される前面ビューのIDを指定します。swipeBackView: スワイプ後に表示される背面ビューのIDを指定します。
これらのビューは、SwipeListViewの各行のレイアウトファイル内で定義します。以下は行レイアウトの例です(例: list_item.xml)。
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<!-- 背面パネル(スワイプで表示) -->
<LinearLayout
android:id="@+id/back_panel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#CCCCCC"
android:gravity="right"
android:tag="back">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/btn_delete"
android:text="削除" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/btn_edit"
android:text="編集" />
</LinearLayout>
<!-- 前面パネル(通常表示) -->
<RelativeLayout
android:id="@+id/front_panel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#FFFFFF">
<ImageView
android:layout_width="48dp"
android:layout_height="48dp"
android:id="@+id/row_icon" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/row_icon"
android:id="@+id/row_title" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/row_icon"
android:layout_below="@id/row_title"
android:id="@+id/row_description" />
</RelativeLayout>
</FrameLayout>
行レイアウトはFrameLayoutをルート要素とします。FrameLayout内のすべての子ビューは左上に重なって配置されるため、前面ビューと背面ビューが重なり、スワイプで前面が移動すると背面が現れる動作が実現します。
4. アダプターの実装
SwipeListViewは標準のListViewと同様にアダプターを使用します。独自のアダプタークラスを作成し、getView()メソッドで行レイアウトをinflateします。アダプターの基本的な実装は通常のListViewと変わりませんので、ここでは割愛します。
5. メインアクティビティでの制御
メインアクティビティでSwipeListViewのリスナーを設定し、スワイプ時の動作を定義します。
package com.example.myswipe;
import android.os.Bundle;
import android.util.Log;
import androidx.appcompat.app.AppCompatActivity;
import com.fortysevendeg.swipelistview.BaseSwipeListViewListener;
import com.fortysevendeg.swipelistview.SwipeListView;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
private static final String LOG_TAG = "SwipeDemo";
private SwipeListView swipeListView;
private MyAdapter adapter;
private ArrayList<String> dataList;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initData();
swipeListView = findViewById(R.id.swipe_list);
adapter = new MyAdapter(this, dataList, swipeListView);
swipeListView.setAdapter(adapter);
swipeListView.setSwipeListViewListener(new BaseSwipeListViewListener() {
@Override
public void onOpened(int position, boolean toRight) {
Log.d(LOG_TAG, "アイテムが開かれました: 位置=" + position + ", 右方向=" + toRight);
}
@Override
public void onClosed(int position, boolean fromRight) {
Log.d(LOG_TAG, "アイテムが閉じられました: 位置=" + position + ", 右から=" + fromRight);
}
@Override
public void onChoiceChanged(int position, boolean selected) {
Log.d(LOG_TAG, "選択状態が変更されました: 位置=" + position + ", 選択=" + selected);
}
@Override
public void onStartOpen(int position, int action, boolean right) {
Log.d(LOG_TAG, "開く動作開始: 位置=" + position + ", アクション=" + action + ", 右=" + right);
}
@Override
public void onStartClose(int position, boolean right) {
Log.d(LOG_TAG, "閉じる動作開始: 位置=" + position + ", 右=" + right);
}
@Override
public void onClickFrontView(int position) {
Log.d(LOG_TAG, "前面ビューがクリックされました: 位置=" + position);
}
@Override
public void onClickBackView(int position) {
Log.d(LOG_TAG, "背面ビューがクリックされました: 位置=" + position);
}
@Override
public void onDismiss(int[] reverseSortedPositions) {
Log.d(LOG_TAG, "アイテムが却下されました");
// ここでデータ削除処理など
}
@Override
public void onListChanged() {
Log.d(LOG_TAG, "リストが変更されました");
swipeListView.closeOpenedItems();
}
});
}
private void initData() {
dataList = new ArrayList<>();
for (int i = 0; i < 15; i++) {
dataList.add("データ項目 " + i);
}
}
}
重要なのはsetSwipeListViewListener()メソッドです。これにより、BaseSwipeListViewListenerを継承したリスナーを設定し、各イベントに対応する処理をオーバーライドします。例えば、onDismissでアイテム削除、onClickBackViewで背面ボタンのクリック処理などを行います。
6. 実行結果
アプリを実行すると、リスト項目をスワイプすることで背面の操作ボタンが表示されます。横方向のスワイプで前面ビューが移動し、背面ビューが現れるアニメーションが確認できます。