有的时候会需要做一些自定义的动画效果,在会反复用到的动画效果可以考虑做成动画框架,方便使用,做成框架的话就需要考虑很多的问题,最典型的问题就是属性和方法必须要是可配置的,这里就来聊一聊自定义动画框架的做法

重难点分析

在自定义动画框架里面,最难的一个问题就是怎么样获得属性,如果直接写自定义的属性,那么编译时候就会报错了,那么自然就想到了在外层包裹自定义的属性,通过处理自定义的属性来得到,这样便是android源代码一些控件的解决办法,其实就是一招偷梁换柱,在处理的时候,如果是自定义的属性,就自己处理,如果不是自定义的属性,就交由系统处理,然后在addView上再做手脚,另外从xml的解析入手,可以自定义LayoutInflater,然后重写onCreateView(),在里面就可以完成偷梁换柱,故这里有两种方法可以完成

在addView时候拦截做法

这里做一个滑动的自定义动画

在滑动的View里面,需要实现滑动时候和重置的工作,这里有一个接口,及实现接口的方法来做

  1. public interface DiscrollvableInterface {
  2. //当滑动的时候调用该方法,用来控制里面的控件执行相应的动画
  3. void onDiscrollve(float ratio);
  4. //重置view的属性----恢复view的原来属性
  5. void onResetDiscrollve();
  6. }

实现上面的接口

  1. public class DiscrollvableView extends FrameLayout implements DiscrollvableInterface {
  2. private static final int TRANSLATION_FROM_TOP = 0x01;
  3. private static final int TRANSLATION_FROM_BOTTOM = 0x02;
  4. private static final int TRANSLATION_FROM_LEFT = 0x04;
  5. private static final int TRANSLATION_FROM_RIGHT = 0x08;
  6. //颜色估值器
  7. private static ArgbEvaluator sArgbEvaluator = new ArgbEvaluator();
  8. //自定义属性的一些接收的变量
  9. private int mDiscrollveFromBgColor;//背景颜色变化开始值
  10. private int mDiscrollveToBgColor;//背景颜色变化结束值
  11. private boolean mDiscrollveAlpha;//是否需要透明度动画
  12. private int mDisCrollveTranslation;//平移值
  13. private boolean mDiscrollveScaleX;//是否需要x轴方向缩放
  14. private boolean mDiscrollveScaleY;//是否需要y轴方向缩放
  15. private int mHeight;//本view的高度
  16. private int mWidth;//宽度
  17. public void setmDiscrollveFromBgColor(int mDiscrollveFromBgColor) {
  18. this.mDiscrollveFromBgColor = mDiscrollveFromBgColor;
  19. }
  20. public void setmDiscrollveToBgColor(int mDiscrollveToBgColor) {
  21. this.mDiscrollveToBgColor = mDiscrollveToBgColor;
  22. }
  23. public void setmDiscrollveAlpha(boolean mDiscrollveAlpha) {
  24. this.mDiscrollveAlpha = mDiscrollveAlpha;
  25. }
  26. public void setmDisCrollveTranslation(int mDisCrollveTranslation) {
  27. this.mDisCrollveTranslation = mDisCrollveTranslation;
  28. }
  29. public void setmDiscrollveScaleX(boolean mDiscrollveScaleX) {
  30. this.mDiscrollveScaleX = mDiscrollveScaleX;
  31. }
  32. public void setmDiscrollveScaleY(boolean mDiscrollveScaleY) {
  33. this.mDiscrollveScaleY = mDiscrollveScaleY;
  34. }
  35. @Override
  36. protected void onSizeChanged(int w, int h, int oldw, int oldh) {
  37. super.onSizeChanged(w, h, oldw, oldh);
  38. mWidth = w;
  39. mHeight = h;
  40. onResetDiscrollve();
  41. }
  42. public DiscrollvableView(Context context, AttributeSet attrs) {
  43. super(context, attrs);
  44. }
  45. public DiscrollvableView(Context context) {
  46. super(context);
  47. }
  48. @Override
  49. public void onDiscrollve(float ratio) {
  50. // ratio:0~1
  51. //控制自身的动画属性
  52. if (mDiscrollveAlpha) {
  53. setAlpha(ratio);
  54. }
  55. if (mDiscrollveScaleX) {
  56. setScaleX(ratio);
  57. }
  58. if (mDiscrollveScaleY) {
  59. setScaleY(ratio);
  60. }
  61. //判断到底是哪一种值:fromTop,fromBottom,fromLeft,fromRight
  62. if (isDiscrollTranslationFrom(TRANSLATION_FROM_BOTTOM)) {
  63. setTranslationY(mHeight * (1 - ratio));//mHeight-->0(代表原来的位置)
  64. }
  65. if (isDiscrollTranslationFrom(TRANSLATION_FROM_TOP)) {
  66. setTranslationY(-mHeight * (1 - ratio));//-mHeight-->0(代表原来的位置)
  67. }
  68. if (isDiscrollTranslationFrom(TRANSLATION_FROM_LEFT)) {
  69. setTranslationX(-mWidth * (1 - ratio));//-width-->0(代表原来的位置)
  70. }
  71. if (isDiscrollTranslationFrom(TRANSLATION_FROM_RIGHT)) {
  72. setTranslationX(mWidth * (1 - ratio));//width-->0(代表原来的位置)
  73. }
  74. //颜色渐变动画
  75. if (mDiscrollveFromBgColor != -1 && mDiscrollveToBgColor != -1) {
  76. //ratio=0.5 color=中间颜色
  77. setBackgroundColor((Integer) sArgbEvaluator.evaluate(ratio, mDiscrollveFromBgColor, mDiscrollveToBgColor));
  78. }
  79. }
  80. private boolean isDiscrollTranslationFrom(int translationMask) {
  81. if (mDisCrollveTranslation == -1) {
  82. return false;
  83. }
  84. //fromLeft|fromBottom & fromBottom = fromBottom
  85. return (mDisCrollveTranslation & translationMask) == translationMask;
  86. }
  87. @Override
  88. public void onResetDiscrollve() {
  89. //控制自身的动画属性
  90. if (mDiscrollveAlpha) {
  91. setAlpha(0);
  92. }
  93. if (mDiscrollveScaleX) {
  94. setScaleX(0);
  95. }
  96. if (mDiscrollveScaleY) {
  97. setScaleY(0);
  98. }
  99. //判断到底是哪一种值:fromTop,fromBottom,fromLeft,fromRight
  100. if (isDiscrollTranslationFrom(TRANSLATION_FROM_BOTTOM)) {
  101. setTranslationY(mHeight);//mHeight-->0(代表原来的位置)
  102. }
  103. if (isDiscrollTranslationFrom(TRANSLATION_FROM_TOP)) {
  104. setTranslationY(-mHeight);//-mHeight-->0(代表原来的位置)
  105. }
  106. if (isDiscrollTranslationFrom(TRANSLATION_FROM_LEFT)) {
  107. setTranslationX(-mWidth);//-width-->0(代表原来的位置)
  108. }
  109. if (isDiscrollTranslationFrom(TRANSLATION_FROM_RIGHT)) {
  110. setTranslationX(mWidth);//width-->0(代表原来的位置)
  111. }
  112. }
  113. }

