最近项目需求,需要实现标题的悬浮和渐隐渐变….于是故事开始了
1,代码接受后,业务逻辑复杂,最好的完成就是基于现在XML和activity 做最少的改动实现,所以网上的demo,以及5.0后的Coordinglayout 我就放弃了,这样的改动及时能实现,也要改太多东西,
2,于是还是自己实现下吧,下图是预览,GIF 录制不了..哎
上滑动的时候titlebar 渐出
下滑动的时候titlebar 渐入
这里是初始进入的样子
这里是慢慢画出的样子
这里是全部画出的样子
最后是渐入的样子,
==========================================================
OK,基于不是自己实现的代码,最加单的实现就是改变最外层布局为FrameLayout + include进来titlebarbar
初始化的时候把titlebar隐藏,注意这里的隐藏不是GONE掉,而是把titlebar设置到屏幕外面,很简单,大家都会,这里就不浪费时间描述了
发现短短的三句代码不到 我们的功能已经实现了一半了对不对
首先描述下实现思路
- 根据现在的代码状态,基于scrollview的滑动,改变titlebar的位置
- 由于scrollview给我们提供了一个私有的onScrollChanged方法,注意
如果你用AS自己设置一个onScrollChanged方法,会提示你,低版本的API不能用而且会crash,所以我们自己重写scrollview 提供接口给外界activity用 - 然后我们重写scrollview的ontouch方法,模拟出scrollview的滑动状态
public interface OnScrollListener {
int SCROLL_STATE_IDLE = 0;
int SCROLL_STATE_TOUCH_SCROLL = 1;
int SCROLL_STATE_FLING = 2;
void onBottomArrived();
void onScrollStateChanged(ATListenedScrollView view, int scrollState);
void onScrollChanged(int l, int t, int oldl, int oldt);
}
这里是ontouch方法
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
inTouch = true;
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
inTouch = false;
lastT = getScrollY();
checkStateHandler.removeMessages(CHECK_STATE);
checkStateHandler.sendEmptyMessageDelayed(CHECK_STATE, 5);
break;
default:
break;
}
return super.onTouchEvent(ev);
}
完整的scrollview 代码如下,直接用就可以获取 坐标的变化和scrollview的滑动状态
public class ATListenedScrollView extends ScrollView {
private static final int CHECK_STATE = 0;
private OnScrollListener onScrollListener;
private boolean inTouch = false;
private int lastT = 0;
public ATListenedScrollView(Context context) {
super(context);
}
public ATListenedScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ATListenedScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
inTouch = true;
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
inTouch = false;
lastT = getScrollY();
checkStateHandler.removeMessages(CHECK_STATE);
checkStateHandler.sendEmptyMessageDelayed(CHECK_STATE, 5);
break;
default:
break;
}
return super.onTouchEvent(ev);
}
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
if (onScrollListener == null) {
return;
}
if (inTouch) {
if (t != oldt) {
onScrollListener.onScrollStateChanged(this,
OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);
}
} else {
if (t != oldt) {
onScrollListener.onScrollStateChanged(this,
OnScrollListener.SCROLL_STATE_FLING);
lastT = t;
checkStateHandler.removeMessages(CHECK_STATE);
checkStateHandler.sendEmptyMessageDelayed(CHECK_STATE, 5);
}
}
onScrollListener.onScrollChanged(l, t, oldl, oldt);
}
public void setOnScrollListener(OnScrollListener onScrollListener) {
this.onScrollListener = onScrollListener;
}
public interface OnScrollListener {
int SCROLL_STATE_IDLE = 0;
int SCROLL_STATE_TOUCH_SCROLL = 1;
int SCROLL_STATE_FLING = 2;
void onBottomArrived();
void onScrollStateChanged(ATListenedScrollView view, int scrollState);
void onScrollChanged(int l, int t, int oldl, int oldt);
}
private WeakRefHandler checkStateHandler = new WeakRefHandler(this);
/**
* WeakReference handler
*/
static class WeakRefHandler extends Handler {
WeakReference<ATListenedScrollView> atListenedScrollViewWeakReference;
WeakRefHandler(ATListenedScrollView atListenedScrollView) {
atListenedScrollViewWeakReference = new WeakReference<ATListenedScrollView>(atListenedScrollView);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
ATListenedScrollView atListenedScrollView = atListenedScrollViewWeakReference.get();
if (atListenedScrollView.lastT == atListenedScrollView.getScrollY()) {
atListenedScrollView.onScrollListener.onScrollStateChanged(atListenedScrollView, OnScrollListener.SCROLL_STATE_IDLE);
if (atListenedScrollView.getScrollY() + atListenedScrollView.getHeight() >= atListenedScrollView.computeVerticalScrollRange()) {
atListenedScrollView.onScrollListener.onBottomArrived();
}
}
}
}
}
现在我们的scrollview都提供好了,工作又完成了2/3
剩下的代码就是activity里面的滑动监听,改变titlebar的位置,顺道加一个渐变动画soeasy
Duang 来了
private void initTitleScroll() {
scrollView.setOnScrollListener(new ATListenedScrollView.OnScrollListener() {
@Override
public void onBottomArrived() {
//如果scrollview滚到底部并且是静止状态,显示titlebar
if (isIDLE) {
hotelTitelViewLayoutParams.topMargin = 0;
hotelTitelView.setLayoutParams(hotelTitelViewLayoutParams);
hotelTitelView.setAlpha(1.F);
}
}
@Override
public void onScrollStateChanged(ATListenedScrollView view, int scrollState) {
// 给我们定义的是否是滚动和滑翔的状态变量赋值
isFilling = scrollState == SCROLL_STATE_FLING;
isIDLE = scrollState == SCROLL_STATE_IDLE;
}
@Override
public void onScrollChanged(int l, int y, int oldl, int oldY) {
// 滑动监听的回调,如果是静止状态,并且y是0,这时候隐藏titlebar
if (isIDLE && 0 == y) {
hotelTitelViewLayoutParams.topMargin = -hotelTitleHeight;
hotelTitelView.setLayoutParams(hotelTitelViewLayoutParams);
hotelTitelView.setAlpha(0);
return;
}
// 如果是滑动状态,并且titlebar的topmarin是0,显示titlebar
if (isFilling && hotelTitelViewLayoutParams.topMargin == 0) {
hotelTitelView.setAlpha(1.F);
return;
} else if (isFilling && hotelTitelViewLayoutParams.topMargin < 0) {
hotelTitelViewLayoutParams.topMargin = -hotelTitleHeight;
hotelTitelView.setAlpha(0);
hotelTitelView.setLayoutParams(hotelTitelViewLayoutParams);
return;
}
//特殊情况判断完毕,这是在滑动的时候dy是偏移量,根据便宜量计算出titlebar的alpha值,和改变titlebar距离顶部的位置,ok到此全部实现,
int dY = oldY - y;
hotelTitelView.setAlpha(ATUtils.getAlphaScale(hotelTitleHeight - Math.abs(hotelTitelViewLayoutParams.topMargin), hotelTitleHeight));
if (hotelTitelViewLayoutParams.topMargin > 0) {
hotelTitelViewLayoutParams.topMargin = 0;
hotelTitelView.setLayoutParams(hotelTitelViewLayoutParams);
return;
}
if (dY < 0 && hotelTitelViewLayoutParams.topMargin == 0) {
// up slide edge
return;
} else if (dY > 0 && hotelTitelViewLayoutParams.topMargin == -hotelTitleHeight) {
// down slide edge
return;
}
if (hotelTitelViewLayoutParams.topMargin >= -hotelTitleHeight && hotelTitelViewLayoutParams.topMargin <= 0) {
hotelTitelViewLayoutParams.topMargin = hotelTitelViewLayoutParams.topMargin - dY;
} else if (hotelTitelViewLayoutParams.topMargin > 0) {
hotelTitelViewLayoutParams.topMargin = 0;
} else if (hotelTitelViewLayoutParams.topMargin < -hotelTitleHeight) {
hotelTitelViewLayoutParams.topMargin = -hotelTitleHeight;
}
hotelTitelView.setLayoutParams(hotelTitelViewLayoutParams);
}
});
}
关键代码和自定义的scrollview全部在这里,因为直接写项目里面的所以没有demo可上传,如果有类似效果直接copy上面代码即可
当有需求改变,但前期代码不是你自己的情况下,尽可能的做的改动最少来实现,尤其是业务逻辑部分,根本不用关心,只需要扩展即可,如果找github或者其他demo 如果是基于Coordinglayout等等,需要改变的就不是这么一点了