Android高级UI开发(二十九)自定义动画框架-让scrollview里的所有控件随着滑动的距离执行各自的动画

举报
yd_57386892 发表于 2020/12/28 22:55:14 2020/12/28
3.7k+ 0 0
【摘要】         这次我们做一个动画框架,配置scrollview里包含的控件的自定义属性,就可以实现滑动Scrollview时,里面的控件根据滑动的距离执行各自的动画进度。scrollivew里包含的这些控件可以是任意常用的控件,如 imageView,Button,TextView等。我们将给这些普通的系统控件配置自定义属性!看到这里是不是觉得无法实现,因为系统的Image...

        这次我们做一个动画框架,配置scrollview里包含的控件的自定义属性,就可以实现滑动Scrollview时,里面的控件根据滑动的距离执行各自的动画进度。scrollivew里包含的这些控件可以是任意常用的控件,如 imageView,Button,TextView等。我们将给这些普通的系统控件配置自定义属性!看到这里是不是觉得无法实现,因为系统的ImageView,Button等是无法识别我们自定义的属性值的,系统的控件怎么识别我们随便定义的属性呢。今天我们就来解决这个问题,解决这个问题的意义在于让系统控件能像我们自定义控件一样,配置了属性就可以执行相应的动画。我们先来看一下运行效果:

源码下载地址:https://download.csdn.net/download/gaoxiaoweiandy/11136228

我们还是从界面布局讲起,直观上分析一下。

1. 布局

   activity_main.xml


      <com.example.animateframe1.DiscrollView 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"
      xmlns:app="http://schemas.android.com/apk/res/com.example.animateframe1">
      <com.example.animateframe1.DiscrollViewContent
      android:layout_width="match_parent"
      android:layout_height="match_parent">
      <TextView
      android:layout_width="match_parent"
      android:layout_height="600dp"
      android:background="@android:color/white"
      android:textColor="@android:color/black"
      android:textSize="25sp"
      android:padding="25dp"
      tools:visibility="gone"
      android:gravity="center"
      android:fontFamily="serif"
      android:text="冯绍峰对着倪妮发誓说:‘’如果有一天我离开了你,我就把名字倒着念‘’。倪妮说:‘我也是’’!——尼玛,看着我也是醉了!" />
      <View
      android:layout_width="match_parent"
      android:layout_height="200dp"
      android:background="#007788"
      app:discrollve_alpha="true"
       />
      <ImageView
      android:layout_width="200dp"
      android:layout_height="120dp"
      app:discrollve_alpha="true"
      app:discrollve_translation="fromLeft|fromBottom"
      android:src="@drawable/baggage" />
      <View
      android:layout_width="match_parent"
      android:layout_height="200dp"
      app:discrollve_fromBgColor="#ffff00"
      app:discrollve_toBgColor="#88EE66" />
      <ImageView
      android:layout_width="220dp"
      android:layout_height="110dp"
      android:layout_gravity="right"
      android:src="@drawable/camera"
      app:discrollve_translation="fromRight" />
      <TextView
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:padding="20dp"
      android:fontFamily="serif"
      android:gravity="center"
      android:text="眼见范冰冰与李晨在一起了,孩子会取名李冰冰;李冰冰唯有嫁给范伟,生个孩子叫范冰冰,方能扳回一城。"
      android:textSize="23sp"
      app:discrollve_alpha="true"
      app:discrollve_translation="fromBottom" />
      <ImageView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_margin="20dp"
      android:layout_gravity="center"
      android:src="@drawable/sweet"
      app:discrollve_scaleX="true"
      app:discrollve_scaleY="true"  />
      <ImageView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_margin="20dp"
      android:layout_gravity="center"
      android:src="@drawable/camera"
      app:discrollve_translation="fromLeft|fromBottom"
       />
      </com.example.animateframe1.DiscrollViewContent>
      </com.example.animateframe1.DiscrollView>
  
 

我们可以看出布局结构图如下

