最近在即刻里看到即刻的"猜你喜歡"的板塊,覺得效果很贊。
當點擊"換一換"時,上面三個條目程序切換效果,並且三個條目的切換以不同的速度進行。
於是開始想辦法擼出這樣的切換效果。
我的思路是使用豎直切換的ViewPager,因為之前使用過ViewPager的一些切換動畫和這里的切換很相似。
最后實現的兩種效果:
關於豎直切換的ViewPager,你能找到很多資料,我這里使用的是一個日本小哥的VerticalViewPager
他在onInterceptTouchEvent方法里巧妙的添加了swapTouchEvent方法,將左右滑動切換轉換為上下滑動,或者說當你上下滑動時,造成一個假象,viewpager以為你在上下切換。
package com.example.xw.jikeviewpager; /** * Created by xw on 2016/10/9. */ import android.content.Context; import android.support.v4.view.ViewPager; import android.util.AttributeSet; import android.view.MotionEvent; import Transformer.DefaultTransformer; public class VerticalViewPager extends ViewPager { public VerticalViewPager(Context context) { this(context, null); } public VerticalViewPager(Context context, AttributeSet attrs) { super(context, attrs); setPageTransformer(false, new DefaultTransformer()); } private MotionEvent swapTouchEvent(MotionEvent event) { float width = getWidth(); float height = getHeight(); float swappedX = (event.getY() / height) * width; float swappedY = (event.getX() / width) * height; event.setLocation(swappedX, swappedY); return event; } @Override public boolean onInterceptTouchEvent(MotionEvent event) { boolean intercept = super.onInterceptTouchEvent(swapTouchEvent(event)); //If not intercept, touch event should not be swapped. swapTouchEvent(event); return intercept; } @Override public boolean onTouchEvent(MotionEvent ev) { return super.onTouchEvent(swapTouchEvent(ev)); } }
關於DefaultTransformer類,是用來控制切換效果的,接下來我們會用ZoomOutTransformer。
關於這幾個類,代碼點我。
接下來發現即刻切換中的圖片都是圓角矩形,關於自定義一個圓角矩形ImageView,代碼如下,具體就不分析了。
public class RoundRectImageView extends ImageView{ private Paint paint; public RoundRectImageView(Context context) { this(context,null); } public RoundRectImageView(Context context, AttributeSet attrs) { this(context, attrs,0); } public RoundRectImageView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); paint = new Paint(); } /** * 繪制圓角矩形圖片 * @author caizhiming */ @Override protected void onDraw(Canvas canvas) { Drawable drawable = getDrawable(); if (null != drawable) { Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap(); Bitmap b = getRoundBitmap(bitmap, 20); final Rect rectSrc = new Rect(0, 0, b.getWidth(), b.getHeight()); final Rect rectDest = new Rect(0,0,getWidth(),getHeight()); paint.reset(); canvas.drawBitmap(b, rectSrc, rectDest, paint); } else { super.onDraw(canvas); } } /** * 獲取圓角矩形圖片方法 * @param bitmap * @param roundPx,一般設置成14 * @return Bitmap * @author caizhiming */ private Bitmap getRoundBitmap(Bitmap bitmap, int roundPx) { Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Config.ARGB_8888); Canvas canvas = new Canvas(output); final int color = 0xff424242; final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()); final RectF rectF = new RectF(rect); paint.setAntiAlias(true); canvas.drawARGB(0, 0, 0, 0); paint.setColor(color); int x = bitmap.getWidth(); canvas.drawRoundRect(rectF, roundPx, roundPx, paint); paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN)); canvas.drawBitmap(bitmap, rect, rect, paint); return output; } }
接下來我們實現第一種切換效果,以一個RoundRectImageView加textview作為fragment,然后將fragment作為VerticalViewPager的內容。
fragment_item.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="130dp" android:layout_height="130dp"> <com.example.xw.jikeviewpager.RoundRectImageView android:id="@+id/iv" android:scaleType="centerCrop" android:layout_width="match_parent" android:layout_height="120dp"/> <TextView android:id="@+id/tv" android:text="1111" android:layout_width="wrap_content" android:layout_height="wrap_content"/> </LinearLayout>
創建fragment類和一個item的實體類代碼就省略了。
要注意這里利用 Argument傳數據生成fragment。
public static MyFragment newInstance(int imgId,String text){ Bundle args=new Bundle(); args.putSerializable(ARG_IMG_ID,imgId); args.putSerializable(ARG_TEXT,text); MyFragment fragment=new MyFragment(); fragment.setArguments(args); return fragment; }
接下來在MainActivity中,我們的布局使用兩個VerticalViewPager和一個負責切換的button。
private void initViewPager() { int[] ims={R.drawable.kenan,R.drawable.mingren,R.drawable.lufei}; String[] text={"身體雖然變小,但頭腦依然靈活", "卡 給 分 新 諾 句 子 !!!", "我是要成為海賊王的男人!"}; for (int i = 0; i <ims.length ; i++) { Item item=new Item(ims[i],text[i]); list.add(item); } int[] ims2={R.drawable.qinnv,R.drawable.yasuo,R.drawable.timo}; String[] text2={ "為什么啞巴琴女會說話?", "死亡如風,常伴吾身", "timo隊長,正在送命" }; for (int i = 0; i <ims2.length ; i++) { Item item=new Item(ims2[i],text2[i]); list2.add(item); } viewPager.setPageTransformer(true, new ZoomOutTransformer()); viewPager2.setPageTransformer(true,new ZoomOutTransformer()); //viewPager.setPageTransformer(true, new StackTransformer()); FragmentManager fm=getSupportFragmentManager(); viewPager.setAdapter(new FragmentStatePagerAdapter(fm) { @Override public Fragment getItem(int position) { Item item=list.get(position); return MyFragment.newInstance(item.getImageId(),item.getText()); } @Override public int getCount() { return list.size(); } }); viewPager.setOverScrollMode(View.OVER_SCROLL_NEVER); viewPager.setCurrentItem(list.size()-1); viewPager2.setAdapter(new FragmentStatePagerAdapter(fm) { @Override public Fragment getItem(int position) { Item item=list2.get(position); return MyFragment.newInstance(item.getImageId(),item.getText()); } @Override public int getCount() { return list2.size(); } }); viewPager2.setOverScrollMode(View.OVER_SCROLL_NEVER); viewPager2.setCurrentItem(list.size()-1); }
因為我們用的是ZoomOut的效果,所以先設置顯示在最后一頁,點擊button時,設置位置減1,剛好可以實現往下切換的效果。
(其實是因為沒有找到ZoomIn的類,如果你找到了直接使用這個轉換效果類即可)
changeButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { viewPager.setCurrentItem((viewPager.getCurrentItem()-1)); viewPager2.setCurrentItem(viewPager2.getCurrentItem()-1); } });
最后,還要實現的是兩邊切換速度不一樣,因為viewPager.setCurrentItem方法設施的切換總是一閃而過,太短暫了,無法形成視覺停留的效果。
這時候需要用到的類
FixedSpeedScroller
public class FixedSpeedScroller extends Scroller { private int mDuration = 1500; public FixedSpeedScroller(Context context) { super(context); } public FixedSpeedScroller(Context context, Interpolator interpolator) { super(context, interpolator); } @Override public void startScroll(int startX, int startY, int dx, int dy, int duration) { // Ignore received duration, use fixed one instead super.startScroll(startX, startY, dx, dy, mDuration); } @Override public void startScroll(int startX, int startY, int dx, int dy) { // Ignore received duration, use fixed one instead super.startScroll(startX, startY, dx, dy, mDuration); } public void setmDuration(int time) { mDuration = time; } public int getmDuration() { return mDuration; } public void initViewPagerScroll(ViewPager viewPager) { try { Field mScroller = ViewPager.class.getDeclaredField("mScroller"); mScroller.setAccessible(true); mScroller.set(viewPager, this); } catch(Exception e) { e.printStackTrace(); } } }
這里我們看到是利用的反射的手法修改viewpager的"mScroller"字段。
MainActivity中初始化時調用
private void initSpeed() { FixedSpeedScroller scroller=new FixedSpeedScroller(this); scroller.setmDuration(2500); scroller.initViewPagerScroll(viewPager); FixedSpeedScroller scroller2=new FixedSpeedScroller(this); scroller2.setmDuration(1500); scroller2.initViewPagerScroll(viewPager2); }
這時候就已經完成了不同的切換速度。
效果:
至於第二種文字單獨切換的,只要將文字單獨用一個VerticalViewPager即可。
以上方法僅供參考,實際應用的話肯定還是要不斷地優化處理。