こんにちはandroidでアプリ開発を学習中のみのむしと申します。
今回は、ViewPager2を使ってカルーセルを実装しましたので、
備忘録として残したいと思います。
レイアウトと実装方法について
今回は、CardViewとViewPager2を使ってmaterialdesign風にレイアウトしたいと思います。また、参考サイトのほとんどがkotlinによる実装でしたので、
今回はjavaを用いた実装としたいと思います。
前提知識
offset
:横にアイテム(今回はカード)をどれだけ見せるか設定する
margin
:アイテム間の距離をどれだけとるか設定する
実装
activity.main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/cardViewPager"
android:layout_width="match_parent"
android:layout_height="300dp"
android:orientation="horizontal"
android:layout_marginTop="40dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
calendar_cell.xml
<androidx.cardview.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginStart="36dp"
android:layout_marginEnd="36dp"
app:cardBackgroundColor="@color/white"
app:cardCornerRadius="16dp"
app:cardElevation="7dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="300dp">
<ImageView
android:id="@+id/CardImage"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/image1"
tools:ignore="ContentDescription" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
ポイントは、ViewPager2のorientationを
android:orientation="horizontal"
と指定し、横方向にスライドできるように指定しているところです。
縦方向のスライドを実装したい場合は、verticalと設定すれば可能です。
また、注意点としてはCardViewのheightとwidthはmatch_parentに設定しないと以下Exceptionが発生します。
java.lang.IllegalStateException: Pages must fill the whole ViewPager2 (use match_parent)
MainActivity.java
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.RecyclerView;
import androidx.viewpager2.widget.CompositePageTransformer;
import androidx.viewpager2.widget.MarginPageTransformer;
import androidx.viewpager2.widget.ViewPager2;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.view.View;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
//ViewPager2を取得する
private ViewPager2 viewPager2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
viewPager2 = findViewById(R.id.cardViewPager);
//作成したスライダーのリストを連携させる
List<ImageModel> list = new ArrayList<>();
list.add(new ImageModel(R.drawable.image1));
list.add(new ImageModel(R.drawable.image2));
list.add(new ImageModel(R.drawable.image3));
list.add(new ImageModel(R.drawable.image4));
list.add(new ImageModel(R.drawable.image5));
viewPager2.setAdapter(new CardAdapter(list, viewPager2));
viewPager2.setClipToPadding(false);
viewPager2.setClipChildren(false);
viewPager2.setOffscreenPageLimit(3);
viewPager2.getChildAt(0).setOverScrollMode(RecyclerView.OVER_SCROLL_NEVER);
CompositePageTransformer compositePageTransformer = new CompositePageTransformer();
compositePageTransformer.addTransformer(new MarginPageTransformer(40));
compositePageTransformer.addTransformer(new ViewPager2.PageTransformer() {
@Override
public void transformPage(@NonNull View page, float position) {
//カルーセルを作成する処理
float offset = position * (dpFormat(2) * dpFormat(10) + dpFormat(10));
page.setTranslationX(-offset);
}
});
//scrollした際のAnimationを設定する
viewPager2.setPageTransformer(compositePageTransformer);
}
//pxをdpに変換する
private int dpFormat(int dp) {
DisplayMetrics displayMetrics = getApplicationContext().getResources().getDisplayMetrics();
return Math.round(dp * (displayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT));
}
}
CalendarAdapter.java
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import androidx.viewpager2.widget.ViewPager2;
import java.util.List;
public class CardAdapter extends RecyclerView.Adapter<CardAdapter.MyCardViewAdapter> {
private final List<ImageModel> Item;
ViewPager2 viewPager2;
public CardAdapter(List<ImageModel> item, ViewPager2 viewPager2) {
this.Item = item;
this.viewPager2 = viewPager2;
}
@NonNull
@Override
public MyCardViewAdapter onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.calendar_cell,parent,false);
return new MyCardViewAdapter(view);
}
@Override
public void onBindViewHolder(@NonNull MyCardViewAdapter holder, int position) {
holder.setImageview(Item.get(position));
}
@Override
public int getItemCount() {
return Item.size();
}
public static class MyCardViewAdapter extends RecyclerView.ViewHolder{
private final ImageView Imageview;
public MyCardViewAdapter(@NonNull View itemView) {
super(itemView);
Imageview = itemView.findViewById(R.id.CardImage);
}
private void setImageview(@NonNull ImageModel adapter){
Imageview.setImageResource(adapter.getImage());
}
}
}
ImageModel.java
public class ImageModel {
private final int image;
ImageModel (int image){
this.image = image;
}
public int getImage() {
return image;
}
}
応用編
応用編としてチラ見させるアイテムのセルにアニメーションをつけるバージョン
MainActivity.java
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.RecyclerView;
import androidx.viewpager2.widget.CompositePageTransformer;
import androidx.viewpager2.widget.MarginPageTransformer;
import androidx.viewpager2.widget.ViewPager2;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.view.View;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//ViewPager2を取得する
ViewPager2 viewPager2 = findViewById(R.id.cardViewPager);
//作成したスライダーのリストを連携させる
List<ImageModel> list = new ArrayList<>();
list.add(new ImageModel(R.drawable.image1));
list.add(new ImageModel(R.drawable.image2));
list.add(new ImageModel(R.drawable.image3));
list.add(new ImageModel(R.drawable.image4));
list.add(new ImageModel(R.drawable.image5));
viewPager2.setAdapter(new CardAdapter(list, viewPager2));
viewPager2.setClipToPadding(false);
viewPager2.setClipChildren(false);
viewPager2.setOffscreenPageLimit(3);
viewPager2.getChildAt(0).setOverScrollMode(RecyclerView.OVER_SCROLL_NEVER);
CompositePageTransformer compositePageTransformer = new CompositePageTransformer();
compositePageTransformer.addTransformer(new MarginPageTransformer(40));
compositePageTransformer.addTransformer(new ViewPager2.PageTransformer() {
@Override
public void transformPage(@NonNull View page, float position) {
//カルーセルを作成する処理
float offset = position * (dpFormat(2) * dpFormat(10) + dpFormat(10));
float r = 1-Math.abs(position);
page.setTranslationX(-offset);
page.setScaleY(0.85f + r * 0.15f);
}
});
//scrollした際のAnimationを設定する
viewPager2.setPageTransformer(compositePageTransformer);
}
//PxをDpに変換する
private int dpFormat(int dp) {
DisplayMetrics displayMetrics = getApplicationContext().getResources().getDisplayMetrics();
return Math.round(dp * (displayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT));
}
}