高级UI-自定义动画框架

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

重难点分析

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

在addView时候拦截做法

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

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

实现上面的接口

public class DiscrollvableView extends FrameLayout implements DiscrollvableInterface {

    private static final int TRANSLATION_FROM_TOP = 0x01;
    private static final int TRANSLATION_FROM_BOTTOM = 0x02;
    private static final int TRANSLATION_FROM_LEFT = 0x04;
    private static final int TRANSLATION_FROM_RIGHT = 0x08;

    //颜色估值器
    private static ArgbEvaluator sArgbEvaluator = new ArgbEvaluator();
    //自定义属性的一些接收的变量
    private int mDiscrollveFromBgColor;//背景颜色变化开始值
    private int mDiscrollveToBgColor;//背景颜色变化结束值
    private boolean mDiscrollveAlpha;//是否需要透明度动画
    private int mDisCrollveTranslation;//平移值
    private boolean mDiscrollveScaleX;//是否需要x轴方向缩放
    private boolean mDiscrollveScaleY;//是否需要y轴方向缩放
    private int mHeight;//本view的高度
    private int mWidth;//宽度

    public void setmDiscrollveFromBgColor(int mDiscrollveFromBgColor) {
        this.mDiscrollveFromBgColor = mDiscrollveFromBgColor;
    }

    public void setmDiscrollveToBgColor(int mDiscrollveToBgColor) {
        this.mDiscrollveToBgColor = mDiscrollveToBgColor;
    }

    public void setmDiscrollveAlpha(boolean mDiscrollveAlpha) {
        this.mDiscrollveAlpha = mDiscrollveAlpha;
    }

    public void setmDisCrollveTranslation(int mDisCrollveTranslation) {
        this.mDisCrollveTranslation = mDisCrollveTranslation;
    }

    public void setmDiscrollveScaleX(boolean mDiscrollveScaleX) {
        this.mDiscrollveScaleX = mDiscrollveScaleX;
    }

    public void setmDiscrollveScaleY(boolean mDiscrollveScaleY) {
        this.mDiscrollveScaleY = mDiscrollveScaleY;
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mWidth = w;
        mHeight = h;
        onResetDiscrollve();
    }


    public DiscrollvableView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public DiscrollvableView(Context context) {
        super(context);
    }

    @Override
    public void onDiscrollve(float ratio) {
        // ratio:0~1
        //控制自身的动画属性
        if (mDiscrollveAlpha) {
            setAlpha(ratio);
        }
        if (mDiscrollveScaleX) {
            setScaleX(ratio);
        }
        if (mDiscrollveScaleY) {
            setScaleY(ratio);
        }
        //判断到底是哪一种值:fromTop,fromBottom,fromLeft,fromRight
        if (isDiscrollTranslationFrom(TRANSLATION_FROM_BOTTOM)) {
            setTranslationY(mHeight * (1 - ratio));//mHeight-->0(代表原来的位置)
        }
        if (isDiscrollTranslationFrom(TRANSLATION_FROM_TOP)) {
            setTranslationY(-mHeight * (1 - ratio));//-mHeight-->0(代表原来的位置)
        }
        if (isDiscrollTranslationFrom(TRANSLATION_FROM_LEFT)) {
            setTranslationX(-mWidth * (1 - ratio));//-width-->0(代表原来的位置)
        }
        if (isDiscrollTranslationFrom(TRANSLATION_FROM_RIGHT)) {
            setTranslationX(mWidth * (1 - ratio));//width-->0(代表原来的位置)
        }
        //颜色渐变动画
        if (mDiscrollveFromBgColor != -1 && mDiscrollveToBgColor != -1) {
            //ratio=0.5 color=中间颜色
            setBackgroundColor((Integer) sArgbEvaluator.evaluate(ratio, mDiscrollveFromBgColor, mDiscrollveToBgColor));
        }

    }

    private boolean isDiscrollTranslationFrom(int translationMask) {
        if (mDisCrollveTranslation == -1) {
            return false;
        }
        //fromLeft|fromBottom & fromBottom = fromBottom
        return (mDisCrollveTranslation & translationMask) == translationMask;
    }