那么便完成了对于滑动效果的控制,为了便于控制,自定义View继承ScrollView

  1. public class DiscrollView extends ScrollView {
  2. private DiscrollViewContent mContent;
  3. public DiscrollView(Context context, AttributeSet attrs) {
  4. super(context, attrs);
  5. }
  6. @Override
  7. protected void onFinishInflate() {
  8. super.onFinishInflate();
  9. View content = getChildAt(0);
  10. mContent = (DiscrollViewContent) content;
  11. }
  12. @Override
  13. protected void onSizeChanged(int w, int h, int oldw, int oldh) {
  14. super.onSizeChanged(w, h, oldw, oldh);
  15. View first = mContent.getChildAt(0);
  16. first.getLayoutParams().height = getHeight();
  17. }
  18. @Override
  19. protected void onScrollChanged(int l, int t, int oldl, int oldt) {
  20. super.onScrollChanged(l, t, oldl, oldt);
  21. int scrollViewHeight = getHeight();
  22. //监听滑动----接口---->控制DiscrollViewContent的属性
  23. //遍历MyLinearLayout里面所有子控件(MyViewGroup)
  24. for (int i = 0; i < mContent.getChildCount(); i++) {
  25. View child = mContent.getChildAt(i);
  26. if (!(child instanceof DiscrollvableInterface)) {
  27. continue;
  28. }
  29. //ratio:0~1
  30. DiscrollvableInterface discrollvableInterface = (DiscrollvableInterface) child;
  31. //1.child离scrollview顶部的高度
  32. int discrollvableTop = child.getTop();
  33. int discrollvableHeight = child.getHeight();
  34. //2.得到scrollview滑出去的高度
  35. //3.得到child离屏幕顶部的高度
  36. int discrollvableAbsoluteTop = discrollvableTop - t;
  37. //什么时候执行动画?当child滑进屏幕的时候
  38. if (discrollvableAbsoluteTop <= scrollViewHeight) {
  39. int visibleGap = scrollViewHeight - discrollvableAbsoluteTop;
  40. //确保ratio是在0~1,超过了1 也设置为1
  41. discrollvableInterface.onDiscrollve(clamp(visibleGap / (float) discrollvableHeight, 1f, 0f));
  42. } else {//否则,就恢复到原来的位置
  43. discrollvableInterface.onResetDiscrollve();
  44. }
  45. }
  46. }
  47. public static float clamp(float value, float max, float min) {
  48. return Math.max(Math.min(value, max), min);
  49. }
  50. }