DisScrollview(重写了Scrollview) --->DisScrollviewContent(重写了LinearLayout)--->包含的系统控件。系统控件中类似于app:discrollve_translation="fromRight" 的属性都是自定义属性。现在我们来解释下为什么要重写DisScrollview与DisScrollviewContent以及如何让系统控件如Imageview等来识别我们的自定义属性。

1.1  为什么需要自定义DisScrollview

为什么要重写Scrollview,这个是为了重写

protected void onScrollChanged(int l, int t, int oldl, int oldt)函数,用于获取滚动的距离t,对于第一个控件来说的话,t/height(第一个控件的View的高度)就是aplha透明度的比例。当t=height时,aplha=1,为不透明。alpha在0-1之间。
 

在这里我们先不细谈onScrollChanged里的具体算法,我们现在只需大概知道它是用来获取滑动的距离t的,然后t/控件的height就可以得出一个比例,执行动画(透明度,平移X,平移Y等)的比例。

1.2 为什么需要DisScrollviewContent

这个是关键,为了解决系统控件(如Imageview)不识别我们的自定义属性的问题。

DisScrollviewContent继承于LinearLayout,我们自定义LinearLayout,无非是想改变LinearLayout的行为。那么想改变什么行为呢。我们想利用DisScrollviewContent来获取DisScrollviewContent包含的各个系统控件,并且解析到为系统控件配置的自定义属性值。这个我们很容易用一个for循环获取到各个子控件及相关XML自定义属性的值。但是我们获取到了这些自定义控件属性又能如何,imageview等系统控件又不识别,就不能执行动画。那我们获取这些自定义属性给谁用??

我们可以在imageview外再包裹一个自定义父布局ViewGroup,然后把这些获取到的自定义属性(动画属性值)赋予这个包裹的VIEWGROUP,然后让父布局可以根据属性值来执行动画,那么里面的imageview是不是也就跟着动起来了呢?这个想法应该可以实现,整体布局都执行动画飞了起来,子布局自然就跟着动了起来,相当于我们的系统控件(如Imageview)执行了动画。这是一个瞒天过海的做法,关于如何在LinearLayout addView之前给每一个系统控件包裹VIEWGROUP的事情,就交给了我们自定义的LinearLayout:DisScrollviewContent。这就是我们为什么需要DisScrollviewContent的原因:“”包裹+动画 = 子控件动画 = 瞒天过海“” 现在我们的布局应该是这样子的:

至于代码的具体实现我们后面章节讲解,当然不看讲解,直接去下载源码看也行。

我们总结一下上面的分析:

1. 自定义LinearLayout:DisScrollviewContent,改变布局结构,用自定义VIEWGROUP包裹系统控件如imageview等。

2. 自定义VIEWGROUP(用于包裹)

3. 自定义Scrollview:DisScrollview,根据滑动的距离来计算动画执行的进度比例。

Ok,接下来我们就分析一下上面1,2,3的核心代码是如何实现的。

2. 代码

