最近在即刻里看到即刻的"猜你喜欢"的板块,觉得效果很赞。
当点击"换一换"时,上面三个条目程序切换效果,并且三个条目的切换以不同的速度进行。
于是开始想办法撸出这样的切换效果。
我的思路是使用竖直切换的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即可。
以上方法仅供参考,实际应用的话肯定还是要不断地优化处理。