设置属性,偷梁换柱

  1. public class DiscrollViewContent extends LinearLayout {
  2. public DiscrollViewContent(Context context, AttributeSet attrs) {
  3. super(context, attrs);
  4. setOrientation(VERTICAL);
  5. }
  6. @Override
  7. public LayoutParams generateLayoutParams(AttributeSet attrs) {
  8. // 得到xml里面穿过来的参数
  9. return new MyLayoutParams(getContext(), attrs);
  10. }
  11. @Override
  12. public void addView(View child, int index, android.view.ViewGroup.LayoutParams params) {
  13. //从child里面拿到我自定义的属性,传到discrollvableView里面
  14. MyLayoutParams p = (MyLayoutParams) params;
  15. if (!isDiscrollvable(p)) {//判断该view是否穿了自定义属性值,不是就不需要执行动画,不包一层FrameLayout
  16. super.addView(child, index, params);
  17. } else {
  18. //在addView里面插一脚,往child外面包裹一层FrameLayout
  19. DiscrollvableView discrollvableView = new DiscrollvableView(getContext());
  20. discrollvableView.setmDiscrollveAlpha(p.mDiscrollveAlpha);
  21. discrollvableView.setmDisCrollveTranslation(p.mDisCrollveTranslation);
  22. discrollvableView.setmDiscrollveScaleX(p.mDiscrollveScaleX);
  23. discrollvableView.setmDiscrollveScaleY(p.mDiscrollveScaleY);
  24. discrollvableView.setmDiscrollveFromBgColor(p.mDiscrollveFromBgColor);
  25. discrollvableView.setmDiscrollveToBgColor(p.mDiscrollveToBgColor);
  26. discrollvableView.addView(child);
  27. super.addView(discrollvableView, index, params);
  28. }
  29. }
  30. private boolean isDiscrollvable(MyLayoutParams p) {
  31. return p.mDiscrollveAlpha || p.mDiscrollveScaleX
  32. || p.mDiscrollveScaleY || p.mDisCrollveTranslation != -1
  33. || (p.mDiscrollveFromBgColor != -1 && p.mDiscrollveToBgColor != -1);
  34. }
  35. public static class MyLayoutParams extends LinearLayout.LayoutParams {
  36. public int mDiscrollveFromBgColor;//背景颜色变化开始值
  37. public int mDiscrollveToBgColor;//背景颜色变化结束值
  38. public boolean mDiscrollveAlpha;//是否需要透明度动画
  39. public int mDisCrollveTranslation;//平移值
  40. public boolean mDiscrollveScaleX;//是否需要x轴方向缩放
  41. public boolean mDiscrollveScaleY;//是否需要y轴方向缩放
  42. public MyLayoutParams(Context context, AttributeSet attrs) {
  43. super(context, attrs);
  44. // 从child里面拿到我自定义的属性
  45. TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DiscrollView_LayoutParams);
  46. mDiscrollveAlpha = a.getBoolean(R.styleable.DiscrollView_LayoutParams_discrollve_alpha, false);
  47. mDiscrollveScaleX = a.getBoolean(R.styleable.DiscrollView_LayoutParams_discrollve_scaleX, false);
  48. mDiscrollveScaleY = a.getBoolean(R.styleable.DiscrollView_LayoutParams_discrollve_scaleY, false);
  49. mDisCrollveTranslation = a.getInt(R.styleable.DiscrollView_LayoutParams_discrollve_translation, -1);
  50. mDiscrollveFromBgColor = a.getColor(R.styleable.DiscrollView_LayoutParams_discrollve_fromBgColor, -1);
  51. mDiscrollveToBgColor = a.getColor(R.styleable.DiscrollView_LayoutParams_discrollve_toBgColor, -1);
  52. a.recycle();
  53. }
  54. }
  55. }

