ScrollView滑动监听实现界面动画效果

来源:互联网 发布:python统计字母个数 编辑:程序博客网 时间:2024/05/01 19:35

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);}

滑动状态用Enum枚举类型表示,STOP表示滑动停止,UP表示向上滑动,DOWN表示向下滑动,代码如下:

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左上角的距离。
这里判断滑动方向是根据当前触摸点的Y轴位置(距离View顶部的距离)和之前触摸点的Y轴位置判断的,如果当前触摸点的位置距离View顶部的距离大于之前触摸点距离View顶部的距离,表示向上滑动,反之,向下滑动。每次滑动后,记得把当前触摸点距离View顶部的距离赋值给之前触摸点距离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代码如下:

<com.lc.proctice.androidstudioproctice.anim.widget.ObservableScrollView xmlns:android="http://schemas.android.com/apk/res/android"    android:id="@+id/scroll"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:fillViewport="true">    <TextView        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:layout_marginBottom="16dp"        android:layout_marginLeft="16dp"        android:layout_marginRight="16dp"        android:layout_marginTop="16dp"        android:text="@string/lipsum" /></com.lc.proctice.androidstudioproctice.anim.widget.ObservableScrollView>

注意,ObservableScrollView控件是自定义的,这里引入要根据自己项目的实际文件位置命名。


此事件只处理了ActionBar的显示和隐藏,根据滑动状态处理,向上滑动,则隐藏ActionBar,向下滑动,则显示ActionBar。这样,前面动图的滑动效果就实现啦。


二、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之间的转换如下:
<span style="white-space:pre"></span>/** * 根据手机的分辨率从 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);}


R.layout.flexiblespacetoolbarscrollview 布局文件代码如下:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:app="http://schemas.android.com/apk/res-auto"    android:layout_width="match_parent"    android:layout_height="match_parent">    <com.lc.proctice.androidstudioproctice.anim.widget.ObservableScrollView        android:id="@+id/scroll"        android:layout_width="match_parent"        android:layout_height="match_parent"        android:fillViewport="true"        android:scrollbars="none">        <FrameLayout            android:id="@+id/body"            android:layout_width="match_parent"            android:layout_height="wrap_content"            android:paddingTop="@dimen/flexible_space_height">            <TextView                android:layout_width="match_parent"                android:layout_height="wrap_content"                android:background="@android:color/white"                android:paddingBottom="@dimen/activity_vertical_margin"                android:paddingLeft="@dimen/activity_horizontal_margin"                android:paddingRight="@dimen/activity_horizontal_margin"                android:paddingTop="@dimen/activity_vertical_margin"                android:text="@string/lipsum" />        </FrameLayout>    </com.lc.proctice.androidstudioproctice.anim.widget.ObservableScrollView>    <View        android:id="@+id/flexible_space"        android:layout_width="match_parent"        android:layout_height="0dp"        android:background="@color/primary" />    <android.support.v7.widget.Toolbar        android:id="@+id/toolbar"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:background="?attr/colorPrimary"        android:minHeight="?attr/actionBarSize"        app:popupTheme="@style/Theme.AppCompat.Light.DarkActionBar"        app:theme="@style/Base.Widget.AppCompat.Toolbar"        />    <!--    android:layout_marginLeft on FrameLayout seems to be ignored on Android 2.3    so add a parent RelativeLayout and set padding to it.    -->    <RelativeLayout        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:paddingLeft="@dimen/toolbar_margin_start">        <TextView            android:id="@+id/title"            android:layout_width="match_parent"            android:layout_height="wrap_content"            android:ellipsize="end"            android:gravity="center_vertical"            android:maxLines="1"            android:minHeight="?attr/actionBarSize"            android:textColor="@android:color/white"            android:textSize="20sp" />        <LinearLayout            android:layout_width="match_parent"            android:layout_height="wrap_content"            android:orientation="vertical">            <View                android:layout_width="match_parent"                android:layout_height="?attr/actionBarSize"                android:background="@android:color/transparent" />            <View                android:layout_width="match_parent"                android:layout_height="@dimen/flexible_space_height"                android:background="@android:color/transparent" />        </LinearLayout>    </RelativeLayout></FrameLayout>









0 2