    @Override
    public void onResetDiscrollve() {
        //控制自身的动画属性
        if (mDiscrollveAlpha) {
            setAlpha(0);
        }
        if (mDiscrollveScaleX) {
            setScaleX(0);
        }
        if (mDiscrollveScaleY) {
            setScaleY(0);
        }
        //判断到底是哪一种值:fromTop,fromBottom,fromLeft,fromRight
        if (isDiscrollTranslationFrom(TRANSLATION_FROM_BOTTOM)) {
            setTranslationY(mHeight);//mHeight-->0(代表原来的位置)
        }
        if (isDiscrollTranslationFrom(TRANSLATION_FROM_TOP)) {
            setTranslationY(-mHeight);//-mHeight-->0(代表原来的位置)
        }
        if (isDiscrollTranslationFrom(TRANSLATION_FROM_LEFT)) {
            setTranslationX(-mWidth);//-width-->0(代表原来的位置)
        }
        if (isDiscrollTranslationFrom(TRANSLATION_FROM_RIGHT)) {
            setTranslationX(mWidth);//width-->0(代表原来的位置)
        }
    }
}

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

public class DiscrollView extends ScrollView {

    private DiscrollViewContent mContent;

    public DiscrollView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        View content = getChildAt(0);
        mContent = (DiscrollViewContent) content;
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        View first = mContent.getChildAt(0);
        first.getLayoutParams().height = getHeight();
    }


    @Override
    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
        super.onScrollChanged(l, t, oldl, oldt);

        int scrollViewHeight = getHeight();
        //监听滑动----接口---->控制DiscrollViewContent的属性
        //遍历MyLinearLayout里面所有子控件(MyViewGroup)
        for (int i = 0; i < mContent.getChildCount(); i++) {
            View child = mContent.getChildAt(i);
            if (!(child instanceof DiscrollvableInterface)) {
                continue;
            }

            //ratio:0~1
            DiscrollvableInterface discrollvableInterface = (DiscrollvableInterface) child;
            //1.child离scrollview顶部的高度
            int discrollvableTop = child.getTop();
            int discrollvableHeight = child.getHeight();

            //2.得到scrollview滑出去的高度
            //3.得到child离屏幕顶部的高度
            int discrollvableAbsoluteTop = discrollvableTop - t;
            //什么时候执行动画?当child滑进屏幕的时候
            if (discrollvableAbsoluteTop <= scrollViewHeight) {
                int visibleGap = scrollViewHeight - discrollvableAbsoluteTop;
                //确保ratio是在0~1,超过了1 也设置为1
                discrollvableInterface.onDiscrollve(clamp(visibleGap / (float) discrollvableHeight, 1f, 0f));
            } else {//否则,就恢复到原来的位置
                discrollvableInterface.onResetDiscrollve();
            }
        }
    }

    public static float clamp(float value, float max, float min) {
        return Math.max(Math.min(value, max), min);
    }
}

设置属性,偷梁换柱

public class DiscrollViewContent extends LinearLayout {

    public DiscrollViewContent(Context context, AttributeSet attrs) {
        super(context, attrs);
        setOrientation(VERTICAL);
    }

    @Override
    public LayoutParams generateLayoutParams(AttributeSet attrs) {
        // 得到xml里面穿过来的参数
        return new MyLayoutParams(getContext(), attrs);
    }

    @Override
    public void addView(View child, int index, android.view.ViewGroup.LayoutParams params) {
        //从child里面拿到我自定义的属性,传到discrollvableView里面
        MyLayoutParams p = (MyLayoutParams) params;
        if (!isDiscrollvable(p)) {//判断该view是否穿了自定义属性值,不是就不需要执行动画,不包一层FrameLayout
            super.addView(child, index, params);
        } else {
            //在addView里面插一脚,往child外面包裹一层FrameLayout
            DiscrollvableView discrollvableView = new DiscrollvableView(getContext());
            discrollvableView.setmDiscrollveAlpha(p.mDiscrollveAlpha);
            discrollvableView.setmDisCrollveTranslation(p.mDisCrollveTranslation);
            discrollvableView.setmDiscrollveScaleX(p.mDiscrollveScaleX);
            discrollvableView.setmDiscrollveScaleY(p.mDiscrollveScaleY);
            discrollvableView.setmDiscrollveFromBgColor(p.mDiscrollveFromBgColor);
            discrollvableView.setmDiscrollveToBgColor(p.mDiscrollveToBgColor);
            discrollvableView.addView(child);
            super.addView(discrollvableView, index, params);
        }
    }

    private boolean isDiscrollvable(MyLayoutParams p) {
        return p.mDiscrollveAlpha || p.mDiscrollveScaleX
                || p.mDiscrollveScaleY || p.mDisCrollveTranslation != -1
                || (p.mDiscrollveFromBgColor != -1 && p.mDiscrollveToBgColor != -1);
    }