最后是一些自定义属性

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <resources>
  3. <declare-styleable name="DiscrollView_LayoutParams">
  4. <attr name="discrollve_alpha" format="boolean"/>
  5. <attr name="discrollve_scaleX" format="boolean"/>
  6. <attr name="discrollve_scaleY" format="boolean"/>
  7. <attr name="discrollve_fromBgColor" format="color"/>
  8. <attr name="discrollve_toBgColor" format="color"/>
  9. <attr name="discrollve_translation"/>
  10. </declare-styleable>
  11. <attr name="discrollve_translation">
  12. <flag name="fromTop" value="0x01" />
  13. <flag name="fromBottom" value="0x02" />
  14. <flag name="fromLeft" value="0x04" />
  15. <flag name="fromRight" value="0x08" />
  16. </attr>
  17. </resources>

布局

  1. <com.cj5785.customanimationaddview.DiscrollView xmlns:android="http://schemas.android.com/apk/res/android"
  2. xmlns:custom="http://schemas.android.com/apk/res-auto"
  3. android:layout_width="match_parent"
  4. android:layout_height="match_parent">
  5. <com.cj5785.customanimationaddview.DiscrollViewContent
  6. android:layout_width="match_parent"
  7. android:layout_height="match_parent">
  8. <TextView
  9. android:layout_width="match_parent"
  10. android:layout_height="560dp"
  11. android:background="@android:color/white"
  12. android:gravity="center"
  13. android:text="测试文字使用情况"
  14. android:textSize="25sp" />
  15. <View
  16. android:layout_width="match_parent"
  17. android:layout_height="200dp"
  18. android:background="@android:color/darker_gray"
  19. custom:discrollve_alpha="true" />
  20. <ImageView
  21. android:layout_width="89dp"
  22. android:layout_height="80dp"
  23. android:src="@drawable/duck"
  24. custom:discrollve_alpha="true"
  25. custom:discrollve_translation="fromLeft|fromBottom" />
  26. <View
  27. android:layout_width="match_parent"
  28. android:layout_height="200dp"
  29. custom:discrollve_fromBgColor="#ffff00"
  30. custom:discrollve_toBgColor="#88EE66" />
  31. <ImageView
  32. android:layout_width="150dp"
  33. android:layout_height="106dp"
  34. android:layout_gravity="right"
  35. android:src="@drawable/camera"
  36. custom:discrollve_translation="fromRight" />
  37. <TextView
  38. android:layout_width="match_parent"
  39. android:layout_height="wrap_content"
  40. android:gravity="center"
  41. android:text="第二段文字使用测试"
  42. android:textSize="28sp"
  43. custom:discrollve_alpha="true"
  44. custom:discrollve_translation="fromBottom" />
  45. <ImageView
  46. android:layout_width="wrap_content"
  47. android:layout_height="wrap_content"
  48. android:layout_gravity="center"
  49. android:layout_margin="20dp"
  50. android:src="@drawable/sun"
  51. custom:discrollve_scaleX="true"
  52. custom:discrollve_scaleY="true" />
  53. <ImageView
  54. android:layout_width="wrap_content"
  55. android:layout_height="wrap_content"
  56. android:layout_gravity="center"
  57. android:layout_margin="20dp"
  58. android:src="@drawable/balloon"
  59. custom:discrollve_translation="fromLeft|fromBottom" />
  60. </com.cj5785.customanimationaddview.DiscrollViewContent>
  61. </com.cj5785.customanimationaddview.DiscrollView>

效果如下

自定义LayoutInflater

这里实现一个仿小红书的视差动画效果

