Andriod进阶知识之一,滑动监听也是初学者不得不学习的一项技能。关于滑动,你能想到什么?ScrollView、ListView、GridView、WebView、RecyclerView。。。你是否写过或研究过ScrollView滑动监听的代码呢?其他控件的滑动监听代码是怎么实现的呢?看看下面的效果,是不是很想实现?让我们带着这些疑问,一起进入Android滑动监听的世界吧。
一、ScrollView滑动监听,实现ActionBar显示和隐藏
1、写一个ScrollView滑动监听的接口,供实现滑动监听的Activity实现,代码如下:
public interface ObservableScrollViewCallbacks {
/**
* 滑动过程监听
* @param scrollY 当前滑动位置
* @param firstScoll 是否第一次滑动
* @param dragging 是否滑动中
*/
void onScrollChanged(int scrollY, boolean firstScoll, boolean dragging);
//当按下事件发生时候调用此方法
void onDownMotionEvent();
//滑动的状态
void onUpOrCancleMotionEvent(ScrollState scrollState);
}
public enum ScrollState {
STOP,
UP,
DOWN,
}
2、写一个Scrollable接口,供自定义ScrollView控件ObservableScrollView实现,代码如下:
public interface Scrollable {
//接口设置
void setScrollViewCallbacks(ObservableScrollViewCallbacks listener);
//获取Y轴滑动距离
int getCurrentScrollY();
//设置手势拦截
void setTouchInterceptionViewGroup(ViewGroup viewGroup);
}
3、自定义ObservableScrollView,实现Scrollable接口,代码如下:
public class ObservableScrollView extends ScrollView implements Scrollable{
private int mPrevScrollY;
private int mScrollY;
private ScrollState mScrollState;
private boolean mFirstScroll,mDraggging;
private ObservableScrollViewCallbacks callbacks;
public ObservableScrollView(Context context) {
super(context);
}
public ObservableScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ObservableScrollView(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);
mScrollY = t;
if (callbacks != null) {
callbacks.onScrollChanged(t, mFirstScroll, mDraggging);
}
if (mFirstScroll) {
mFirstScroll = false;
}
Log.i("ObservableScrollview",mPrevScrollY +" "+ t);
if(mPrevScrollY < t){
mScrollState = ScrollState.UP;
}else {
mScrollState = ScrollState.DOWN;
}
mPrevScrollY = t;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
switch (ev.getActionMasked()){
case MotionEvent.ACTION_DOWN:
mFirstScroll = mDraggging = true;
break;
}
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getActionMasked()) {
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
mDraggging = false;
callbacks.onUpOrCancleMotionEvent(mScrollState);
break;
case MotionEvent.ACTION_MOVE:
break;
}
return super.onTouchEvent(ev);
}
@Override
public void setScrollViewCallbacks(ObservableScrollViewCallbacks listener) {
callbacks = listener;
}
@Override
public int getCurrentScrollY() {
return mScrollY;
}
@Override
public void setTouchInterceptionViewGroup(ViewGroup viewGroup) {
}
}
这里有必要解释下onScrollChanged方法中的四个参数,l , t分别表示触摸点距离View左上角的距离,oldl , oldt分别表示上一次触摸点距离View左上角的距离。这里判断是否第一次滑动以及是否在滑动中赋值写在onInterceptTouchEvent方法中,是因为View的touch事件生命周期执行顺序是onInterceptTouchEvent-->onTouchEvent.关于View的事件拦截机制可以参考这篇博文 滑动事件拦截处理机制详解 写得很详细。手指抬起的时候,isDragging赋值false,同时调用接口的onUpOrCancleMotionEvent方法将滑动状态传入接口,供Activity回调。
4、写一个Activity实现ObserableScrollViewCallBacks接口,代码如下:
public class ActionBarControllScrollviewActivity extends AppCompatActivity implements ObservableScrollViewCallbacks{
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_actionbarcontrolscrollview);
ObservableScrollView scrollView = (ObservableScrollView) findViewById(R.id.scroll);
scrollView.setScrollViewCallbacks(this);
}
@Override
public void onScrollChanged(int scrollY, boolean firstScoll, boolean dragging) {
}
@Override
public void onDownMotionEvent() {
}
@Override
public void onUpOrCancleMotionEvent(ScrollState scrollState) {
ActionBar actionBar = getSupportActionBar();
if(actionBar == null){
return;
}
if(scrollState == ScrollState.UP){
if(actionBar.isShowing()){
actionBar.hide();
}
}else if(scrollState == ScrollState.DOWN){
if(!actionBar.isShowing()){
actionBar.show();
}
}
}
}
R.layout.actionbarcontrolscrollview代码如下:
二、ScrollView滑动监听,实现ToolBar(ActionBar)文字的缩放移动
效果图:
FlexibleSpaceActionBarScrollviewActivity代码如下:
public class FlexibleSpaceActionBarScrollviewActiivty extends AppCompatActivity implements ObservableScrollViewCallbacks {
private View mFlexibleSpaceView; //滑动可变区域
private View mToolbarView;
private TextView mTitleView;
private int mFlexibleSpaceHeight;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_flexiblespacetoolbarscrollview);
mFlexibleSpaceView = findViewById(R.id.flexible_space);
mTitleView = (TextView) findViewById(R.id.title);
mTitleView.setText(getTitle());
setTitle(null);
mToolbarView = findViewById(R.id.toolbar);
final ObservableScrollView scrollView = (ObservableScrollView) findViewById(R.id.scroll);
scrollView.setScrollViewCallbacks(this);
mFlexibleSpaceHeight = getResources().getDimensionPixelSize(R.dimen.flexible_space_height);//216
int flexibleSpaceAndToolbarHeight = mFlexibleSpaceHeight + getActionBarSize(); //216+168=384
Log.i("mFlexibleSpaceHeight:",mFlexibleSpaceHeight+" ActionBarSize:"+getActionBarSize());
findViewById(R.id.body).setPadding(0, flexibleSpaceAndToolbarHeight ,0 ,0);
//初始化加载时给滑动可变区域设置一个高度 384
mFlexibleSpaceView.getLayoutParams().height = flexibleSpaceAndToolbarHeight;
//给移动的文字设置滑动监听动画
ScrollUtils.addOnGlobalLayoutListener(mTitleView, new Runnable() {
@Override
public void run() {
updateFlexibleSpaceText(scrollView.getCurrentScrollY());
}
});
}
@Override
public void onScrollChanged(int scrollY, boolean firstScoll, boolean dragging) {
updateFlexibleSpaceText(scrollY);
}
@Override
public void onDownMotionEvent() {
}
@Override
public void onUpOrCancleMotionEvent(ScrollState scrollState) {
}
//获取actionBarSize 返回168
protected int getActionBarSize(){
TypedValue typedValue = new TypedValue();
int[] textSizeAttr = new int[]{R.attr.actionBarSize};
TypedArray typedArray = this.obtainStyledAttributes(typedValue.data,textSizeAttr);
int actionBarSize = typedArray.getDimensionPixelSize(0,-1);
typedArray.recycle();
return actionBarSize;
}
private void updateFlexibleSpaceText(final int scrollY) {
Log.i("scrollY滑动距离:",scrollY+" mFlexibleSpaceHeight高度:"+mFlexibleSpaceHeight );
//设置滑动区域的滑动距离
ViewHelper.setTranslationY(mFlexibleSpaceView, -scrollY);
//设置滑动区域的滑动距离介于0-216
int adjustedScrollY = (int) ScrollUtils.getFloat(scrollY, 0, mFlexibleSpaceHeight);
Log.i("scrollY滑动距离:","adjustedScrollY" + adjustedScrollY);
//计算文字缩放的倍数值
float maxScale = (float) (mFlexibleSpaceHeight - mToolbarView.getHeight()) / mToolbarView.getHeight();
float scale = maxScale * ((float) mFlexibleSpaceHeight - adjustedScrollY) / mFlexibleSpaceHeight;
//设置文字放大倍数 设置中心点位置为中间点 设置缩放比例
ViewHelper.setPivotX(mTitleView, 0);
ViewHelper.setPivotY(mTitleView, 0);
ViewHelper.setScaleX(mTitleView, 1 + scale);
ViewHelper.setScaleY(mTitleView, 1 + scale);
//设置文字移动位置
int maxTitleTranslationY = mToolbarView.getHeight() + mFlexibleSpaceHeight - (int) (mTitleView.getHeight() * (1 + scale));
int titleTranslationY = (int) (maxTitleTranslationY * ((float) mFlexibleSpaceHeight - adjustedScrollY) / mFlexibleSpaceHeight);
ViewHelper.setTranslationY(mTitleView, titleTranslationY);
}
}
动画效果用nineoldandroids实现, android studio环境引入
compile 'com.nineoldandroids:library:2.4.0'
本例中用到的nineoldandroids的几个方法介绍:
ViewHelper.setTranslationY(view,distance); 将view在Y轴方向移动distance距离
ViewHelper.setPivotX(view,0);
ViewHelper.setPivoY(view,0); 将view的缩放中心放置在view的中间点
ViewHelper.setScaleX(view,scale); 设置view在X轴方向的缩放倍数
ViewHelper.setScaleY(view,scale); 设置view在Y轴方向的缩放倍数
示例代码中所有滑动距离都是用像素px表示。px和dp之间的转换如下:
/**
* 根据手机的分辨率从 dp 的单位 转成为 px(像素)
*/
public static int dip2px(Context context, float dpValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
/**
* 根据手机的分辨率从 px(像素) 的单位 转成为 dp
*/
public static int px2dip(Context context, float pxValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (pxValue / scale + 0.5f);
}