    public static class MyLayoutParams extends LinearLayout.LayoutParams {
        public int mDiscrollveFromBgColor;//背景颜色变化开始值
        public int mDiscrollveToBgColor;//背景颜色变化结束值
        public boolean mDiscrollveAlpha;//是否需要透明度动画
        public int mDisCrollveTranslation;//平移值
        public boolean mDiscrollveScaleX;//是否需要x轴方向缩放
        public boolean mDiscrollveScaleY;//是否需要y轴方向缩放
        public MyLayoutParams(Context context, AttributeSet attrs) {
            super(context, attrs);
            // 从child里面拿到我自定义的属性
            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DiscrollView_LayoutParams);
            mDiscrollveAlpha = a.getBoolean(R.styleable.DiscrollView_LayoutParams_discrollve_alpha, false);
            mDiscrollveScaleX = a.getBoolean(R.styleable.DiscrollView_LayoutParams_discrollve_scaleX, false);
            mDiscrollveScaleY = a.getBoolean(R.styleable.DiscrollView_LayoutParams_discrollve_scaleY, false);
            mDisCrollveTranslation = a.getInt(R.styleable.DiscrollView_LayoutParams_discrollve_translation, -1);
            mDiscrollveFromBgColor = a.getColor(R.styleable.DiscrollView_LayoutParams_discrollve_fromBgColor, -1);
            mDiscrollveToBgColor = a.getColor(R.styleable.DiscrollView_LayoutParams_discrollve_toBgColor, -1);
            a.recycle();
        }
    }
}

最后是一些自定义属性

<?xml version="1.0" encoding="UTF-8"?>
<resources>
    <declare-styleable name="DiscrollView_LayoutParams">
        <attr name="discrollve_alpha" format="boolean"/>
        <attr name="discrollve_scaleX" format="boolean"/>
        <attr name="discrollve_scaleY" format="boolean"/>
        <attr name="discrollve_fromBgColor" format="color"/>
        <attr name="discrollve_toBgColor" format="color"/>
        <attr name="discrollve_translation"/>
    </declare-styleable>

    <attr name="discrollve_translation">
        <flag name="fromTop" value="0x01" />
        <flag name="fromBottom" value="0x02" />
        <flag name="fromLeft" value="0x04" />
        <flag name="fromRight" value="0x08" />
    </attr>
</resources>

布局

<com.cj5785.customanimationaddview.DiscrollView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:custom="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.cj5785.customanimationaddview.DiscrollViewContent
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="560dp"
            android:background="@android:color/white"
            android:gravity="center"
            android:text="测试文字使用情况"
            android:textSize="25sp" />

        <View
            android:layout_width="match_parent"
            android:layout_height="200dp"
            android:background="@android:color/darker_gray"
            custom:discrollve_alpha="true" />

        <ImageView
            android:layout_width="89dp"
            android:layout_height="80dp"
            android:src="@drawable/duck"
            custom:discrollve_alpha="true"
            custom:discrollve_translation="fromLeft|fromBottom" />

        <View
            android:layout_width="match_parent"
            android:layout_height="200dp"
            custom:discrollve_fromBgColor="#ffff00"
            custom:discrollve_toBgColor="#88EE66" />

        <ImageView
            android:layout_width="150dp"
            android:layout_height="106dp"
            android:layout_gravity="right"
            android:src="@drawable/camera"
            custom:discrollve_translation="fromRight" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:text="第二段文字使用测试"
            android:textSize="28sp"
            custom:discrollve_alpha="true"
            custom:discrollve_translation="fromBottom" />

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_margin="20dp"
            android:src="@drawable/sun"
            custom:discrollve_scaleX="true"
            custom:discrollve_scaleY="true" />

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_margin="20dp"
            android:src="@drawable/balloon"
            custom:discrollve_translation="fromLeft|fromBottom" />

    </com.cj5785.customanimationaddview.DiscrollViewContent>

</com.cj5785.customanimationaddview.DiscrollView>

效果如下
自定义动画框架-addView

自定义LayoutInflater

这里实现一个仿小红书的视差动画效果
首先定义一个根布局,然后实例化其自定义控件

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/white">

    <com.cj5785.customanimationlayoutinflater.ParallaxContainer
        android:id="@+id/parallax_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

    <ImageView
        android:id="@+id/iv_man"
        android:layout_width="66dp"
        android:layout_height="202dp"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="10dp"
        android:background="@drawable/intro_item_manrun_1"/>

</RelativeLayout>

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

public class ParallaxAdapter extends FragmentPagerAdapter {
    private List<ParallaxFragment> fragmentList;

    public ParallaxAdapter(FragmentManager fm, List<ParallaxFragment> fragmentList) {
        super(fm);
        this.fragmentList = fragmentList;
    }

    @Override
    public Fragment getItem(int position) {
        return fragmentList.get(position);
    }