2.1 DisScrollviewContent ( 自定义LinearLayout )


      package com.example.animateframe1;
      import android.content.Context;
      import android.content.res.TypedArray;
      import android.util.AttributeSet;
      import android.view.View;
      import android.widget.LinearLayout;
      public class DiscrollViewContent extends LinearLayout {
     	public DiscrollViewContent(Context context, AttributeSet attrs) {
     		super(context, attrs);
      		setOrientation(VERTICAL);
      	}
      /**
       * 这个函数在加载XML布局时自动调用,可以获取到每一个系统控件配置的布局参数,包括自定义参数。
       * 每加载一个系统控件(如Imageview),则调用一次这个函数。
       * @param attrs
       * @return
       */
     	@Override
     	public LayoutParams generateLayoutParams(AttributeSet attrs) {
      //从attrs所有参数里提取自定义属性值,并保持在MyLayoutParams对象里,以供“自定义包裹VIEWGROUP"使用并执行动画。
     		return new MyLayoutParams(getContext(),attrs);
      	}
      /**
       * 这个函数是在generateLayoutParams之后执行,在这里我们可以获取到generateLayoutParams函数返回的MyLayoutParams里的自定义属性值。
       * 然后在addview系统控件(如Imageview)之前,先创建并添加一个“自定义包裹VIEWGROUP"视图,然后将自定义属性赋给这个视图,最后在把系统控件
       * addview到"自定义包裹VIEWGROUP"里,从而实现了在代码中为XML里的每一个系统控件外层包裹一个“自定义包裹VIEWGROUP"视图。
       * @param child
       * @param index
       * @param params
       */
     	@Override
     	public void addView(View child, int index,
       android.view.ViewGroup.LayoutParams params) {
      		MyLayoutParams p = (MyLayoutParams) params;
     		if(!isDiscrollvable(p)){  //没有自定义属性的系统控件,我们就不需要外层包裹一个“自定义包裹VIEWGROUP"视图。直接addview即可。
     			super.addView(child, index, params);
      		}else{
      //有自定义属性的系统控件,我们需要外层包裹一个“自定义包裹VIEWGROUP"视图。
      			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);
     			//先为child包裹一个外层视图
      			discrollvableView.addView(child);
     			//然后再把外层父视图添加到LinearLayout里。
     			super.addView(discrollvableView, index, params);
      		}
      	}
     	private boolean isDiscrollvable(MyLayoutParams p) {
     		// TODO Auto-generated method stub
     		return p.mDiscrollveAlpha||
       p.mDiscrollveScaleX||
       p.mDiscrollveScaleY||
       p.mDisCrollveTranslation!=-1||
       (p.mDiscrollveFromBgColor!=-1&&
       p.mDiscrollveToBgColor!=-1);
      	}
     	public static class MyLayoutParams extends LayoutParams{
     		public int mDiscrollveFromBgColor;//背景颜色变化开始值
     		public int mDiscrollveToBgColor;//背景颜色变化结束值
     		public boolean mDiscrollveAlpha;//透明度
     		public int mDisCrollveTranslation;//平移ֵ
     		public boolean mDiscrollveScaleX;//宽度缩放
     		public boolean mDiscrollveScaleY;//高度缩放
     		public MyLayoutParams(Context context, AttributeSet attrs) {
     			super(context, attrs);
     			// 备份自定义属性值
      			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();
      		}
      	}
      }
  
 

上面代码已经很详细,我们还是有必要解释下这几个重写函数,以及为什么要重写它们。


      2.1.1 generateLayoutParams(atrr):
         是在addview之前执行的。由于我们想要在addview里实现为系统控件(如Imageview)添加外层包裹VIEWGOURP,并且把系统控件里的自定义属性赋给“”自定义包裹VIEWGROUP",为了这个自定义VIEWGROUP将来能根据这些属性带着子控件一起飞起来(执行动画)。为了获取这些自定义属性,所以我们得重写generateLayoutParams函数。这个函数返回的LayoutParams恰好是接下来要调用的addview函数的LayoutParams params参数。因此我们在generateLayoutParams函数里从attr里筛选出自定义属性并保存到自定义的MyLayoutParams对象里,将来传递到addview中的LayoutParams就是MyLayoutParams类型。
  
 

2.1.2  addView(View child, int index, android.view.ViewGroup.LayoutParams params)

 addView是在generateLayoutParams之后执行,其中params参数就是2.1.1中generateLayoutParams函数返回的MyLayoutParams参数,从这个参数里可以获取到自定义属性,然后在addview系统控件(如Imageview)之前,先创建并添加一个“自定义包裹VIEWGROUP"视图,并将自定义属性赋给这个视图类,最后在把系统控件(如Imageview) *addview到"自定义包裹VIEWGROUP"里,最终一起添加到LinearLayout里。从而实现了在代码中为XML里的每一个系统控件外层包裹一个“自定义包裹VIEWGROUP"视图。

OK,至此我们已经实现了在系统控件外包裹一层可以识别自定义属性的VIEWGROUP父布局,接下来我们就来看一下这个自定义VIEWGROUP是如何执行动画的。