首先定义一个根布局,然后实例化其自定义控件

  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2. xmlns:tools="http://schemas.android.com/tools"
  3. android:layout_width="match_parent"
  4. android:layout_height="match_parent"
  5. android:background="@android:color/white">
  6. <com.cj5785.customanimationlayoutinflater.ParallaxContainer
  7. android:id="@+id/parallax_container"
  8. android:layout_width="match_parent"
  9. android:layout_height="match_parent"/>
  10. <ImageView
  11. android:id="@+id/iv_man"
  12. android:layout_width="66dp"
  13. android:layout_height="202dp"
  14. android:layout_alignParentBottom="true"
  15. android:layout_centerHorizontal="true"
  16. android:layout_marginBottom="10dp"
  17. android:background="@drawable/intro_item_manrun_1"/>
  18. </RelativeLayout>

实例化自定义控件,在最后一个登陆界面,由于与前面的不同,使用切换Fragment的方式,需要使用到适配器

适配器

  1. public class ParallaxAdapter extends FragmentPagerAdapter {
  2. private List<ParallaxFragment> fragmentList;
  3. public ParallaxAdapter(FragmentManager fm, List<ParallaxFragment> fragmentList) {
  4. super(fm);
  5. this.fragmentList = fragmentList;
  6. }
  7. @Override
  8. public Fragment getItem(int position) {
  9. return fragmentList.get(position);
  10. }
  11. @Override
  12. public int getCount() {
  13. return fragmentList.size();
  14. }
  15. }

自定义Fragment,其主要的偷梁换柱工作就是在这里完成的,返回我们处理以后的View

  1. public class ParallaxFragment extends Fragment {
  2. private List<View> parallaxViews = new ArrayList<>();
  3. @Nullable
  4. @Override
  5. public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
  6. Bundle bundle = getArguments();
  7. int layoutId = bundle.getInt("layoutId");
  8. //正常情况下在这里获得view,然后返回,我们就在这里偷梁换柱
  9. // View view = inflater.inflate(layoutId, container);
  10. //使用自定义的渲染器渲染
  11. ParallaxLayoutInflater layoutInflater = new ParallaxLayoutInflater(inflater, getActivity(), this);
  12. return layoutInflater.inflate(layoutId, null);
  13. }
  14. public List<View> getParallaxViews() {
  15. return parallaxViews;
  16. }
  17. }

动画引导层

  1. //引导页的最外层布局
  2. public class ParallaxContainer extends FrameLayout implements OnPageChangeListener {
  3. private List<ParallaxFragment> fragmentList;
  4. private ParallaxAdapter adapter;
  5. private float containerWidth;
  6. private ImageView iv_man;
  7. public ParallaxContainer(@NonNull Context context, @Nullable AttributeSet attrs) {
  8. super(context, attrs);
  9. }
  10. //指定引导页的所有页面布局文件
  11. public void setUp(int... ids) {
  12. //初始化framentList,在调用处直接调用setUp()
  13. fragmentList = new ArrayList<>();
  14. for (int i = 0; i < ids.length; i++) {
  15. ParallaxFragment fragment = new ParallaxFragment();
  16. Bundle bundle = new Bundle();
  17. //Fragment中需要加载的布局文件id
  18. bundle.putInt("layoutId", ids[i]);
  19. fragment.setArguments(bundle);
  20. fragmentList.add(fragment);
  21. }
  22. //设置ViewPager
  23. ViewPager viewPager = new ViewPager(getContext());
  24. viewPager.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
  25. viewPager.setId(R.id.parallax_pager);
  26. adapter = new ParallaxAdapter(((MainActivity) getContext()).getSupportFragmentManager(), fragmentList);
  27. viewPager.setAdapter(adapter);
  28. addView(viewPager, 0);
  29. //设置监听
  30. viewPager.addOnPageChangeListener(this);
  31. }
  32. @Override
  33. public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
  34. containerWidth = getWidth();
  35. //在翻页的过程中,不断根据视图的标签中对应的动画参数,改变视图的位置或者透明度
  36. //获取到进入的页面
  37. ParallaxFragment inFragment = null;
  38. try {
  39. inFragment = fragmentList.get(position - 1);
  40. } catch (Exception e) {
  41. }
  42. //获取到退出的页面
  43. ParallaxFragment outFragment = null;
  44. try {
  45. outFragment = fragmentList.get(position);
  46. } catch (Exception e) {
  47. }
  48. if (inFragment != null) {
  49. //获取Fragment上所有的视图,实现动画效果
  50. List<View> inViews = inFragment.getParallaxViews();
  51. if (inViews != null) {
  52. for (View view : inViews) {
  53. //获取标签,从标签上获取所有的动画参数
  54. ParallaxViewTag tag = (ParallaxViewTag) view.getTag(R.id.parallax_view_tag);
  55. if (tag == null) {
  56. continue;
  57. }
  58. //translationY改变view的偏移位置,translationY=100,代表view在其原始位置向下移动100
  59. //仔细观察进入的fragment中view从远处过来,不断向下移动,最终停在原始位置
  60. view.setTranslationX((containerWidth - positionOffsetPixels) * tag.xIn);
  61. view.setTranslationY((containerWidth - positionOffsetPixels) * tag.yIn);
  62. }
  63. }
  64. }
  65. if (outFragment != null) {
  66. List<View> outViews = outFragment.getParallaxViews();
  67. if (outViews != null) {
  68. for (View view : outViews) {
  69. ParallaxViewTag tag = (ParallaxViewTag) view.getTag(R.id.parallax_view_tag);
  70. if (tag == null) {
  71. continue;
  72. }
  73. //仔细观察退出的fragment中view从原始位置开始向上移动,translationY应为负数
  74. view.setTranslationX((-positionOffsetPixels) * tag.xOut);
  75. view.setTranslationY((-positionOffsetPixels) * tag.yOut);
  76. }
  77. }
  78. }
  79. }
  80. @Override
  81. public void onPageSelected(int position) {
  82. if (position == adapter.getCount() - 1) {
  83. iv_man.setVisibility(INVISIBLE);
  84. } else {
  85. iv_man.setVisibility(VISIBLE);
  86. }
  87. }
  88. @Override
  89. public void onPageScrollStateChanged(int state) {
  90. AnimationDrawable animation = (AnimationDrawable) iv_man.getBackground();
  91. switch (state) {
  92. case ViewPager.SCROLL_STATE_DRAGGING:
  93. animation.start();
  94. break;
  95. case ViewPager.SCROLL_STATE_IDLE:
  96. animation.stop();
  97. break;
  98. default:
  99. break;
  100. }
  101. }
  102. public void setIv_man(ImageView iv_man) {
  103. this.iv_man = iv_man;
  104. }
  105. }