    @Override
    public int getCount() {
        return fragmentList.size();
    }
}

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

public class ParallaxFragment extends Fragment {
    private List<View> parallaxViews = new ArrayList<>();

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        Bundle bundle = getArguments();
        int layoutId = bundle.getInt("layoutId");
        //正常情况下在这里获得view,然后返回,我们就在这里偷梁换柱
//        View view = inflater.inflate(layoutId, container);
        //使用自定义的渲染器渲染
        ParallaxLayoutInflater layoutInflater = new ParallaxLayoutInflater(inflater, getActivity(), this);

        return layoutInflater.inflate(layoutId, null);
    }

    public List<View> getParallaxViews() {
        return parallaxViews;
    }
}

动画引导层

//引导页的最外层布局
public class ParallaxContainer extends FrameLayout implements OnPageChangeListener {
    private List<ParallaxFragment> fragmentList;
    private ParallaxAdapter adapter;
    private float containerWidth;
    private ImageView iv_man;

    public ParallaxContainer(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    //指定引导页的所有页面布局文件
    public void setUp(int... ids) {
        //初始化framentList,在调用处直接调用setUp()
        fragmentList = new ArrayList<>();
        for (int i = 0; i < ids.length; i++) {
            ParallaxFragment fragment = new ParallaxFragment();
            Bundle bundle = new Bundle();
            //Fragment中需要加载的布局文件id
            bundle.putInt("layoutId", ids[i]);
            fragment.setArguments(bundle);
            fragmentList.add(fragment);
        }
        //设置ViewPager
        ViewPager viewPager = new ViewPager(getContext());
        viewPager.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
        viewPager.setId(R.id.parallax_pager);
        adapter = new ParallaxAdapter(((MainActivity) getContext()).getSupportFragmentManager(), fragmentList);
        viewPager.setAdapter(adapter);
        addView(viewPager, 0);
        //设置监听
        viewPager.addOnPageChangeListener(this);
    }

    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        containerWidth = getWidth();
        //在翻页的过程中,不断根据视图的标签中对应的动画参数,改变视图的位置或者透明度
        //获取到进入的页面
        ParallaxFragment inFragment = null;
        try {
            inFragment = fragmentList.get(position - 1);
        } catch (Exception e) {
        }
        //获取到退出的页面
        ParallaxFragment outFragment = null;
        try {
            outFragment = fragmentList.get(position);
        } catch (Exception e) {
        }

        if (inFragment != null) {
            //获取Fragment上所有的视图,实现动画效果
            List<View> inViews = inFragment.getParallaxViews();
            if (inViews != null) {
                for (View view : inViews) {
                    //获取标签,从标签上获取所有的动画参数
                    ParallaxViewTag tag = (ParallaxViewTag) view.getTag(R.id.parallax_view_tag);
                    if (tag == null) {
                        continue;
                    }
                    //translationY改变view的偏移位置,translationY=100,代表view在其原始位置向下移动100
                    //仔细观察进入的fragment中view从远处过来,不断向下移动,最终停在原始位置
                    view.setTranslationX((containerWidth - positionOffsetPixels) * tag.xIn);
                    view.setTranslationY((containerWidth - positionOffsetPixels) * tag.yIn);
                }
            }
        }

        if (outFragment != null) {
            List<View> outViews = outFragment.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应为负数
                    view.setTranslationX((-positionOffsetPixels) * tag.xOut);
                    view.setTranslationY((-positionOffsetPixels) * tag.yOut);
                }
            }
        }
    }

    @Override
    public void onPageSelected(int position) {
        if (position == adapter.getCount() - 1) {
            iv_man.setVisibility(INVISIBLE);
        } else {
            iv_man.setVisibility(VISIBLE);
        }
    }

    @Override
    public void onPageScrollStateChanged(int state) {
        AnimationDrawable animation = (AnimationDrawable) iv_man.getBackground();
        switch (state) {
            case ViewPager.SCROLL_STATE_DRAGGING:
                animation.start();
                break;
            case ViewPager.SCROLL_STATE_IDLE:
                animation.stop();
                break;
            default:
                break;
        }
    }

    public void setIv_man(ImageView iv_man) {
        this.iv_man = iv_man;
    }
}

拦截系统处理xml

public class ParallaxLayoutInflater extends LayoutInflater {

    private static final String TAG = "ParallaxLayoutInflater";
    private ParallaxFragment parallaxFragment;