2.1 自定义VIEWGROUP:  DiscrollvableView


      package com.example.animateframe1;
      import android.animation.ArgbEvaluator;
      import android.content.Context;
      import android.util.AttributeSet;
      import android.widget.FrameLayout;
      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) {
     		// TODO Auto-generated method stub
     		super.onSizeChanged(w, h, oldw, oldh);
      		mWidth = w;
      		mHeight = h;
      		onResetDiscrollve();
      	}
     	public DiscrollvableView(Context context, AttributeSet attrs) {
     		super(context, attrs);
     		// TODO Auto-generated constructor stub
      	}
     	public DiscrollvableView(Context context) {
     		super(context);
     		// TODO Auto-generated constructor stub
      	}
     	@Override
     	public void onDiscrollve(float ratio) {
     		// ratio:0~1
     		//根据ratio执行动画进度
     		if(mDiscrollveAlpha){
      			setAlpha(ratio);
      		}
     		if(mDiscrollveScaleX){
      			setScaleX(ratio);
      		}
     		if(mDiscrollveScaleY){
      			setScaleY(ratio);
      		}
     		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);
      		}
     		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(代表原来的位置)
      		}
      	}
      }
  
 

我们发现自定义控件里实现了接口DiscrollvableInterface并重写了

public void onDiscrollve(float ratio)  //根据比例,执行动画进度

public void onResetDiscrollve();//逆向动画,恢复到初始状态。

在这两个函数里会根据自定义属性值与ratio来执行 这个“自定义VIEWGROUP包裹”的动画,从而内部包含的系统控件(如Imageview)等也会跟着动起来。那这两个函数是在什么地方调用的,以及ratio是怎么算出来的,那这个与Scrollview的滑动有关系。那我们就来看一下自定义Scrollview。

2.2 自定义Scrollview

