自定义悬浮头部标题栏HeaderFloatTitle(支持背景/透明度/位移的变化)
来源:互联网 发布:关节机器人编程 编辑:程序博客网 时间:2024/06/10 00:19
最近两个月一直在做视频播放以及代码重构方面的事情,收获挺多,但是一直没有时间来总结一下。
效果图参考:这里写链接内容
此文就是对顶部标题栏的一个简单封装。
关键的几个方法:
1.设置需要监听的View的Y轴变化区间。(比如想监听一个图片从Y坐标100移动到-100过程中,顶部标题栏有颜色透明度变化,则可以在这个方法中设置setRange(100, -100)
/** * 设置变化区间Y值坐标 * @param mRangeMax * @param mRangeMin * @return */ public HeaderFloatTitle setRange(float mRangeMax, float mRangeMin){ this.mRangeMax = mRangeMax; this.mRangeMin = mRangeMin; return this; }
2.设置起始终点颜色
/** * 设置背景色变化范围 * @param startColor 起始颜色 * @param endColor 最终颜色 * @return */ public HeaderFloatTitle setBackgroundRangeColor(int startColor, int endColor){ mStartColor = startColor; mEndColor = endColor; return this; }
3.添加子View,这些子View会随着监听View的位置变化而进行透明度的变幻
/** * 添加Alpha由1-0的View * @param mStartView * @return */ public HeaderFloatTitle addStartView(View mStartView){ this.mStartView = mStartView; addView(mStartView, 0); return this; } /** * 添加Alpha由0-1的View * @param mEndView * @return */ public HeaderFloatTitle addEndView(View mEndView){ this.mEndView = mEndView; addView(mEndView, 1); return this; }
4.添加分割线,有时候UI可以想在标题栏底部加一条很细的线,可以调用此方法。
/** * 添加底部分割线 * @return */ public HeaderFloatTitle addBottomSepLine(){ mBottomSepLine = new View(getContext()); mBottomSepLine.setBackgroundColor(Color.parseColor("#dddddd")); LayoutParams params = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, Local.dip2px(0.5f)); params.gravity = Gravity.BOTTOM; mBottomSepLine.setLayoutParams(params); addView(mBottomSepLine); return this; }
5.下面两个方法是最重要的两个方法。
onDependentViewChanged用来设置监听View移动过程中的Y坐标,调用这个方法,可以实现标题栏的透明度/背景色/位移变化。
onDependentViewScrollChanged用来监听滚动过程中,控制头部悬浮栏显示隐藏。
/** * 依赖的View位置发送变化时监听 * @param dependY 顶部y坐标 */ public void onDependentViewChanged(float dependY){ //纪录当前位置 mDependY = dependY; float percent = getPercent(dependY); LogUtils.i_debug(TAG, "percent:" + percent + " nowY:" + dependY + " height:" + mHeight); loadAlpha(percent); loadBackgroundColor(percent); loadOffset(percent); showLine(percent); } /** * 依赖的View滚动时头部显示隐藏控制 * @param dy */ public void onDependentViewScrollChanged(float dy){ loadScrollAnimate(dy); }
使用:
//在RecyclerView中,比如想监听头图ldv_head从完全显示到完全隐藏过程中titleBar有颜色透明度位移变化。//当列表滚动时,控制titleBar的显示隐藏,则设置如下:myRecyclerView.setScrollLinsteners(new RefreshRecyclerView.ScrollLinsteners() { @Override public void onScrolled(int firstVisibleItem, int dx, int dy) { if (mLayoutManager != null && mLayoutManager.findViewByPosition(0) != null) { View view = mLayoutManager.findViewByPosition(0); int ivH = view.findViewById(R.id.ldv_head).getHeight(); int paddingBottom = view.findViewById(R.id.ldv_head).getPaddingBottom(); int top = view.getTop(); mHeaderFloatTitle.setHasOffset(false).setRange(0, -ivH + paddingBottom); mHeaderFloatTitle.onDependentViewChanged(top); }else { mHeaderFloatTitle.onDependentViewScrollChanged(dy); }}
在CoordinatorLayout中,也可以通过自定义Behavior来使用。比如:
headerFloatTitle.addStartView(rl_head_w); headerFloatTitle.addEndView(rl_head_b); headerFloatTitle.addBottomSepLine(); headerFloatTitle.setHasScrollAnimate(false).setHasOffset(false).setHasAlpha(true); float image_hight = Local.getWidthPx() / 1.33f; headerFloatTitle.setRange(image_hight , 0);
……….
public class BrandDetailTitleBehavior extends CoordinatorLayout.Behavior<HeaderFloatTitle> { float height = Local.getWidthPx() / 1.33f; public BrandDetailTitleBehavior() { } public BrandDetailTitleBehavior(Context context, AttributeSet attrs) { super(context, attrs); } @Override public boolean layoutDependsOn(CoordinatorLayout parent, HeaderFloatTitle child, View dependency) {// child.setRange(200, 0); return dependency instanceof AppBarLayout; } @Override public boolean onDependentViewChanged(CoordinatorLayout parent, HeaderFloatTitle child, View dependency) { child.onDependentViewChanged(dependency.getTop() + height); return super.onDependentViewChanged(parent, child, dependency); }}
全部代码:
import android.animation.Animator;import android.animation.AnimatorListenerAdapter;import android.animation.ArgbEvaluator;import android.content.Context;import android.graphics.Color;import android.util.AttributeSet;import android.view.Gravity;import android.view.View;import android.view.ViewGroup;import android.widget.FrameLayout;import com.loookapp.loook.Utils.Local;import com.loookapp.loook.Utils.LogUtils;/** * Created by lk on 16/11/11. * * 头部悬浮控件 * */public class HeaderFloatTitle extends FrameLayout { private static final String TAG = "HeaderFloatTitle"; private final ArgbEvaluator mArgbEvaluator;//颜色估值器 private float mRangeMax;//最大Y偏移量 private float mRangeMin;//最小Y偏移量 private float mDependY;//依赖控件Y的偏移量 private int mStartColor;//开始颜色 private int mEndColor;//最终颜色 private View mStartView, mEndView; private boolean hasAlpha;//是否有alpha变化 private boolean hasOffset;//是否有偏移变化 private boolean hasScrollAnimate;//是否随着滚动而显示隐藏 private boolean hasColor;//是否有颜色变化 private boolean deleteTitleBarHeight;//计算range是否需要减去titlebar高度 private boolean animateLoading;//动画进行中 private float mHeight = -1;//自身高度 private long animateDuration = 160; //动画执行时间 private View mBottomSepLine;//分割线 public HeaderFloatTitle(Context context) { this(context, null); } public HeaderFloatTitle(Context context, AttributeSet attrs) { this(context, attrs, 0); } public HeaderFloatTitle(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mArgbEvaluator = new ArgbEvaluator(); mStartColor = 0x00FFFFFF; mEndColor = 0xFFFFFFFF; hasAlpha = true; hasOffset = true; hasScrollAnimate = true; hasColor = true; deleteTitleBarHeight = true; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); mHeight = getMeasuredHeight(); } /** * 依赖的View位置发送变化时监听 * @param dependY 顶部y坐标 */ public void onDependentViewChanged(float dependY){ //纪录当前位置 mDependY = dependY; float percent = getPercent(dependY); LogUtils.i_debug(TAG, "percent:" + percent + " nowY:" + dependY + " height:" + mHeight); loadAlpha(percent); loadBackgroundColor(percent); loadOffset(percent); showLine(percent); } /** * 依赖的View滚动时头部显示隐藏控制 * @param dy */ public void onDependentViewScrollChanged(float dy){ loadScrollAnimate(dy); } /** * 判断是否显示线 * @param percent */ private void showLine(float percent) { if (mBottomSepLine != null) { if (percent < 1 && hasColor) { mBottomSepLine.setVisibility(GONE); } else { mBottomSepLine.setVisibility(VISIBLE); } } } private void loadOffset(float percent) { if(hasOffset){ float outOfRangeDy; if(percent == 1) { outOfRangeDy = mDependY - mRangeMin - mHeight;//超出的dy }else{ outOfRangeDy = 0; } LogUtils.i_debug(TAG, "outOfRangeDy:" + outOfRangeDy + " height:" + mHeight); animate().translationY(outOfRangeDy).setDuration(0).start(); } } private void loadScrollAnimate(float dy) { LogUtils.i_debug(TAG, "mDependY:" + mDependY + " mRangeMin:" + mRangeMin); LogUtils.i_debug(TAG, "hasScrollAnimate:" + hasScrollAnimate + " dy" + dy); if(hasScrollAnimate && !animateLoading){// mDependY < mRangeMin && //位置移动 if (dy < -10 && getTranslationY() != 0) { animate().translationY(0).setListener(new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { super.onAnimationStart(animation); animateLoading = true; } @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); animateLoading = false; } }).setDuration(animateDuration).start(); } else if (dy > 10 && getTranslationY() != -mHeight) { animate().translationY(-mHeight).setListener(new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { super.onAnimationStart(animation); animateLoading = true; } @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); animateLoading = false; } }).setDuration(animateDuration).start(); } } } private void loadBackgroundColor(float percent) { if(hasColor){ int bgColor = getBackgroundColor(percent); setBackgroundColor(bgColor); }else { setBackgroundColor(mEndColor); } } private int getBackgroundColor(float percent){ return (int) mArgbEvaluator.evaluate(percent, mStartColor, mEndColor); } private void loadAlpha(float percent) { if(hasAlpha) { if(mStartView != null) { mStartView.setAlpha(1 - percent);//1~0 start } if(mEndView != null) { mEndView.setAlpha(percent); //0~1 end } } } /** * 获取当前百分比 * @param dependY * @return */ private float getPercent(float dependY){ if(dependY > mRangeMax)return 0; if(dependY < mRangeMin + mHeight)return 1; float range = getRange(); float now = dependY - mRangeMin; if (deleteTitleBarHeight) { now -= mHeight; } //max >= dy >= min; float percent = (range != 0) ? (1 - Math.abs(now / range)) : 0;//0~1 percent = percent > 1 ? 1 : (percent < 0 ? 0 : percent); return percent; } /** * 获取颜色或alpha变化的dy范围 * @return */ private float getRange(){ float range = mRangeMax - mRangeMin; if(deleteTitleBarHeight && range > mHeight){ range -= mHeight; }else { hasOffset = false; } return range; } /** * 添加Alpha由1-0的View * @param mStartView * @return */ public HeaderFloatTitle addStartView(View mStartView){ this.mStartView = mStartView; addView(mStartView, 0); return this; } /** * 添加Alpha由0-1的View * @param mEndView * @return */ public HeaderFloatTitle addEndView(View mEndView){ this.mEndView = mEndView; addView(mEndView, 1); return this; } /** * 添加底部分割线 * @return */ public HeaderFloatTitle addBottomSepLine(){ mBottomSepLine = new View(getContext()); mBottomSepLine.setBackgroundColor(Color.parseColor("#dddddd")); LayoutParams params = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, Local.dip2px(0.5f)); params.gravity = Gravity.BOTTOM; mBottomSepLine.setLayoutParams(params); addView(mBottomSepLine); return this; } /** * 子View是否进行alpha变化 * @param hasAlpha * @return */ public HeaderFloatTitle setHasAlpha(boolean hasAlpha) { this.hasAlpha = hasAlpha; return this; } /** * 是否随着位置移动改变floatTitle的位置 * @param hasOffset * @return */ public HeaderFloatTitle setHasOffset(boolean hasOffset) { this.hasOffset = hasOffset; return this; } /** * 滚动时是否加载显示隐藏动画 * @param hasScrollAnimate * @return */ public HeaderFloatTitle setHasScrollAnimate(boolean hasScrollAnimate) { this.hasScrollAnimate = hasScrollAnimate; return this; } /** * 设置是否有颜色变化 * @param hasColor * @return */ public HeaderFloatTitle setHasColor(boolean hasColor) { this.hasColor = hasColor; return this; } /** * 是否去除titleBar高度 * @param deleteTitleBarHeight * @return */ public HeaderFloatTitle setDeleteTitleBarHeight(boolean deleteTitleBarHeight){ this.deleteTitleBarHeight = deleteTitleBarHeight; return this; } /** * 设置变化区间Y值坐标 * @param mRangeMax * @param mRangeMin * @return */ public HeaderFloatTitle setRange(float mRangeMax, float mRangeMin){ this.mRangeMax = mRangeMax; this.mRangeMin = mRangeMin; return this; } /** * 设置背景色变化范围 * @param startColor 起始颜色 * @param endColor 最终颜色 * @return */ public HeaderFloatTitle setBackgroundRangeColor(int startColor, int endColor){ mStartColor = startColor; mEndColor = endColor; return this; }}
0 0
- 自定义悬浮头部标题栏HeaderFloatTitle(支持背景/透明度/位移的变化)
- 实现自定义actionbar背景透明度的变化
- 实现头部透明度变化
- 标题栏仿QQ空间(透明度变化)效果
- Java 自定义窗体(标题栏、窗体背景)
- 自定义dialog设置背景透明度
- Android标题栏和状态栏显示与否的设置&&& Button或者ImageButton的背景透明度
- 自定义进度条动画(背景跟随变化)
- Android自定义头部悬浮,快速索引ListView
- Android改变标题栏的透明度
- 去掉Activity的头部标题栏
- div 背景透明度如何设置一个div的背景透明度
- 设置Dialog的背景透明度
- UITEXTVIEW的背景透明度问题
- 设置背景的颜色透明度
- Activity背景的透明度效果
- Android关于小米相册悬浮标题栏、冻结标题栏的实现方式(嵌套型RecycleView)
- ListView实现标题栏的透明度的问题
- File操作-InputStreamReader/Writer
- 火狐安装后双击没反应的解决办法
- 423. Reconstruct Original Digits from English
- 关于log4j root logger 标签 以及additivity 属性
- CTP JAVA_API(JCTP)编译(利用Swig封装C++动态库)linux版64位
- 自定义悬浮头部标题栏HeaderFloatTitle(支持背景/透明度/位移的变化)
- Centos7 安装 tomcat9 后访问慢且 shutdown.sh 报错,提示 8005 端口未启用
- Android - 判断Android设备是手机还是平板?
- UE4学习笔记(20161116) 正则表达式检测输入信息
- java图片上传
- Interesting_C
- STL-forward_list(单向链表)
- mongodb基本概念
- poi处理excel的string index out of range -3