一個可以上下左右滑動且當前項居中的ViewPager
背景介紹
最近專案需求,做一個訂製的ViewPager,要求在能夠左右滑動的同時,ViewPager的item本身也是一個疊放的容器且可上下滑動實現業務要求,如上滑刪除、下滑加入收藏等,於是寫一篇部落格記錄一下控制元件的實現。
首先來看一下效果:
基本思路
外層為ViewPager,實現條目的左右滑動
ViewPager的子Item為RecyclerView,自定義該RecyclerView的LayoutManager及ItemTouchHelper.SimpleCallback實現控制元件疊放及上下滑動
實現過程
首先是佈局檔案:
<android.support .constraint.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"
android:clipChildren="false"
tools:context="com.mapleaf.centerviewpager.MainActivity">
<android.support.v4.view.ViewPager
android:id="@+id/viewpager"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clipChildren="false"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
</android.support.constraint.ConstraintLayout>
記住viewpager及父控制元件都要新增android:clipChildren="false"
,這句程式碼的含義是子控制元件可以超過自己的位置顯示出來。
接下來初始化ViewPager:
private void initViewPager() {
mPagerAdapter = new CustomPagerAdapter<CustomRecyclerView<String>>();
mViewPager.setAdapter(mPagerAdapter);
mViewPager.setOffscreenPageLimit(2);
ViewGroup.LayoutParams layoutParams = mViewPager.getLayoutParams();
layoutParams.width = ((Activity) mViewPager.getContext()).getWindowManager().getDefaultDisplay().getWidth() / 21 * 10;
mTransformer = new ScaleTransformer();
mViewPager.setPageTransformer(false, mTransformer);
}
ScaleTransformer繼承自ViewPager.PageTransformer,它的作用是實現了ViewPager切換時的動畫效果,具體見程式碼。
然後來實現自定義的RecyclerView,其核心在於自定義LayoutManager實現層疊擺放;以及實現ItemTouchHelper.SimpleCallback來訂製上下滑動的操作。
自定義的LayoutManager程式碼如下:
public class SimilarItemLayoutManager extends RecyclerView.LayoutManager {
@Override
public RecyclerView.LayoutParams generateDefaultLayoutParams() {
return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
}
@Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
if (getItemCount() <= 0 || state.isPreLayout()) {
return;
}
detachAndScrapAttachedViews(recycler);
int visibleCount = 2;
if (getItemCount() < visibleCount) {
visibleCount = getItemCount();
}
for(int i=visibleCount;i>=1;i--){
View view=recycler.getViewForPosition(i-1);
addView(view);
measureChildWithMargins(view, 0, 0);
int widthSpace = getWidth() - getDecoratedMeasuredWidth(view);
int heightSpace = getHeight() - getDecoratedMeasuredHeight(view);
layoutDecorated(view,
widthSpace / 2,
heightSpace / 2,
widthSpace / 2 + getDecoratedMeasuredWidth(view),
heightSpace / 2 + getDecoratedMeasuredHeight(view));
}
}
}
因為我們的RecyclerView是層疊擺放,因此只需要顯示上面的2個view即可,最終將view擺放在RecyclerView的中間。
最後就是實現ItemTouchHelper.SimpleCallback
public class ItemSwipeCallBack extends ItemTouchHelper.SimpleCallback {
private CustomRecyclerViewAdapter mAdapter;
private ViewPager mViewPager;
public ItemSwipeCallBack(CustomRecyclerViewAdapter adapter, ViewPager viewPager) {
super(0, ItemTouchHelper.UP | ItemTouchHelper.DOWN);
mAdapter = adapter;
mViewPager = viewPager;
}
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
return false;
}
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
if(direction== ItemTouchHelper.UP){
//上滑將該頁插入最後
mAdapter.moveItemToBottom(viewHolder.getAdapterPosition());
}else if(direction== ItemTouchHelper.DOWN){
viewHolder.itemView.animate()
.translationYBy(1000)
.scaleX(0)
.scaleY(0)
.rotation(720)
.setDuration(500)
.setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
mViewPager.setCurrentItem(mViewPager.getCurrentItem() + 1, true);
}
});
}
}
@Override
public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
if (dY < 0) {
viewHolder.itemView.setTranslationY(dY);
} else if (dY < viewHolder.itemView.getHeight()) {
viewHolder.itemView.setTranslationY(dY);
}
}
}
因為在我的控制元件裡只需要上下滑動,因此在建構函式中super(0, ItemTouchHelper.UP | ItemTouchHelper.DOWN);
第一個引數0表示禁止拖動,第二個引數表示支援上下滑動。
在onSwiped
中可以監測到上下滑動,實現自己的邏輯即可,我在上滑時將第一個元素刪除並新增到最後,在下滑時做了動畫並移動ViewPager到下一個條目。
在onChildDraw
中可以自定義動畫,想幹嘛幹嘛(~ ̄▽ ̄)~
那麼,最後一步就是將上面兩個自定義的東東和我們的RecyclerView關聯起來即可:
SimilarItemLayoutManager layoutManager = new SimilarItemLayoutManager();
setLayoutManager(layoutManager);
ItemSwipeCallBack callback=new ItemSwipeCallBack(adapter,viewPager);
ItemTouchHelper helper=new ItemTouchHelper(callback);
helper.attachToRecyclerView(this);