Kuwapp's Blog

Android と Flutter やってます

【Android】ViewPagerを使ってListViewをカスタマイズ!ボタンがニョッキっと出てくるListViewを作る。iOS風?【VIewPager】

iOS風なんて書いちゃってますが、普段Android使ってないのでAndroidでもよくあるものだったらごめんなさい(泣)
まずは完成形の動画を。


各要素をスワイプすると、ボタンがにょっきと出てくるListViewです。
検索しても見つからないし、作るのに時間がかかったので記事にしてメモメモ。

・ViewPagerって?

スワイプ動作で、ページ送りができるライブラリです。
ギャラリーでスワイプすると写真がスライドして次の写真が表示されますよね。
ああいったものを簡単に作ることができます。
ListViewをカスタマイズするときにAdapterをセットしますが、ViewPagerも同じようにAdapterをセットして実装します。
今回はこのViewPagerをListViewのそれぞれの要素に実装します。

・どう実装するか

今回は1ページ目の内容を残しつつ、ボタンが表れた際、ボタンの幅だけ1ページ目の内容を左に押し出すようにしたいです。
ですがViewPagerは普通に実装すると、ページが入れ替わってしまいます。
そこで、2つのページを重ねるようにすると、上手くいきます。

2つのページを重ねて2ページ目の削除ボタンは右寄せにしておいて画面外に出し、残った左部分を透明にして1ページ目と重ねます。
スワイプが行われると、1ページ目と2ページ目が入れ替わりますが、2つのページは重なっていて2ページ目の左部分は透明なので、1ページ目の右部分は残り、押し出されるように見えます。


・用意するもの

・main.xml
 ListViewを表示するメインのレイアウト。
・row.xml
 ListViewの行のレイアウト。
・page1.xml
 ViewPagerで切り替える1ページ目のレイアウト。
・page2.xml
 ViewPagerで切り替える2ページ目のレイアウト。
・MainActivity.java
・MyListAdapter.java
・MyPagerAdapter.java

main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <ListView
        android:id="@+id/listView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
    </ListView>

</LinearLayout>

ListViewを配置するだけのレイアウト。

row.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
 
<android.support.v4.view.ViewPager
    android:id="@+id/viewpager"
    android:layout_width="match_parent"
    android:layout_height="60dp"
    android:layout_gravity="center" />
 
</LinearLayout>

ListViewの行レイアウト。ViewPagerを記述しておき、ここにページを表示します。

page1.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#ffffff"
    android:orientation="vertical" >

    <TextView
        android:id="@+id/text"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:textColor="#000000"
        android:singleLine="true"
        android:textSize="20sp" />

</LinearLayout>

1ページ目はテキストを表示するだけです。

page2.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal" >

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:background="#00000000" >

    </RelativeLayout>

    <Button
        android:id="@+id/button2"
        android:layout_width="70dp"
        android:layout_height="match_parent"
        android:background="#cc2828"
        android:text="削除"
        android:textColor="#ffffff" />

</LinearLayout>

このレイアウトで定義しているRelativerLayoutをページ1と重ねます。
重ねるとページ1の内容が見えなくなるので、RelativeLayoutを透過しています。

MyListAdapter.java
public class MyListAdapter extends ArrayAdapter<String>{
	
	private LayoutInflater inflater = null;
	private static final float BUTTON_WIDTH_DP = 70f;
	private int margin;

	public MyListAdapter(Context context, int resource,String[] items) {
		super(context, resource,items);
		inflater = LayoutInflater.from(context);
		
		//ページ2のRelativeLayoutの幅を計算してmarginへ格納する。
		float density = getContext().getResources().getDisplayMetrics().density; 
		int buttonWidthPX = (int) (BUTTON_WIDTH_DP * density + 0.5f);
		
		WindowManager wm = (WindowManager)getContext().getSystemService(getContext().WINDOW_SERVICE);
        Display dp = wm.getDefaultDisplay();
		margin = dp.getWidth() - buttonWidthPX;
		
	}

	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
		
		if(convertView == null){
			convertView = inflater.inflate(R.layout.row,null);
		}
		
		ViewPager viewPager = (ViewPager)convertView.findViewById(R.id.viewpager);
		viewPager.setPageMargin(-margin);
		MyPagerAdapter adapter = new MyPagerAdapter(getContext(),getItem(position));
		viewPager.setAdapter(adapter);
			
		return convertView;
	}
}

透過したページ2のRelattiveLayoutの幅を端末の画面幅から削除ボタンの幅を引き、marginに格納しておきます。

viewPager.setPageMargin(-margin);
ページ1とページ2を重ねるために、RelativeLayoutの幅の分マイナスにマージンをとります。

MyPagerAdapter.java
public class MyPagerAdapter extends PagerAdapter{
	
	private LayoutInflater inflater;
	
	private static final int PAGE_NUM = 2;
	private String str;
	
	public MyPagerAdapter(Context context,String str) {
		super();
		inflater = LayoutInflater.from(context);
		this.str = str;
	}

	@Override
	public Object instantiateItem(ViewGroup container, int position) {
		
		LinearLayout layout = null;
		if(position == 0){
			layout = (LinearLayout)inflater.inflate(R.layout.page1, null);
			TextView text = (TextView)layout.findViewById(R.id.text);
			text.setText(str);
		}else{
			layout = (LinearLayout)inflater.inflate(R.layout.page2, null);
		}

		container.addView(layout);

		return layout;
	}
	
	@Override
	public void destroyItem(ViewGroup container, int position, Object object) {
		((ViewPager) container).removeView((View) object);
	}
	
	@Override
	public int getCount() {
		
		return PAGE_NUM;
	}

	@Override
	public boolean isViewFromObject(View view, Object obj) {
		
		return view.equals(obj);
	}

}

ページ1とページ2のレイアウトの切り替え処理を記述。

MainActivity.java
public class MainActivity extends Activity {

	private String str[] = {"項目1","項目2","項目3","項目4","項目5","項目6","項目7","項目8","項目9","項目10"};
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		
		ListView listView = (ListView)findViewById(R.id.listView);
		MyListAdapter adapter = new MyListAdapter(this,R.layout.row,str);
		listView.setAdapter(adapter);
	}
}


こんな感じのコードで実装できました。
もっと簡単な方法があったり、間違っていたり何かあれば教えて下さい。
よろしくー

(追記)
これってアニメーション使えば簡単に実装できるんだろか・・・