    protected ParallaxLayoutInflater(LayoutInflater original, Context newContext, ParallaxFragment parallaxFragment) {
        super(original, newContext);
        this.parallaxFragment = parallaxFragment;
        //源代码中存在:
        //View view;
        //if (mFactory2 != null) {
        //  view = mFactory2.onCreateView(parent, name, context, attrs);
        //} else if (mFactory != null) {
        //  view = mFactory.onCreateView(name, context, attrs);
        //} else {
        //  view = null;
        //}
        //if (view == null && mPrivateFactory != null) {
        //  view = mPrivateFactory.onCreateView(parent, name, context, attrs);
        //}
        //也就是说只要实现Factory,那么就不会调用后面的方法
        setFactory(new ParallaxFactory(this));
    }

    @Override
    public LayoutInflater cloneInContext(Context newContext) {
        //实际用于创建LayoutInflater的方法
        return new ParallaxLayoutInflater(this, newContext, parallaxFragment);
    }

    //这个框架最核心的地方就是在这里设置Factory,从而进行拦截
	class ParallaxFactory implements LayoutInflater.Factory{

        private LayoutInflater inflater;
        //系统提供的视图分为android.widget.xxx和android.view.xxx
        private final String[] prefixs = {
                "android.widget.",
                "android.view."
        };

        public ParallaxFactory(LayoutInflater inflater) {
            this.inflater = inflater;
        }

        @Override
        public View onCreateView(String name, Context context, AttributeSet attrs) {
            //实例化view
            View view = null;
            if (view == null) {
				view = createViewOrFailQuietly(name,context,attrs);
            }
            if (view != null) {
                //获取自定义属性,并将自定义标签值绑定到view上面
                getCustomAttrs(context, attrs, view);
                parallaxFragment.getParallaxViews().add(view);
            }
            return view;
        }

        private void getCustomAttrs(Context context, AttributeSet attrs, View 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
            };
			//获取
            TypedArray typedArray = context.obtainStyledAttributes(attrs, attrIds);
            if (typedArray != null && typedArray.length() > 0) {
				//获取自定义属性的值
                Log.d(TAG, "getCustomAttrs");
                ParallaxViewTag tag = new ParallaxViewTag();
                tag.alphaIn = typedArray.getFloat(0, 0f);
                tag.alphaOut = typedArray.getFloat(1, 0f);
                tag.xIn = typedArray.getFloat(2, 0f);
                tag.xOut = typedArray.getFloat(3, 0f);
                tag.yIn = typedArray.getFloat(4, 0f);
                tag.yOut = typedArray.getFloat(5, 0f);
                view.setTag(R.id.parallax_view_tag, tag);
            }
            typedArray.recycle();
        }

        private View createViewOrFailQuietly(String name, String prefix, Context context, AttributeSet attrs) {
            try {
				//通过系统的inflater创建视图,读取系统的属性
                return inflater.createView(name, prefix, attrs);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
                return null;
            }
        }

        private View createViewOrFailQuietly(String name, Context context, AttributeSet attrs) {
            //通过系统inflater创建视图
			//1.自定义控件标签名称带点,所以创建时不需要前缀
            if (name.contains(".")) {
                return createViewOrFailQuietly(name, null, context, attrs);
            }
			//2.系统视图需要加上前缀
			                for (String prefix : prefixs) {
                    View view = createViewOrFailQuietly(name, prefix, context, attrs);
                    if (view != null) {
                        return view;
                    }
                }
            return null;
        }
    }
}

参数控制

//视差动画播放时参数的控制
public class ParallaxViewTag {
    protected int index;
    protected float xIn;
    protected float xOut;
    protected float yIn;
    protected float yOut;
    protected float alphaIn;
    protected float alphaOut;

    @Override
    public String toString() {
        return "ParallaxViewTag [index=" + index + ", xIn=" + xIn + ", xOut=" + xOut + ", yIn=" + yIn
                + ", yOut=" + yOut + ", alphaIn=" + alphaIn + ", alphaOut=" + alphaOut + "]";
    }
}

同时完成人行走的帧动画

<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="false">
    <item
        android:drawable="@drawable/intro_item_manrun_1"
        android:duration="200" />
    <item
        android:drawable="@drawable/intro_item_manrun_2"
        android:duration="200" />
</animation-list>

自定义属性

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <attr name="a_in" format="float" />
    <attr name="a_out" format="float" />
    <attr name="x_in" format="float" />
    <attr name="x_out" format="float" />
    <attr name="y_in" format="float" />
    <attr name="y_out" format="float" />
</resources>

ids

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <item name="parallax_pager" type="id"/>
    <item name="parallax_view_tag" type="id"/>
</resources>

实现效果如下图所示
自定义动画框架-inflater

posted @ 2019-04-06 23:12  cj5785  阅读(323)  评论(0编辑  收藏  举报