拦截系统处理xml

  1. public class ParallaxLayoutInflater extends LayoutInflater {
  2. private static final String TAG = "ParallaxLayoutInflater";
  3. private ParallaxFragment parallaxFragment;
  4. protected ParallaxLayoutInflater(LayoutInflater original, Context newContext, ParallaxFragment parallaxFragment) {
  5. super(original, newContext);
  6. this.parallaxFragment = parallaxFragment;
  7. //源代码中存在:
  8. //View view;
  9. //if (mFactory2 != null) {
  10. // view = mFactory2.onCreateView(parent, name, context, attrs);
  11. //} else if (mFactory != null) {
  12. // view = mFactory.onCreateView(name, context, attrs);
  13. //} else {
  14. // view = null;
  15. //}
  16. //if (view == null && mPrivateFactory != null) {
  17. // view = mPrivateFactory.onCreateView(parent, name, context, attrs);
  18. //}
  19. //也就是说只要实现Factory,那么就不会调用后面的方法
  20. setFactory(new ParallaxFactory(this));
  21. }
  22. @Override
  23. public LayoutInflater cloneInContext(Context newContext) {
  24. //实际用于创建LayoutInflater的方法
  25. return new ParallaxLayoutInflater(this, newContext, parallaxFragment);
  26. }
  27. //这个框架最核心的地方就是在这里设置Factory,从而进行拦截
  28. class ParallaxFactory implements LayoutInflater.Factory{
  29. private LayoutInflater inflater;
  30. //系统提供的视图分为android.widget.xxx和android.view.xxx
  31. private final String[] prefixs = {
  32. "android.widget.",
  33. "android.view."
  34. };
  35. public ParallaxFactory(LayoutInflater inflater) {
  36. this.inflater = inflater;
  37. }
  38. @Override
  39. public View onCreateView(String name, Context context, AttributeSet attrs) {
  40. //实例化view
  41. View view = null;
  42. if (view == null) {
  43. view = createViewOrFailQuietly(name,context,attrs);
  44. }
  45. if (view != null) {
  46. //获取自定义属性,并将自定义标签值绑定到view上面
  47. getCustomAttrs(context, attrs, view);
  48. parallaxFragment.getParallaxViews().add(view);
  49. }
  50. return view;
  51. }
  52. private void getCustomAttrs(Context context, AttributeSet attrs, View view) {
  53. //所有自定义的属性
  54. int[] attrIds = {
  55. R.attr.a_in,
  56. R.attr.a_out,
  57. R.attr.x_in,
  58. R.attr.x_out,
  59. R.attr.y_in,
  60. R.attr.y_out
  61. };
  62. //获取
  63. TypedArray typedArray = context.obtainStyledAttributes(attrs, attrIds);
  64. if (typedArray != null && typedArray.length() > 0) {
  65. //获取自定义属性的值
  66. Log.d(TAG, "getCustomAttrs");
  67. ParallaxViewTag tag = new ParallaxViewTag();
  68. tag.alphaIn = typedArray.getFloat(0, 0f);
  69. tag.alphaOut = typedArray.getFloat(1, 0f);
  70. tag.xIn = typedArray.getFloat(2, 0f);
  71. tag.xOut = typedArray.getFloat(3, 0f);
  72. tag.yIn = typedArray.getFloat(4, 0f);
  73. tag.yOut = typedArray.getFloat(5, 0f);
  74. view.setTag(R.id.parallax_view_tag, tag);
  75. }
  76. typedArray.recycle();
  77. }
  78. private View createViewOrFailQuietly(String name, String prefix, Context context, AttributeSet attrs) {
  79. try {
  80. //通过系统的inflater创建视图,读取系统的属性
  81. return inflater.createView(name, prefix, attrs);
  82. } catch (ClassNotFoundException e) {
  83. e.printStackTrace();
  84. return null;
  85. }
  86. }
  87. private View createViewOrFailQuietly(String name, Context context, AttributeSet attrs) {
  88. //通过系统inflater创建视图
  89. //1.自定义控件标签名称带点,所以创建时不需要前缀
  90. if (name.contains(".")) {
  91. return createViewOrFailQuietly(name, null, context, attrs);
  92. }
  93. //2.系统视图需要加上前缀
  94. for (String prefix : prefixs) {
  95. View view = createViewOrFailQuietly(name, prefix, context, attrs);
  96. if (view != null) {
  97. return view;
  98. }
  99. }
  100. return null;
  101. }
  102. }
  103. }