DiscrollView.java


      package com.example.animateframe1;
      import android.content.Context;
      import android.util.AttributeSet;
      import android.util.Log;
      import android.view.View;
      import android.widget.ScrollView;
      public class DiscrollView extends ScrollView {
      	String TAG = "DiscrollView";
     	private DiscrollViewContent mContent;
     	public DiscrollView(Context context, AttributeSet attrs) {
     		super(context, attrs);
     		// TODO Auto-generated constructor stub
      	}
     	@Override
     	protected void onFinishInflate() {
     		// TODO Auto-generated method stub
     		super.onFinishInflate();
      		View content = getChildAt(0);
      		mContent = (DiscrollViewContent)content;
      	}
     	@Override
     	protected void onSizeChanged(int w, int h, int oldw, int oldh) {
     		// TODO Auto-generated method stub
     		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) {
     		// TODO Auto-generated method stub
     		super.onScrollChanged(l, t, oldl, oldt);
     		int scrollViewHeight = getHeight();
     		//监听滑动----接口---->控制DiscrollViewContent的属性
     		for(int i=0;i<mContent.getChildCount();i++){//遍历MyLinearLayout里面所有子控件(MyViewGroup)
      			View child = mContent.getChildAt(i);
     			if(!(child instanceof DiscrollvableInterface)){
      continue;
      			}
     			//ratio:0~1
      			DiscrollvableInterface discrollvableInterface =  (DiscrollvableInterface) child;
     			//1.child离scrollview顶部的高度 a
     			int discrollvableTop = child.getTop();
     			int discrollvableHeight = child.getHeight();
     			//2.得到scrollview滑出去的高度 b 就是int t,
     			//3.得到child离屏幕顶部的高度 c
     			int discrollvableAbsoluteTop = discrollvableTop - t;
      			Log.i(TAG,"discrollvableHeight0="+discrollvableHeight+
      "\r\nscrollViewHeight="+scrollViewHeight+",discrollvableAbsoluteTop="+discrollvableAbsoluteTop+"\r\nt="+t+",discrollvableTop="+discrollvableTop);//一屏的高度
     			//什么时候执行动画?当child滑进屏幕的时候
     			if(discrollvableAbsoluteTop <= scrollViewHeight)
      			{
      int visibleGap = scrollViewHeight - discrollvableAbsoluteTop;
       Log.i(TAG,"visibleGap="+visibleGap+",discrollvableHeight="+discrollvableHeight+
      "\r\nscrollViewHeight="+scrollViewHeight+",discrollvableAbsoluteTop="+discrollvableAbsoluteTop+"\r\nt="+t+",discrollvableTop="+discrollvableTop);//一屏的高度
      //确保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);
      	}
      }
  
 

我们主要来看一下onScrollChanged函数:


      @Override
      protected void onScrollChanged(int l, int t, int oldl, int oldt) {
      参数:left:滚动后,滚动条左端水平方向上的x位置。
      参数:top:滚动后,滚动条上端垂直方向上的y位置
      参数:oldl:滚动前,滚动条左端水平方向上的x位置。
      参数:oldt:滚动前,滚动条上端垂直方向上的y位置
         super.onScrollChanged(l, t, oldl, oldt);
         int scrollViewHeight = getHeight();这个scrollViewHeight 实质就是一屏的高度。
         //for选好遍历“自定义VIWEGROUP”,根据滑动距离比例,执行动画
         for(int i=0;i<mContent.getChildCount();i++){
        View child = mContent.getChildAt(i);
       if(!(child instanceof DiscrollvableInterface)){
       continue;
       }
       由于自定义VIEWGROUP包裹都实现了DiscrollvableInterface,所以没有实现的就没有包裹,不用执行动画直接continue。
       //ratio:0~1
       DiscrollvableInterface discrollvableInterface = (DiscrollvableInterface) child;
       //1.child离scrollview顶部的高度 a
       int discrollvableTop = child.getTop();
       int discrollvableHeight = child.getHeight();//执行动画时的总距离,比如平移
       //2.得到scrollview滑出去的高度 就是参数int t,
       //3.得到child离屏幕顶部的高度
       int discrollvableAbsoluteTop = discrollvableTop - t;//离屏幕顶端的初始高度 减去 滚动的距离就是当前时刻离屏幕顶端的高度
       //当前时刻离屏幕顶端的的距离 如果 小于 1屏的高度时,说明VIEWGROUP已进入屏幕,则开始执行动画
       if(discrollvableAbsoluteTop <= scrollViewHeight)
       {
       表示当上滑屏幕时,VIEWGROUP从屏幕底部探出的高度visibleGap
       int visibleGap = scrollViewHeight - discrollvableAbsoluteTop;
       //确保ratio是在0~1,超过了1 也设置为1,visibleGap / VIWGROUP控件的总高度就是ratio,执行动画的进度比例
       discrollvableInterface.onDiscrollve(clamp(visibleGap/(float)discrollvableHeight, 1f,0f));
       }else{//否则,就恢复到原来的位置
       discrollvableInterface.onResetDiscrollve();
       }
       }
      }
  
 

OK,所有流程就分析完了。现在总结一下思路:

1. 自定义LinearLayout的addview,让添加imageview等系统控件前,先在外层包裹一个自定义VIEWGROUP,并赋予它自定义属性的配置。

2. 自定义VIEWGROUP,接收滑动的ratio来执行动画进度

3. 自定义Scrollview,计算滑动的ratio,并调用自定义VIEWGROUP里的执行动画函数。

源码下载地址:https://download.csdn.net/download/gaoxiaoweiandy/11136228

文章来源: blog.csdn.net,作者:冉航--小虾米,版权归原作者所有,如需转载,请联系作者。

原文链接:blog.csdn.net/gaoxiaoweiandy/article/details/89379459

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

作者其他文章

评论(0

抱歉,系统识别当前为高风险访问,暂不支持该操作

    全部回复

    上滑加载中

    设置昵称

    在此一键设置昵称,即可参与社区互动!

    *长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

    *长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。