ScrollView 在我们平时开发中经常用到,它是将一些不确定高度的子组件装进确定高度的容器,允许显示比实际多的内容。有时候我们需要监听ScroView的滑动情况,比如滑动了多少距离,是否滑到布局的顶部或者底部等等。下面我们就来看一看通过监听纵向滑动距离,来实现标题栏渐变效果。
效果图1. 实现方法
① 方法一:
通过 View.OnScrollChangeListener 监听组件滑动变化
scrollView.setOnScrollChangeListener(new OnScrollChangeListener() {
@Override
public void onScrollChange(View view, int l, int t, int oldl, int oldt) {
int mHeight = mTvTitle.getHeight();//获取标题栏高度
if (t <= 0) {//未滑动
mTvTitle.setBackgroundColor(Color.argb((int) 0, 31, 100, 240));
} else if (t > 0 && t <= mHeight) { //滑动过程中 并且在mHeight之内
float scale = (float) t / mHeight;
float alpha = (255 * scale);
mTvTitle.setTextColor(Color.argb((int) alpha, 255, 255, 255));
mTvTitle.setBackgroundColor(Color.argb((int) alpha, 31, 100, 240));
} else {//超过mHeight
mTvTitle.setBackgroundColor(Color.argb((int) 255, 31, 100, 240));
}
}
});
注意:该方法是API 23才出来的,没有做向下兼容,所以在低版本使用会报错。
② 方法二:
使用 ViewTreeObserver.OnScrollChangedListener 监听视图中的组件滑动变化
scrollView.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
@Override
public void onScrollChanged() {
int y = scrollView.getScrollY();//获取纵向滑动距离
int mHeight = mTvTitle.getHeight();//获取标题栏高度
if (y <= 0) {//未滑动
mTvTitle.setBackgroundColor(Color.argb((int) 0, 31, 100, 240));
} else if (y > 0 && y <= mHeight) { //滑动过程中 并且在mHeight之内
float scale = (float) y / mHeight;
float alpha = (255 * scale);
mTvTitle.setTextColor(Color.argb((int) alpha, 255, 255, 255));
mTvTitle.setBackgroundColor(Color.argb((int) alpha, 31, 100, 240));
} else {//超过mHeight
mTvTitle.setBackgroundColor(Color.argb((int) 255, 31, 100, 240));
}
}
});
注意:ViewTreeObserver.OnScrollChangedListener接口在视图中组件发生改变或者某个组件状态发生改变时,都会调用回调函数。所以可能会被多次触发,不需要用的时候记得移除监听scrollView.getViewTreeObserver().removeOnScrollChangedListener(this)
,否则可能会出现内存泄漏。
③ 方法三:
通过 View.OnTouchListener 触摸监听来实现
scrollView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if(event.getAction()==MotionEvent.ACTION_MOVE) {
int y = scrollView.getScrollY();//获取纵向滑动距离
int mHeight = mTvTitle.getHeight();//获取标题栏高度
if (y <= 0) {//未滑动
mTvTitle.setBackgroundColor(Color.argb((int) 0, 31, 100, 240));
} else if (y > 0 && y <= mHeight) { //滑动过程中 并且在mHeight之内
float scale = (float) y / mHeight;
float alpha = (255 * scale);
mTvTitle.setTextColor(Color.argb((int) alpha, 255, 255, 255));
mTvTitle.setBackgroundColor(Color.argb((int) alpha, 31, 100, 240));
} else {//超过mHeight
mTvTitle.setBackgroundColor(Color.argb((int) 255, 31, 100, 240));
}
}
return false;
}
});
注意:该方法有个弊端,如果我们“用力滑动靠惯性让ScrollView自己滚动到底部或顶部”时,这种“惯性滑动”触摸事件是监听不到ScrollView滑动距离的,就会出现透明度显示错误的BUG。
④ 方法四:
自定义scrollview 实现一个接口,重写 onScrollChanged(int x, int y, int oldx, int oldy)
方法,在方法中调用接口。
1)自定义scrollview 类和接口:
public class MyScrollView extends ScrollView {
private OnScrollChanged mOnScrollChanged;
public MyScrollView(Context context) {
this(context);
}
public MyScrollView(Context context, AttributeSet attrs) {
this(context, attrs);
}
public MyScrollView(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);
if (mOnScrollChanged != null)
mOnScrollChanged.onScroll(l, t, oldl, oldt);
}
public void setOnScrollChanged(OnScrollChanged onScrollChanged) {
this.mOnScrollChanged = onScrollChanged;
}
public interface OnScrollChanged {
void onScroll(int l, int t, int oldl, int oldt);
}
}
2)在方法中调用接口:
scrollView.setOnScrollChanged(new OnScrollChanged() {
@Override
public void onScroll(View view, int l, int t, int oldl, int oldt) {
int mHeight = mTvTitle.getHeight();//获取标题栏高度
if (t <= 0) {//未滑动
mTvTitle.setBackgroundColor(Color.argb((int) 0, 31, 100, 240));
} else if (t > 0 && t <= mHeight) { //滑动过程中 并且在mHeight之内
float scale = (float) t / mHeight;
float alpha = (255 * scale);
mTvTitle.setTextColor(Color.argb((int) alpha, 255, 255, 255));
mTvTitle.setBackgroundColor(Color.argb((int) alpha, 31, 100, 240));
} else {//超过mHeight
mTvTitle.setBackgroundColor(Color.argb((int) 255, 31, 100, 240));
}
}
});
注意:l
:当前横向滑动距离;t
:当前纵向滑动距离 ;oldl
:之前横向滑动距离 ;oldt
:之前纵向滑动距离
2. 注意事项
如果通过getHeight()
获取标题栏或其他控件高度失败时,可以通过 ViewTreeObserver.OnGlobalLayoutListener 接口来监听获得。
scrollView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
mHeight = mTvTitle.getHeight();//获取标题栏高度
scrollView.getViewTreeObserver().removeGlobalOnLayoutListener(this);//注销监听,防止内存泄漏
}
});
注意:当获得正确的宽高后,记得移除这个监听,否则回调会多次执行,可能会出现内存泄漏。
网友评论