参数控制

  1. //视差动画播放时参数的控制
  2. public class ParallaxViewTag {
  3. protected int index;
  4. protected float xIn;
  5. protected float xOut;
  6. protected float yIn;
  7. protected float yOut;
  8. protected float alphaIn;
  9. protected float alphaOut;
  10. @Override
  11. public String toString() {
  12. return "ParallaxViewTag [index=" + index + ", xIn=" + xIn + ", xOut=" + xOut + ", yIn=" + yIn
  13. + ", yOut=" + yOut + ", alphaIn=" + alphaIn + ", alphaOut=" + alphaOut + "]";
  14. }
  15. }

同时完成人行走的帧动画

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <animation-list xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:oneshot="false">
  4. <item
  5. android:drawable="@drawable/intro_item_manrun_1"
  6. android:duration="200" />
  7. <item
  8. android:drawable="@drawable/intro_item_manrun_2"
  9. android:duration="200" />
  10. </animation-list>

自定义属性

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <resources>
  3. <attr name="a_in" format="float" />
  4. <attr name="a_out" format="float" />
  5. <attr name="x_in" format="float" />
  6. <attr name="x_out" format="float" />
  7. <attr name="y_in" format="float" />
  8. <attr name="y_out" format="float" />
  9. </resources>

ids

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <resources>
  3. <item name="parallax_pager" type="id"/>
  4. <item name="parallax_view_tag" type="id"/>
  5. </resources>

实现效果如下图所示

