Android仿小红书启动页平行动画
其实之前在很多APP的引导页上都看到过这个效果,通过速度不同给人带来视觉差,感觉很炫酷。
通过网易云的课做了一个demo,记录一下。

首先来梳理一下实现的思路,其实就是viewpager+fragment,只不过这个fragment里的控件,会根据viewpager的滑动产生不同的速度的位移。从架构的角度来考虑的话,我们要做到可以随时添加fragment,同时可以随时添加fragment里的控件,控件的速度可以设置。所以要给系统控件来添加自定义属性。
attrs文件
//进入的时候透明度 //出去的时候透明度 //进入的时候x方向的速度 //出去的时候x方向的速度 //进入的时候Y方向的速度 出去的时候Y方向的速度 
android:id="@+id/iv_0"android:layout_width="103dp"android:layout_height="19dp"android:layout_centerInParent="true"android:src="@drawable/intro1_item_0"app:x_in="1.2"app:x_out="1.2" />
自定义FrameLayout
public void setUp(int... childIds) {//fragment集合fragments = new ArrayList(); for (int i = 0; i < childIds.length; i++) {ParallaxFragment fragment = new ParallaxFragment();//通过bundle传递参数给fragmentBundle bundle = new Bundle();bundle.putInt("layoutId", childIds[i]);fragment.setArguments(bundle);fragments.add(fragment);}ViewPager vp = new ViewPager(getContext());vp.setId(R.id.parallax_pager);//从value里的ids拿的SplashActivity activity = (SplashActivity) getContext();parallaxPagerAdapter = new ParallaxPagerAdapter(activity.getSupportFragmentManager(), fragments);vp.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));vp.setAdapter(parallaxPagerAdapter);vp.setOnPageChangeListener(this);addView(vp,0);}
自定义Fragment
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {//获取到布局文件idint rootViewId = getArguments().getInt("layoutId");//自定义LayoutInflater来处理里面的控件ParallaxLayoutInflater parallaxLayoutInflater = new ParallaxLayoutInflater(inflater,getActivity(),this);View rootView = parallaxLayoutInflater.inflate(rootViewId, null);return rootView;}
自定义LayoutInflater
class ParallaxFactory implements Factory2 {private LayoutInflater layoutInflater;//系统控件的前缀private String[] sClassPrefix = {"android.widget.","android.view."};//系统控件自定义的属性int[] attrIds = {R.attr.a_in,R.attr.a_out,R.attr.x_in,R.attr.x_out,R.attr.y_in,R.attr.y_out};public ParallaxFactory(LayoutInflater layoutInflater) {this.layoutInflater = layoutInflater;}/*** 反射机制** @param parent 顶级容器* @param name 控件名字(像RelativeLayout,ImageView)如果是自定义控件的话 返回的是全路径名字* @param context* @param attrs 控件的属性(width,height)* @return*/@Overridepublic View onCreateView(View parent, String name, Context context, AttributeSet attrs) {View view = null;view = createMyView(name, context, attrs);if (view != null) {//attrs 控件的所有属性,attrIds 是想要获取的控件的属性TypedArray array = context.obtainStyledAttributes(attrs, attrIds);if (array != null && array.length() > 0) {ParallaxViewTag viewTag = new ParallaxViewTag();viewTag.alphaIn = array.getFloat(0, 0f);viewTag.alphaOut = array.getFloat(1, 0f);viewTag.xIn = array.getFloat(2, 0f);viewTag.xOut = array.getFloat(3, 0f);viewTag.yIn = array.getFloat(4, 0f);viewTag.yOut = array.getFloat(5, 0f);view.setTag(R.id.parallax_view_tag, viewTag);}fragment.getParallaxViews().add(view);array.recycle();}return view;}/*** 创建view* @param name* @param context* @param attrs* @return*/private View createMyView(String name, Context context, AttributeSet attrs) {//如果是系统的控件,不会有. 如果是自定义控件的话 含有.if (name.contains(".")) {return reflectView(name, null, attrs);} else {//循环两个包for (String prefix : sClassPrefix) {View view = reflectView(name, prefix, attrs);if (view != null) {return view;}}}return null;}/*** @param name 控件名字* @param prefix 控件前缀* @param attrs 控件属性* @return*/private View reflectView(String name, String prefix, AttributeSet attrs) {try {//通过系统的layoutInflater创建视图,读取系统属性return layoutInflater.createView(name, prefix, attrs);} catch (ClassNotFoundException e) {e.printStackTrace();}return null;}@Overridepublic View onCreateView(String name, Context context, AttributeSet attrs) {return null;}}
@Overridepublic void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {//动画操作int containerWidth = getWidth();//获取到退出ParallaxFragment outFragment = null;try {outFragment = fragments.get(position - 1);} catch (Exception e) {e.printStackTrace();}//获取到进入的页面ParallaxFragment inFragment = null;try {inFragment = fragments.get(position);} catch (Exception e) {}if (outFragment != null) {//获取Fragment上所有的视图,实现动画效果ListinViews = outFragment.getParallaxViews(); // 动画if (inViews != null) {for (View view : inViews) {ParallaxViewTag tag = (ParallaxViewTag) view.getTag(R.id.parallax_view_tag);if (tag == null) {continue;}ViewHelper.setTranslationX(view, (containerWidth - positionOffsetPixels) * tag.xIn);ViewHelper.setTranslationY(view, (containerWidth - positionOffsetPixels) * tag.yIn);}}}if (inFragment != null) {ListoutViews = inFragment.getParallaxViews(); if (outViews != null) {for (View view : outViews) {ParallaxViewTag tag = (ParallaxViewTag) view.getTag(R.id.parallax_view_tag);if (tag == null) {continue;}//仔细观察退出的fragment中view从原始位置开始向上移动,translationY应为负数ViewHelper.setTranslationY(view, 0 - positionOffsetPixels * tag.yOut);ViewHelper.setTranslationX(view, 0 - positionOffsetPixels * tag.xOut);}}}}
评论
