浅析 NestedScrolling 嵌套滑动机制之实践篇 - 仿写饿了么商家详情页
3、设置 Collapse Content 部分透明度范围[contentTransY,downCollapsedAlphaY]
4、设置 Shop Bar 位移范围[contentTransY,downShopBarTransY]
5、设置收起按钮和滑动内容透明度[downContentAlphaY,downEndY]
下面是代码实现:
...private ArgbEvaluator iconArgbEvaluator;//返回 icon、拼团 icon 颜色渐变的 Evaluatorprivate ArgbEvaluator topBarArgbEvaluator;//topbar 颜色渐变的 Evaluator
public ElemeNestedScrollLayout(@NonNull Context context) {this(context, null);}
public ElemeNestedScrollLayout(@NonNull Context context, @Nullable AttributeSet attrs) {this(context, attrs, -1);}
public ElemeNestedScrollLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);mParentHelper = new NestedScrollingParentHelper(this);
iconArgbEvaluator = new ArgbEvaluator();topBarArgbEvaluator = new ArgbEvaluator();...
}
@Overridepublic void onNestedPreScroll(@NonNull View target, int dx, int dy, @NonNull int[] consumed, int type) {...
//根据 upAlphaScalePro,设置 logo、收藏 icon 缩放,搜索 icon、分享 icon 透明度 float upAlphaScalePro = getUpAlphaScalePro();alphaScaleByPro(upAlphaScalePro);
//根据 upAlphaGradientPro,设置 topBar 背景、返回 icon、拼团 icon 颜色渐变值,搜索框透明度 float upAlphaGradientPro = getUpAlphaGradientPro();alphaGradientByPro(upAlphaGradientPro);
//根据 downCollapsedAlphaPro,设置折叠内容透明度 float downCollapsedAlphaPro = getDownCollapsedAlphaPro();alphaCollapsedContentByPro(downCollapsedAlphaPro);
//根据 downContentAlphaPro,设置滑动内容、收起按钮的透明度 float downContentAlphaPro = getDownContentAlphaPro();alphaContentByPro(downContentAlphaPro);
//根据 downShopBarTransPro,设置购物内容内容位移 float downShopBarTransPro = getDownShopBarTransPro();transShopBarByPro(downShopBarTransPro);
//根据 upCollapsedContentTransPro,设置折叠内容位移 float upCollapsedContentTransPro = getUpCollapsedContentTransPro();transCollapsedContentByPro(upCollapsedContentTransPro);
if (mProgressUpdateListener!=null){mProgressUpdateListener.onUpAlphaScaleProUpdate(upAlphaScalePro);mProgressUpdateListener.onUpAlphaGradientProUpdate(upAlphaGradientPro);mProgressUpdateListener.onDownCollapsedAlphaProUpdate(downCollapsedAlphaPro);mProgressUpdateListener.onDownContentAlphaProUpdate(downContentAlphaPro);mProgressUpdateListener.onDownShopBarTransProUpdate(downShopBarTransPro);mProgressUpdateListener.onUpCollapsedContentTransProUpdate(upCollapsedContentTransPro);}}
/**
根据 upCollapsedContentTransPro,设置折叠内容位移*/private void transCollapsedContentByPro(float upCollapsedContentTransPro) {float collapsedContentTranY = - (upCollapsedContentTransPro * (contentTransY - topBarHeight));translation(mCollapsedContent,collapsedContentTranY);}
/**
根据 downShopBarTransPro,设置购物内容内容位移*/private void transShopBarByPro(float downShopBarTransPro) {float shopBarTransY = (1-downShopBarTransPro) * shopBarHeight;translation(mShopBar,shopBarTransY);}
/**
根据 downContentAlphaPro,设置滑动内容、收起按钮的透明度*/private void alphaContentByPro(float downContentAlphaPro) {setAlpha(mViewPager,downContentAlphaPro);setAlpha(mStl,downContentAlphaPro);setAlpha(mIvClose,1-downContentAlphaPro);if (mIvClose.getAlpha()==0){mIvClose.setVisibility(GONE);}else {mIvClose.setVisibility(VISIBLE);}}
/**
根据 downCollapsedAlphaPro,设置折叠内容透明度*/private void alphaCollapsedContentByPro(float downCollapsedAlphaPro) {setAlpha(mClCollapsedHeader,downCollapsedAlphaPro);setAlpha(mRvCollapsed,1 - downCollapsedAlphaPro);}
/**
根据 upAlphaGradientPro,设置 topBar 背景、返回 icon、拼团 icon 颜色渐变值,搜索框透明度*/private void alphaGradientByPro(float upAlphaGradientPro) {setAlpha(mTvSearch, upAlphaGradientPro);int iconColor = (int) iconArgbEvaluator.evaluate(upAlphaGradientPro,getContext().getResources().getColor(R.color.white),getContext().getResources().getColor(R.color.black));int topBarColor = (int) topBarArgbEvaluator.evaluate(upAlphaGradientPro,getContext().getResources().getColor(R.color.trans_white),getContext().getResources().getColor(R.color.white));mTopBar.setBackgroundColor(topBarColor);mIvBack.getDrawable().mutate().setTint(iconColor);mIvAssemble.getDrawable().mutate().setTint(iconColor);}
/**
根据 upAlphaScalePro,设置 logo、收藏 icon 缩放,搜索 icon、分享 icon 透明度*/private void alphaScaleByPro(float upAlphaScalePro) {float alpha = 1 - upAlphaScalePro;float scale = 1 - upAlphaScalePro;setAlpha(mIvSearch, alpha);setAlpha(mIvShare, alpha);setScaleAlpha(mIvLogo, scale, scale, alpha);setScaleAlpha(mVCollect, scale, scale, alpha);}
private float getDownContentAlphaPro() {return (downEndY - MathUtils.clamp(mLlContent.getTranslationY(), downContentAlphaY, downEndY)) / (downEndY - downContentAlphaY);}
private float getDownCollapsedAlphaPro() {return (downCollapsedAlphaY - MathUtils.clamp(mLlContent.getTranslationY(), contentTransY, downCollapsedAlphaY)) / (downCollapsedAlphaY -contentTransY);}
private float getDownShopBarTransPro() {return (downShopBarTransY - MathUtils.clamp(mLlContent.getTranslationY(), contentTransY, downShopBarTransY)) / (downShopBarTransY -contentTransY);}
private float getUpAlphaGradientPro() {return (upAlphaScaleY - MathUtils.clamp(mLlContent.getTranslationY(), upAlphaGradientY, upAlphaScaleY)) / (upAlphaScaleY-upAlphaGradientY);}
private float getUpAlphaScalePro() {return (contentTransY - MathUtils.clamp(mLlContent.getTranslationY(), upAlphaScaleY, contentTransY)) / (contentTransY-upAlphaScaleY);}
private float getUpCollapsedContentTransPro() {return (contentTransY - MathUtils.clamp(mLlContent.getTranslationY(), topBarHeight, contentTransY)) / (contentTransY-topBarHeight);}
private void setAlpha(View view, float alpha){view.setAlpha(alpha);}
private void setScale(View view ,float scaleY,float scaleX){view.setScaleY(scaleY);view.setScaleX(scaleX);}private void setScaleAlpha(View view ,float scaleY,float scaleX,float alpha){setAlpha(view,alpha);setScale(view,scaleY,scaleX);}
private void translation(View view, float translationY) {view.setTranslationY(translationY);}
public interface ProgressUpdateListener{
void onUpCollapsedContentTransProUpdate(float pro);
void onUpAlphaScaleProUpdate(float pro);
void onUpAlphaGradientProUpdate(float pro);
void onDownCollapsedAlphaProUpdate(float pro);
void onDownContentAlphaProUpdate(float pro);
void onDownShopBarTransProUpdate(float pro);}
onStopNestedScroll()
在下滑 Content 部分从初始状态转换到展开状态的过程中松手就会执行收起或展开的动画,这逻辑在 onStopNestedScroll()实现,但注意如果动画未执行完毕手指再落下滑动时,应该在 onNestedScrollAccepted()取消当前执行中的动画。
如果 Content 部分的 TransitionY 没有超过 downCollapsedAlphaY,执行收起 Content 部分动画效果,恢复到初始转态。
如果 Content 部分的 TransitionY 超过了 downCollapsedAlphaY,执行展开 Content 部分动画效果,转换到展开转态。 代码实现如下:
...public static final long ANIM_DURATION_FRACTION = 200L;private ValueAnimator restoreOrExpandAnimator;//收起或展开折叠内容时执行的动画
public ElemeNestedScrollLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {...restoreOrExpandAnimator = new ValueAnimator();restoreOrExpandAnimator.setInterpolator(new AccelerateInterpolator());restoreOrExpandAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {translation(mLlContent, (float) animation.getAnimatedValue());
//根据 downShopBarTransPro,设置购物内容内容位移 float downShopBarTransPro = getDownShopBarTransPro();transShopBarByPro(downShopBarTransPro);
//根据 downCollapsedAlphaPro,设置折叠内容透明度 float downCollapsedAlphaPro = getDownCollapsedAlphaPro();alphaCollapsedContentByPro(downCollapsedAlphaPro);
//根据 downContentAlphaPro,设置滑动内容、收起按钮的透明度 float downContentAlphaPro = getDownContentAlphaPro();alphaContentByPro(downContentAlphaPro);
if (mProgressUpdateListener!=null){mProgressUpdateListener.onDownCollapsedAlphaProUpdate(downCollapsedAlphaPro);mProgressUpdateListener.onDownContentAlphaProUpdate(downContentAlphaPro);mProgressUpdateListener.onDownShopBarTransProUpdate(downShopBarTransPro);}}});}
@Overridepublic void onNestedScrollAccepted(@NonNull View child, @NonNull View target, int axes, int type) {mParentHelper.onNestedScrollAccepted(child, target, axes, type);if (restoreOrExpandAnimator.isStarted()) {restoreOrExpandAnimator.cancel();}}
@Overridepublic void onStopNestedScroll(@NonNull View target, int type) {mParentHelper.onStopNestedScroll(target, type);//如果不是从初始状态转换到展开状态过程触发返回 if (mLlContent.getTranslationY() <= contentTransY) {return;}
//根据百分比计算动画执行的时长 float downCollapsedAlphaPro = getDownCollapsedAlphaPro();float downContentAlphaYPro = getDownContentAlphaPro();if (downCollapsedAlphaPro == 0) {expand((long) (downContentAlphaYPro * ANIM_DURATION_FRACTION));} else {restore((long) (downCollapsedAlphaPro * ANIM_DURATION_FRACTION));}}
public void restore(long dur){if (restoreOrExpandAnimator.isStarted()) {restoreOrExpandAnimator.cancel();}restoreOrExpandAnimator.setFloatValues(mLlContent.getTranslationY(), contentTransY);restoreOrExpandAnimator.setDuration(dur);restoreOrExpandAnimator.start();}
public void expand(long dur){if (restoreOrExpandAnimator.isStarted()) {restoreOrExpandAnimator.cancel();}restoreOrExpandAnimator.setFloatValues(mLlContent.getTranslationY(), downEndY);restoreOrExpandAnimator.setDuration(dur);restoreOrExpandAnimator.start();}
处理惯性滑动
NestedScrollingParent2 处理惯性滑动的方式主要有两种: 一、在 onNestedPreFling()或者 onNestedFling()返回值为 true 消费掉。 二、在 onNestedPreFling()和 onNestedFling()返回值都为 false 的前提下,在 onNestedPreScroll()或者 onNestedScroll()消费掉,这种方式可以和普通的滑动共用逻辑代码。
场景 1:快速往上滑动 Content 部分的可滑动 View 产生惯性滑动,这和前面 onNestedPreScroll()处理上滑的效果一模一样,因此可以复用逻辑,使用第二种方式处理。
场景 2:在折叠状态并 Content 部分的可滑动 View 没有滑动到顶部尽头时,快速往下滑动 Content 部分的可滑动 View 产生惯性滑动滑到顶部尽头就停止了,这和前面 onNestedPreScroll()处理下滑的效果类似,但多了个惯性滑动滑到顶部尽头就停止的条件判断,使用第二种方式处理。
场景 3:快速往下滑动 Content 部分的可滑动 View 转化展开状态产生惯性滑动,这和前面 onNestedPreScroll()处理下滑的效果类似,使用第二种方式处理,但注意在惯性滑动没有完全消费掉的时候,会不断触发 onNestedPreScroll()来消费直到惯性滑动完全消费掉,所以当滑动到展开状态的时候要停止 Content 部分的 View 滑动因为这时已经是展开状态了,不需 View 继续滑动触发 onNestedPreScroll(),注意 NestedScrollView 并没有暴露对外停止滑动的方法,只能反射获取它的 OverScroller 停止滑动。 下面是代码实现:
@Overridepublic void onNestedPreScroll(@NonNull View target, int dx, int dy, @NonNull int[] consumed, int type) {float contentTransY = mLlContent.getTranslationY() - dy;//处理上滑和场景 1if (dy > 0) {if (contentTransY >= topBarHeight) {translationByConsume(mLlContent, contentTransY, consumed, dy);} else {translationByConsume(mLlContent, topBarHeight, consumed, (mLlContent.getTranslationY() - topBarHeight));}}
if (dy < 0 && !target.canScrollVertically(-1)) {//下滑时处理 Fling,完全折叠时,下滑 Recycler(或 NestedScrollView) Fling 滚动到列表顶部(或视图顶部)停止 Fling,对应场景 2if (type == ViewCompat.TYPE_NON_TOUCH&&mLlContent.getTranslationY() == topBarHeight) {stopViewScroll(target);return;}
//处理下滑 if (contentTransY >= topBarHeight && contentTransY <= downEndY) {translationByConsume(mLlContent, contentTransY, consumed, dy);} else {translationByConsume(mLlContent, downEndY, consumed, downEndY - mLlContent.getTranslationY());//对应场景 3if (target instanceof NestedScrollView) {stopViewScroll(target);}}}...}
/**
停止 View 的滑动*/private void stopViewScroll(View target){if (target instanceof RecyclerView) {((RecyclerView) target).stopScroll();}if (target instanceof NestedScrollView) {try {Class<? extends NestedScrollView> clazz = ((NestedScrollView) target).getClass();Field mScroller = clazz.getDeclaredField("mScroller");mScroller.setAccessible(true);OverScroller overScroller = (OverScroller) mScroller.get(target);overScroller.abortAnimation();} catch (NoSuchFieldException | IllegalAccessException e) {e.printStackTrace();}}}
场景 4:快速往下滑动 Content 部分的可滑动 View,从非折叠状态转化展开状态产生惯性滑动,因为有回弹效果,所以逻辑处理和 onNestedPreScroll()不一样,使用第一种方式处理。
...private ValueAnimator reboundedAnim;//回弹动画
public ElemeNestedScrollLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {...reboundedAnim = new ValueAnimator();reboundedAnim.setInterpolator(new DecelerateInterpolator());reboundedAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {translation(mLlContent, (float) animation.getAnimatedValue());
//根据 upAlphaScalePro,设置 logo、收藏 icon 缩放,搜索 icon、分享 icon 透明度 float upAlphaScalePro = getUpAlphaScalePro();alphaScaleByPro(upAlphaScalePro);
//根据 upAlphaGradientPro,设置 topBar 背景、返回 icon、拼团 icon 颜色渐变值,搜索框透明度 float upAlphaGradientPro = getUpAlphaGradientPro();alphaGradientByPro(upAlphaGradientPro);
//根据 downCollapsedAlphaPro,设置折叠内容透明度 float downCollapsedAlphaPro = getDownCollapsedAlphaPro();alphaCollapsedContentByPro(downCollapsedAlphaPro);
//根据 downShopBarTransPro,设置购物内容内容位移 float downShopBarTransPro = getDownShopBarTransPro();transShopBarByPro(downShopBarTransPro);
//根据 upCollapsedContentTransPro,设置折叠内容位移 float upCollapsedContentTransPro = getUpCollapsedContentTransPro();transCollapsedContentByPro(upCollapsedContentTransPro);
if (mProgressUpdateListener!=null){mProgressUpdateListener.onUpAlphaScaleProUpdate(upAlphaScalePro);mProgressUpdateListener.onUpAlphaGradientProUpdate(upAlphaGradientPro);mProgressUpdateListener.onDownCollapsedAlphaProUpdate(downCollapsedAlphaPro);mProgressUpdateListener.onDownShopBarTransProUpdate(downShopBarTransPro);mProgressUpdateListener.onUpCollapsedContentTransProUpdate(upCollapsedContentTransPro);}}});reboundedAnim.setDuration(ANIM_DURATION_FRACTION);}
@Overridepublic boolean onNestedFling(@NonNull View target, float velocityX, float velocityY, boolean consumed) {return false;}
@Overridepublic boolean onNestedPreFling(@NonNull View target, float velocityX, float velocityY) {if (velocityY<0){//往下滑动的惯性滑动 float translationY = mLlContent.getTranslationY();float dy = translationY - velocityY;if (translationY >topBarHeight && translationY<= downFlingCutOffY) {//非折叠状态//根据 dy 设置动画结束值,只有 dy>contentTransY 才会有回弹,downFlingCutOffY 是回弹的最大值 if (dy<=contentTransY){reboundedAnim.setFloatValues(translationY,dy);}else if (dy>contentTransY&&dy<downFlingCutOffY){reboundedAnim.setFloatValues(translationY,dy,contentTransY);}else {reboundedAnim.setFloatValues(translationY,downFlingCutOffY,contentTransY);}reboundedAnim.start();return true;}}return false;}
释放资源
在 View 从 Window 上移除时候,执行要停止动画、释放监听者的操作。
@Overrideprotected void onDetachedFromWindow() {super.onDetachedFromWindow();if (restoreOrExpandAnimator.isStarted()) {restoreOrExpandAnimator.cancel();restoreOrExpandAnimator.removeAllUpdateListeners();restoreOrExpandAnimator = null;}
if(reboundedAnim.isStarted()){reboundedAnim.cancel();reboundedAnim.removeAllUpdateListeners();reboundedAnim = null;}
if (mProgressUpdateListener!=null){mProgressUpdateListener=null;}}
商家点餐页的嵌套滑动实现
效果分析
商家点餐页布局主要有上图五部分组成,逻辑上的层级如图所示有 3 层。
滑动 Content 部分时利用 View 的 TransitionY 属性改变位置来消耗滑动值,根据 Content 部分的 TransitionY 设定各种范围从而计算百分比来执行位移、Alpha 效果。下面来说明上图中变量的意义:
contentTransY;//滑动内容初始化 TransYiconTransY;//分享、关闭 icon 初始化 transYupEndIconTransY;//分享、关闭 icon 上滑最终 transYdownFlingCutOffY;///从展开状态下滑产生 fling 时回弹到初始状态的最大值
ElemeFoodNestedScrollLayout
商家点餐页的嵌套滑的自定义 View 继承 FrameLayout、实现 NestedScrollingParent2 接口,命名为 ElemeFoodNestedScrollLayout。
布局
下面是布局要点,侧重于控件的尺寸和位置,完整布局请参考:[ElemeFoodNestedScrollLayout 布局](
)
绑定需要做效果的 View、引入 Dimens、设置 Content 部分的初始化 TransitionY
从上面图片能够分析出:关闭状态时,Content 部分的 TransY 为满屏高度
public class ElemeFoodNestedScrollLayout extends FrameLayout implements NestedScrollingParent2 {...//shopBar 部分 private View mShopBar;
//content 部分 private View mNestedScrollView;private View mTvComm;private View mTvGoodCommRate;private View mTvCommDetail;private View mTvCommCount;private View mVLine;private View mTvFoodDetail;
//expand 部分 private View mIvExpand;
//icon 部分 private View mIvShare;private View mIvClose;
//mask 部分 private View mVMask;
private float shopBarHeight;//shopBar 部分高度 private float ivExpandHegiht;//ivExpand 部分高度 private float statusBarHeight;//状态栏高度 private float iconTransY;//分享、关闭 icon 初始化 transYprivate float contentTransY;//滑动内容初始化 TransYprivate float downFlingCutOffY;//下滑时 fling 上部分回弹临界值 private float upEndIconTransY;//分享、关闭 icon 上滑最终 transY
@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);shopBarHeight = getResources().getDimension(R.dimen.shop_bar_height);ivExpandHegiht = getResources().getDimension(R.dimen.iv_food_expand);contentTransY = getResources().getDimension(R.dimen.food_content_trans_y);iconTransY = getResources().getDimension(R.dimen.iv_food_icon_trans_y);statusBarHeight = getResources().getDimensionPixelSize(getResources().getIdentifier("status_bar_height", "dimen", "android"));downFlingCutOffY = contentTransY + dp2px(92);upEndIconTransY = statusBarHeight + dp2px(8);//因为开始就是关闭状态,设置 Content 部分的 TransY 为满屏高度 mNestedScrollView.setTranslationY(getMeasuredHeight());}
@Overrideprotected void onFinishInflate() {super.onFinishInflate();mNestedScrollView = findViewById(R.id.ns);mShopBar = findViewById(R.id.cl_food_shop_bar);
mTvComm = findViewById(R.id.t_comm);mTvGoodCommRate = findViewById(R.id.t_good_comm_rate);mTvCommDetail = findViewById(R.id.t_comm_detail);mTvFoodDetail = findViewById(R.id.t_food_detail);
mTvCommCount = findViewById(R.id.t_comm_count);mVLine = findViewById(R.id.view_line);
mIvExpand = findViewById(R.id.iv_food_expand);mIvShare = findViewById(R.id.iv_small_share);mIvClose = findViewById(R.id.iv_small_close);mVMask = findViewById(R.id.v_mask);}}
实现 NestedScrollingParent2 接口
onStartNestedScroll()
ElemeFoodNestedScrollLayout 只处理 Content 部分里可滑动 View 的垂直方向的滑动。
@Overridepublic boolean onStartNestedScroll(@NonNull View child, @NonNull View target, int axes, int type) {//处理 Content 部分里可滑动 View 的垂直方向的滑动 return axes == ViewCompat.SCROLL_AXIS_VERTICAL && target.getId() == R.id.ns;}
onNestedPreScroll()
接下来就是处理滑动,上面效果分析提过:Content 部分的上滑范围=[0,contentTransY]、 下滑范围=[contentTransY,即满屏高度]即滑动范围为[0,即满屏高度],ElemeFoodNestedScrollLayout 要控制 Content 部分的 TransitionY 值要在范围内,之前的商家页已经说过了具体思路,这里不再赘述。
...private ProgressUpdateListener mProgressUpdateListener;
@Overridepublic void onNestedPreScroll(@NonNull View target, int dx, int dy, @NonNull int[] consumed, int type) {float contentTransY = target.getTranslationY() - dy;//处理上滑 if (dy > 0) {if (contentTransY >= 0) {translationByConsume(target, contentTransY, consumed, dy);} else {translationByConsume(target, 0, consumed, (target.getTranslationY() - 0));}}
//处理下滑 if (dy < 0 && !target.canScrollVertically(-1)) {if (contentTransY >= 0 && contentTransY < getMeasuredHeight()) {translationByConsume(target, contentTransY, consumed, dy);} else {translationByConsume(target, getMeasuredHeight(), consumed, getMeasuredHeight() - target.getTranslationY());}}
alphaTransView(contentTransY);
if (mProgressUpdateListener!=null){mProgressUpdateListener.onDownConetntCloseProUpdate(getDownConetntClosePro());}}
private void alphaTransView(float transY) {float upCollapseTransPro = getUpExpandTransPro();//位移购物内容 float shopBarTransY = (1 - upCollapseTransPro) * shopBarHeight;translation(mShopBar, shopBarTransY);
//设置商品信息 View 的透明度变化 setAlpha(mTvComm, upCollapseTransPro);setAlpha(mTvGoodCommRate, upCollapseTransPro);setAlpha(mTvCommDetail, upCollapseTransPro);setAlpha(mTvFoodDetail, upCollapseTransPro);setAlpha(mTvCommCount, 1 - upCollapseTransPro);setAlpha(mVLine, 1 - upCollapseTransPro);
//位移 share close 两个 Icon,设置展开 icon 透明度 if (transY <= contentTransY) {float ivExpandUpTransY = upCollapseTransPro * -contentTransY;translation(mIvExpand, ivExpandUpTransY);setAlpha(mIvExpand, 1 - upCollapseTransPro);
float iconTransY = upEndIconTransY + (1 - upCollapseTransPro) * (this.iconTransY - upEndIconTransY);translation(mIvShare, iconTransY);translation(mIvClose, iconTransY);
} else if (transY > contentTransY && transY <= getMeasuredHeight()) {float ivExpandDowndTransY = (1 - getDownIvExpnadPro()) * ivExpandHegiht;translation(mIvExpand, ivExpandDowndTransY);
float iconTransY = transY + dp2px(16);translation(mIvShare, iconTransY);translation(mIvClose, iconTransY);}}
private float getDownConetntClosePro() {return (mNestedScrollView.getTranslationY() - contentTransY) / (getMeasuredHeight() - contentTransY);}
private float getDownIvExpnadPro() {return ((contentTransY+ivExpandHegiht)-MathUtils.clamp(mNestedScrollView.getTranslationY(), contentTransY, contentTransY+ivExpandHegiht)) / ivExpandHegiht;}
private float getUpExpandTransPro() {return (contentTransY - MathUtils.clamp(mNestedScrollView.getTranslationY(), 0, contentTransY)) / contentTransY;}
public interface ProgressUpdateListener{void onDownConetntCloseProUpdate(float pro);}
onStopNestedScroll()
在从初始状态到展开状态的上滑过程中松手,若上滑百分比小于等于 50%则执行恢复到初始状态的动画,否则执行转化到展开状态的动画;同理从初始状态到关闭状态下滑过程中松手,若下滑百分比小于等于 50%则执行恢复到初始状态的动画,否则执行转化到关闭状态的动画;
...private ValueAnimator restoreOrExpandOrCloseAnimator;//收起或展开折叠内容时执行的动画 private NestedScrollingParentHelper mParentHelper;
public ElemeFoodNestedScrollLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
评论