高级UI-自定义动画框架的更多相关文章

  1. 深入学习jQuery自定义动画

    × 目录 [1]属性对象 [2]可选参数 [3]选项参数 前面的话 很多情况下,前面介绍的jQuery动画的简单效果无法满足用户的各种需求,那么就需要对动画有更多的限制,需要采取一些高级的自定义动画来 ...

  2. 高级UI晋升之自定义View实战(六)

    更多Android高级架构进阶视频学习请点击:https://space.bilibili.com/474380680本篇文章将从Android 自定义属性动画&Camera动画来介绍自定义V ...

  3. 高级UI晋升之自定义View实战(五)

    更多Android高级架构进阶视频学习请点击:https://space.bilibili.com/474380680本篇文章将从自定义View利器Canvas和Paint来进行详解 一.Canvas ...

  4. Android开发UI之自定义动画

    自定义动画,需要新建一个类,继承Animation类. 重写applyTransformation()方法和initialize()方法. applyTransformation(float inte ...

  5. UI设计篇·入门篇·简单动画的实现,透明动画/旋转动画/移动动画/缩放动画,混合动画效果的实现,为动画设置监听事件,自定义动画的方法

    基本的动画构成共有四种:透明动画/旋转动画/移动动画/缩放动画. 配置动画的方式有两种,一种是直接使用代码来配置动画效果,另一种是使用xml文档配置动画效果 相比而言,用xml文档写出来的动画效果,写 ...

  6. 高级UI晋升之常用View(三)中篇

    更多Android高级架构进阶视频学习请点击:https://space.bilibili.com/474380680本篇文章将从ViewPager来介绍常用View:文章目录 一.简介 二.基本使用 ...

  7. firefox 扩展开发笔记(三):高级ui交互编程

    firefox 扩展开发笔记(三):高级ui交互编程 前言 前两篇链接 1:firefox 扩展开发笔记(一):jpm 使用实践以及调试 2:firefox 扩展开发笔记(二):进阶开发之移动设备模拟 ...

  8. Android 高级UI设计笔记07:RecyclerView 的详解

    1. 使用RecyclerView       在 Android 应用程序中列表是一个非常重要的控件,适用场合非常多,如新闻列表.应用列表.消息列表等等,但是从Android 一出生到现在并没有非常 ...

  9. iOS 8自定义动画转场上手指南

    原文:http://www.cocoachina.com/ios/20150126/11011.html iOS 5发布的时候,苹果针对应用程序界面的设计,提出了一种全新的,革命性的方法—Storyb ...

随机推荐

  1. 【HTML】解析原理

    标准的web前端工程师需要知道 ◎浏览器(或者相应播放器)的渲染/重绘原理 这我得加把劲了.我还真的说的不是很清楚,我就G下,结果不是很多,找到了有一个,就记下来了... 以下部分来自handawei ...

  2. cube.js 学习(一)简单项目创建

    cube.js 是一个很不错的模块化分析框架,基于schema生成sql 同时内置可代码生成,可以快速的搞定 web 分析应用的开发 安装cli 工具 npm install -g cubejs-cl ...

  3. Codevs 3160 最长公共子串(后缀数组)

    3160 最长公共子串 时间限制: 2 s 空间限制: 128000 KB 题目等级 : 大师 Master 题目描述 Description 给出两个由小写字母组成的字符串,求它们的最长公共子串的长 ...

  4. 洛谷 P1842 奶牛玩杂技 题解

    P1842 奶牛玩杂技 题目背景 Farmer John 养了N(1<=N<=50,000)头牛,她们已经按1~N依次编上了号.FJ所不知道的是,他的所有牛都梦想着从农场逃走,去参加马戏团 ...

  5. 转载:Spark GraphX详解

    1.GraphX介绍 1.1 GraphX应用背景 Spark GraphX是一个分布式图处理框架,它是基于Spark平台提供对图计算和图挖掘简洁易用的而丰富的接口,极大的方便了对分布式图处理的需求. ...

  6. Loadrunner录制+运行+结果-【飞机订票系统实战】

    目录结构: 一.LoadRunner实现订票系统脚本录制 二.Loadrunner实现订票系统IP欺骗(此处可以不设置) 三.Loadrunner运行录制的脚本 四.Load generator配置 ...

  7. 13.mysql数据库

    1.mysql数据库建立           yum install mysql-server           mysql -u root                  mysqladmin ...

  8. input type color

    <input type="color"> https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input ...

  9. 关于Delphi中二维数组的声明和大小调整

    这是一个实例: procedure TMainForm.Button1Click(Sender: TObject);var  arr:array of array of string;begin  s ...

  10. 后端小白的Bootstrap笔记 一

    栅格系统 下面这张图是Bootstrap对栅格系统有关系数的规定 什么是栅格体统? 栅格系统是Bootstrap提供的移动优先的网格系统, 各个分界点如上: 576px 720px 992px 120 ...