//清晰度 赞 踩 自定义动画框架,就是使用自定义属性,在XML文件中的某个控件添加自定义属性,那么这个控件就会执行相应的动画效果,例如:清晰度、渐变颜色、平移等等。 1、自定义属性 对于自定义控件来说,可以通过自定义属性来完成相应的业务逻辑;但是对于系统控件来说,像TextView、Button、ImageView等等,它们是不能识别自定义属性的,要想自定义属性能够被系统控件识别,通常有2种做法。 (1)自定义FrameLayout,将系统控件包裹起来,这样就能识别自定义属性。这种方式适合于控件数量较少的情况下,如果有100个控件,那么就需要套100个帧布局那是不现实的。 (2)自定义一个布局,然后作为像ConstraintLayout那样,作为该布局的根布局,把全部的控件包裹起来,那么不管里边有多少控件,都能够识别自定义属性。 2、UI绘制 在之前动态换肤框架时,详细介绍过UI的绘制流程,当时只是为了hook系统实例化控件,并没有介绍整个XML布局加载的具体流程,下面就从 在 系统在解析XML布局时,首先从root根节点开始解析,当解析完根节点之后,就需要解析子控件,调用 当解析完根节点之后,就通过 所以为了给每个系统控件套上一层帧布局,可以在addView方法中,实现。 因为每个系统控件都包裹一个 3、滑动控件 通过自定义滑动控件,来完成动画的实现。我们需要监听滑动的距离,根据滑动的百分比来控制动画。 其他2个自定义View的源码: Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。
Android属性动画3-----自定义动画框架_framelayout addview(view, params)
<resources>
//动画的类型
<declare-styleable name="animation_type">
//清晰度
<attr name="discrollve_alpha" format="boolean"></attr>
//X轴的缩放
<attr name="discrollve_ScaleX" format="boolean"></attr>
//Y轴的缩放
<attr name="discrollve_ScaleY" format="boolean"></attr>
//背景颜色变换的起始颜色
<attr name="discrollve_fromBgColor" format="color"></attr>
//背景颜色变换的最终颜色
<attr name="discrollve_toBgColor" format="color"></attr>
//动画转换的方向
<attr name="animation_orientation"></attr>
</declare-styleable>
<attr name="animation_orientation">
//从上方
<flag name="fromTop" value="0x01"></flag>
<flag name="fromBottom" value="0x02"></flag>
<flag name="fromLeft" value="0x04"></flag>
<flag name="fromRight" value="0x08"></flag>
</attr>
</resources>
LayoutInflater
的inflate方法开始说。public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
final Resources res = getContext().getResources();
if (DEBUG) {
Log.d(TAG, "INFLATING from resource: \"" + res.getResourceName(resource) + "\" ("
+ Integer.toHexString(resource) + ")");
}
View view = tryInflatePrecompiled(resource, res, root, attachToRoot);
if (view != null) {
return view;
}
//XML资源解析
XmlResourceParser parser = res.getLayout(resource);
try {
return inflate(parser, root, attachToRoot);
} finally {
parser.close();
}
}
inflate
方法中,有一个XmlResourceParser
,这个类的主要工作就是解析XML布局。 // Temp is the root view that was found in the xml
final View temp = createViewFromTag(root, name, inflaterContext, attrs);
ViewGroup.LayoutParams params = null;
if (root != null) {
if (DEBUG) {
System.out.println("Creating params from root: " +
root);
}
// Create layout params that match root, if supplied
params = root.generateLayoutParams(attrs);
if (!attachToRoot) {
// Set the layout params for temp if we are not
// attaching. (If we are, we use addView, below)
temp.setLayoutParams(params);
}
}
if (DEBUG) {
System.out.println("-----> start inflating children");
}
// Inflate all children under temp against its context.
//解析子布局
rInflateChildren(parser, temp, attrs, true);
if (DEBUG) {
System.out.println("-----> done inflating children");
}
rInflateChildren
final void rInflateChildren(XmlPullParser parser, View parent, AttributeSet attrs,
boolean finishInflate) throws XmlPullParserException, IOException {
rInflate(parser, parent, parent.getContext(), attrs, finishInflate);
}
void rInflate(XmlPullParser parser, View parent, Context context,
AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException {
final int depth = parser.getDepth();
int type;
boolean pendingRequestFocus = false;
while (((type = parser.next()) != XmlPullParser.END_TAG ||
parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
if (type != XmlPullParser.START_TAG) {
continue;
}
final String name = parser.getName();
if (TAG_REQUEST_FOCUS.equals(name)) {
pendingRequestFocus = true;
consumeChildElements(parser);
} else if (TAG_TAG.equals(name)) {
parseViewTag(parser, parent, attrs);
} else if (TAG_INCLUDE.equals(name)) {
if (parser.getDepth() == 0) {
throw new InflateException("<include /> cannot be the root element");
}
parseInclude(parser, context, parent, attrs);
} else if (TAG_MERGE.equals(name)) {
throw new InflateException("<merge /> must be the root element");
} else {
final View view = createViewFromTag(parent, name, context, attrs);
final ViewGroup viewGroup = (ViewGroup) parent;
final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
rInflateChildren(parser, view, attrs, true);
viewGroup.addView(view, params);
}
}
createViewFromTag
解析子控件,通过addView
,将子View添加到容器中,这个容器就是父容器parent
根节点。@Override
public void addView(View child) {
//child子View
AnimatorFrameLayout frameLayout = new AnimatorFrameLayout(getContext());
//这个AnimatorFrameLayout就是ViewGroup
frameLayout.addView(child);
super.addView(frameLayout);
}
ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs)
ViewGroup
通过generateLayoutParams
将所有的属性整合,添加到容器中,因此需要在这个方法中,来进行自定义属性的分析。FrameLayout
,那么这个FrameLayout
就是ViewGroup
,解析完ViewGroup
之后,就会解析它其中包裹的一个子控件,这个子控件是系统控件,有些自定义属性需要解析,因为默认解析的就是系统控件属性。 //child子View
AnimatorFrameLayout frameLayout = new AnimatorFrameLayout(getContext());
//这个AnimatorFrameLayout就是ViewGroup
frameLayout.addView(child);
//设置属性
frameLayout.setmDiscrollveAlpha(layoutParams.mDiscrollveAlpha);
frameLayout.setmDiscrollveFromBgColor(layoutParams.mDiscrollveFromBgColor);
frameLayout.setmDiscrollveScaleX(layoutParams.mDiscrollveScaleX);
frameLayout.setmDiscrollveScaleY(layoutParams.mDiscrollveScaleY);
frameLayout.setmDisCrollveTranslation(layoutParams.mDisCrollveTranslation);
frameLayout.setmDiscrollveToBgColor(layoutParams.mDiscrollveToBgColor);
super.addView(frameLayout);
public class AnimatorScrollerView extends ScrollView {
public AnimatorScrollerView(Context context) {
this(context,null);
}
public AnimatorScrollerView(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public AnimatorScrollerView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
//滑动监听
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
//获取AnimatorLinearLayout全部的子控件(AnimatorFrameLayout)
for (int i = 0; i < mContent.getChildCount(); i++) {
View child = getChildAt(i);
// AnimatorFrameLayout frameLayout = (AnimatorFrameLayout) child;
IScroller scroller = (IScroller) child;
//该框架设计的核心就是,通过获取系统控件的自定义属性,获取到自定义属性之后,
//控制动画
//系数的计算 计算从下面冒出的距离 / 控件的宽度 = 系数
//计算当前滑动的距离
int childTop = getTop();
int abslouteTop = childTop - t;
int viewHeight = getHeight();
if(abslouteTop <= viewHeight){
int scrollHight = viewHeight - abslouteTop;
float rate = scrollHight / (float)child.getHeight();
scroller.onScroll(clamp(rate,1,0));
}else{
//划出去
scroller.onResetScroll();
}
}
}
private float clamp(float rate,float max,float min) {
return Math.max(Math.min(rate,max),min);
}
AnimatorLinearLayout mContent;
@Override
protected void onFinishInflate() {
super.onFinishInflate();
//在页面渲染完成之后,拿到AnimatorLayout
mContent = (AnimatorLinearLayout) getChildAt(0);
}
}
AnimatorLinearLayout
:public class AnimatorLinearLayout extends LinearLayout {
public AnimatorLinearLayout(Context context) {
this(context,null,0);
}
public AnimatorLinearLayout(Context context, @Nullable AttributeSet attrs) {
this(context, attrs,0);
}
public AnimatorLinearLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new AnimatorLayoutParams(getContext(),attrs);
}
@Override
public void addView(View child, ViewGroup.LayoutParams params) {
//获取LayoutParams
AnimatorLayoutParams layoutParams = (AnimatorLayoutParams) params;
if(isDiscrollve(params)){
//如果没有自定义属性,就不需要包裹
super.addView(child);
}else{
//child子View
AnimatorFrameLayout frameLayout = new AnimatorFrameLayout(getContext());
//这个AnimatorFrameLayout就是ViewGroup
frameLayout.addView(child);
//设置属性
frameLayout.setmDiscrollveAlpha(layoutParams.mDiscrollveAlpha);
frameLayout.setmDiscrollveFromBgColor(layoutParams.mDiscrollveFromBgColor);
frameLayout.setmDiscrollveScaleX(layoutParams.mDiscrollveScaleX);
frameLayout.setmDiscrollveScaleY(layoutParams.mDiscrollveScaleY);
frameLayout.setmDisCrollveTranslation(layoutParams.mDisCrollveTranslation);
frameLayout.setmDiscrollveToBgColor(layoutParams.mDiscrollveToBgColor);
super.addView(frameLayout);
}
}
private boolean isDiscrollve(ViewGroup.LayoutParams params) {
AnimatorLayoutParams layoutParams = (AnimatorLayoutParams) params;
return layoutParams.mDiscrollveAlpha ||
layoutParams.mDiscrollveScaleX ||
layoutParams.mDiscrollveScaleY ||
layoutParams.mDisCrollveTranslation != -1 ||
(layoutParams.mDiscrollveFromBgColor != -1 && layoutParams.mDiscrollveToBgColor != -1);
}
//动画属性
public class AnimatorLayoutParams extends LinearLayout.LayoutParams{
public boolean mDiscrollveAlpha;
public boolean mDiscrollveScaleX;
public boolean mDiscrollveScaleY;
public int mDisCrollveTranslation;
public int mDiscrollveFromBgColor;
public int mDiscrollveToBgColor;
public AnimatorLayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
TypedArray a = getContext().obtainStyledAttributes(attrs,R.styleable.animation_type);
//没有传属性过来,给默认值FALSE
mDiscrollveAlpha = a.getBoolean(R.styleable.animation_type_discrollve_alpha, false);
mDiscrollveScaleX = a.getBoolean(R.styleable.animation_type_discrollve_ScaleX, false);
mDiscrollveScaleY = a.getBoolean(R.styleable.animation_type_discrollve_ScaleY, false);
mDisCrollveTranslation = a.getInt(R.styleable.animation_type_animation_orientation, -1);
mDiscrollveFromBgColor = a.getColor(R.styleable.animation_type_discrollve_fromBgColor, -1);
mDiscrollveToBgColor = a.getColor(R.styleable.animation_type_discrollve_toBgColor, -1);
a.recycle();
}
}
}
AnimatorFrameLayout
:public class AnimatorFrameLayout extends FrameLayout implements IScroller{
public AnimatorFrameLayout(@NonNull Context context) {
this(context,null,0);
}
public AnimatorFrameLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
this(context, attrs,0);
}
public AnimatorFrameLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
//保存自定义属性
//定义很多的自定义属性
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;
}
@Override
public void onScroll(float rate) {
//判断
if(mDiscrollveAlpha){
setAlpha(rate);
}
if(mDiscrollveScaleX){
setScaleX(rate);
}
if(mDiscrollveScaleY){
setScaleY(rate);
}
//平移动画,有四个方向,需要整合成一个属性
if(isTranslationFrom(TRANSLATION_FROM_BOTTOM)){
setTranslationY(mHeight * (1-rate));
}
if(isTranslationFrom(TRANSLATION_FROM_TOP)){
setTranslationY(-mHeight * (1-rate));
}
if(isTranslationFrom(TRANSLATION_FROM_LEFT)){
setTranslationX(-mWidth * (1-rate));
}
if(isTranslationFrom(TRANSLATION_FROM_RIGHT)){
setTranslationX(mWidth * (1-rate));
}
if(mDiscrollveFromBgColor != -1 && mDiscrollveToBgColor != -1){
setBackgroundColor((Integer) sArgbEvaluator.evaluate(rate,mDiscrollveFromBgColor,mDiscrollveToBgColor));
}
}
@Override
public void onResetScroll() {
//判断
if(mDiscrollveAlpha){
setAlpha(0);
}
if(mDiscrollveScaleX){
setScaleX(0);
}
if(mDiscrollveScaleY){
setScaleY(0);
}
//平移动画,有四个方向,需要整合成一个属性
if(isTranslationFrom(TRANSLATION_FROM_BOTTOM)){
setTranslationY(mHeight);
}
if(isTranslationFrom(TRANSLATION_FROM_TOP)){
setTranslationY(-mHeight);
}
if(isTranslationFrom(TRANSLATION_FROM_LEFT)){
setTranslationX(-mWidth);
}
if(isTranslationFrom(TRANSLATION_FROM_RIGHT)){
setTranslationX(mWidth);
}
// if(mDiscrollveFromBgColor != -1 && mDiscrollveToBgColor != -1){
// setBackgroundColor((Integer) sArgbEvaluator.evaluate(rate,mDiscrollveFromBgColor,mDiscrollveToBgColor));
// }
}
public boolean isTranslationFrom(int translationMask){
if(mDisCrollveTranslation == -1){
//没有偏移
return false;
}
return (mDisCrollveTranslation & translationMask) == translationMask;
}
}