Android_Scroller滑动动画

Posted 杨迈1949

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android_Scroller滑动动画相关的知识,希望对你有一定的参考价值。

转载请注明出处:http://blog.csdn.net/y22222ly/article/details/51842218

当我们在使用view的scrollTo()或scrollBy()时,会发现这个滑动很生硬,没有动画效果,一下就过去了,就像我前篇文章提到的那样。如果能平滑的滑动回去的话,最好不过了,刚好安卓提供一个Scroller类,专门来处理view在scrollTo()或scrollBy()时没有滑动效果的问题。

Scroller基本使用

下例源码引用自:http://ipjmc.iteye.com/blog/1615828
该大神详细说明了各个方法的用法。

import android.content.Context;  
import android.util.AttributeSet;  
import android.util.Log;  
import android.view.View;  
import android.widget.LinearLayout;  
import android.widget.Scroller;  

public class CustomView extends LinearLayout   

    private static final String TAG = "Scroller";  

    private Scroller mScroller;  

    public CustomView(Context context, AttributeSet attrs)   
        super(context, attrs);  
        mScroller = new Scroller(context);  
      

    //调用此方法滚动到目标位置  
    public void smoothScrollTo(int fx, int fy)   
        int dx = fx - mScroller.getFinalX();  
        int dy = fy - mScroller.getFinalY();  
        smoothScrollBy(dx, dy);  
      

    //调用此方法设置滚动的相对偏移  
    public void smoothScrollBy(int dx, int dy)   

        //设置mScroller的滚动偏移量  
        mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(), dx, dy);  
        invalidate();//这里必须调用invalidate()才能保证computeScroll()会被调用,否则不一定会刷新界面,看不到滚动效果  
      

    @Override  
    public void computeScroll()   

        //先判断mScroller滚动是否完成  
        if (mScroller.computeScrollOffset())   

            //这里调用View的scrollTo()完成实际的滚动  
            scrollTo(mScroller.getCurrX(), mScroller.getCurrY());  

            //必须调用该方法,否则不一定能看到滚动效果  
            postInvalidate();  
          
        super.computeScroll();  
      
 

使用Scroller分三步:
1. 声明与初始化
2. 调用startScroll()一定要注意4个参数,前2个为当前滑动偏移量,后2个为需要滑动的偏移量
3. 重写view的computeScroll()方法,调用mScroller.computeScrollOffset()计算滑动值
4. 使用计算后的Scroller实例,调用view的scrollTo()进行滑动

Scroller实战

根据上述代码后自己学习后,对前一篇文章提到的滑动处理scrollTo()问题做了实现。
效果图:

源码:

package com.example.y2222.myview;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.GestureDetector;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.animation.LinearInterpolator;
import android.widget.LinearLayout;
import android.widget.Scroller;

import com.example.y2222.myapplication.R;

/**
 * Created by raise.yang on 2016/06/29.
 */
public class GestureDemoView extends LinearLayout 
    //1,定义GestureDetector类
    private GestureDetector m_gestureDetector;
    //1,定义Scroller类
    private Scroller m_scroller;
    private int m_max_scrollX;

    public GestureDemoView(Context context, AttributeSet attrs) 
        this(context, attrs, 0);
    

    public GestureDemoView(Context context, AttributeSet attrs, int defStyleAttr) 
        super(context, attrs, defStyleAttr);
        //设置为可点击
        setClickable(true);
        //2,初始化手势类,同时设置手势监听
        m_gestureDetector = new GestureDetector(context, onGestureListener);

        LayoutInflater.from(context).inflate(R.layout.view_gesture, this);
        // 2,初始化Scroller类,可自定义插值器
        m_scroller = new Scroller(getContext(), new LinearInterpolator());
    

    @Override
    public boolean onTouchEvent(MotionEvent event) 
        //3,将touch事件交给gesture处理
        m_gestureDetector.onTouchEvent(event);
        if (event.getAction() == MotionEvent.ACTION_UP) 
            // GestureDetector没有处理up事件的方法,只能在这里处理了。
            int scrollX = getScrollX();
            if (scrollX > m_max_scrollX / 2) 
                show_right_view();
             else 
                hide_right_view();
            
        
        return super.onTouchEvent(event);
    

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) 
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) 
            //测量子view的宽高,?不测量,右侧布局会不显示,这里有点疑问
            measureChild(getChildAt(i), widthMeasureSpec, heightMeasureSpec);
            if (i == 1) 
                m_max_scrollX = getChildAt(i).getMeasuredWidth();
            
        
    

    //初始化手势监听对象,使用GestureDetector.OnGestureListener的实现抽象类,因为实际开发中好多方法用不上
    private final GestureDetector.OnGestureListener onGestureListener = new GestureDetector.SimpleOnGestureListener() 

        @Override
        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) 
            Log.d("GestureDemoView", "onScroll() distanceX = " + distanceX + " getScrollX = " + getScrollX() + " max_scrollX = " + m_max_scrollX);
            int scrollX = getScrollX();
            int minScrollX = -scrollX;
            int maxScrollY = m_max_scrollX - scrollX;
            // 对滑动的距离边界控制
            if (distanceX > maxScrollY) 
                distanceX = maxScrollY;
             else if (distanceX < minScrollX) 
                distanceX = minScrollX;
            
//            m_scroller.startScroll(m_scroller.getFinalX(), m_scroller.getFinalY(), m_scroller.getFinalX() + (int) distanceX, 0);
            scrollBy((int) distanceX, 0);
            return true;
        

        @Override
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) 
            Log.d("GestureDemoView", "onFling() velocityX = " + velocityX);
            if (velocityX < 0) 
                //快速向左滑动
                show_right_view();
             else 
                hide_right_view();
            
            return super.onFling(e1, e2, velocityX, velocityY);
        
    ;

    private void show_right_view() 
        //3,开启滑动条
        // 注意:前两个参数为起始滑动点,后2个参数为滑动距离
        m_scroller.startScroll(getScrollX(), 0, m_max_scrollX-getScrollX(), 0);
        //这里必须调用invalidate()才能保证computeScroll()会被调用,否则不一定会刷新界面,看不到滚动效果
        invalidate();
//        scrollTo(m_max_scrollX, 0);
    

    private void hide_right_view() 
        // 注意:前两个参数为起始滑动点,后2个参数为滑动距离
        m_scroller.startScroll(getScrollX(), 0, 0 - getScrollX(), 0);
        invalidate();
//        scrollTo(0, 0);
    

    @Override
    public void computeScroll() 
        //4,调用view自身的scrollTo()滑动
        if (m_scroller.computeScrollOffset()) //计算滑动值,并判断是否结束false表示结束
            // 两个参数为相对父布局的绝对坐标,这里去Scroller计算出来的当前滑动值
            scrollTo(m_scroller.getCurrX(), m_scroller.getCurrY());
            postInvalidate();
        
    

Scroller就像属性动画中的ValueAnimator,他只产生一段连续的值,而不做处理,需要view根据自身的情况去处理这些值,目前看好像只用在scrollTo()或scrollBy()中。
还可以控制动画时间,调用5个参数的startScroll()即可:

    /**
     * Start scrolling by providing a starting point, the distance to travel,
     * and the duration of the scroll.
     * 
     * @param startX Starting horizontal scroll offset in pixels. Positive
     *        numbers will scroll the content to the left.
     * @param startY Starting vertical scroll offset in pixels. Positive numbers
     *        will scroll the content up.
     * @param dx Horizontal distance to travel. Positive numbers will scroll the
     *        content to the left.
     * @param dy Vertical distance to travel. Positive numbers will scroll the
     *        content up.
     * @param duration Duration of the scroll in milliseconds.
     */
    public void startScroll(int startX, int startY, int dx, int dy, int duration) 
    

ListView滑动删除

单个item的滑动效果已经出来了,那么顺便将其集成在ListView中,做成滑动删除效果也不错,效果图如下:

实现起来并不难,主要解决的问题就是:如何处理滑动冲突:左右滑动时,若上下滑动父控件(ListView)会拦截滑动事件,去做上下滑动。这时,子view(ViewGroup)就只接收到一个MotionEvent.ACTION_CANCEL事件。
如何让子view在滑动时,提醒父控件不要拦截事件,将事件传递下去呢?我之前在子View中,设置一个flag用来标识view在滑动状态,然后ListView去根据这个值判断是否拦截滑动事件,这样做起来有些bug,在偶然看listview源码时,看到一个方法可以请求父控件不要拦截touch事件,这个事件就是requestDisallowInterceptTouchEvent(true),在AbsListView.startScrollIfNeeded()方法中。
有了这个方法,实现起来简单很多,只要在左右开始滑动时,请求父类不要拦截,我们自己讲touch事件消耗即可。记住要在ACTION_UP时,将其还原为false.直接上代码:
GestureDemoView:

package com.example.y2222.myview;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.GestureDetector;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.animation.LinearInterpolator;
import android.widget.LinearLayout;
import android.widget.Scroller;
import android.widget.TextView;

import com.example.y2222.myapplication.R;

/**
 * Created by raise.yang on 2016/06/29.
 */
public class GestureDemoView extends LinearLayout 
    private GestureDetector m_gestureDetector;
    private Scroller m_scroller;
    private int m_max_scrollX;
    private TextView m_primary;

    public GestureDemoView(Context context, AttributeSet attrs) 
        this(context, attrs, 0);
    

    public GestureDemoView(Context context, AttributeSet attrs, int defStyleAttr) 
        super(context, attrs, defStyleAttr);
        //设置为可点击
        setClickable(true);
        m_gestureDetector = new GestureDetector(context, onGestureListener);
        m_gestureDetector.setIsLongpressEnabled(false);
        LayoutInflater.from(context).inflate(R.layout.view_gesture, this);
        m_scroller = new Scroller(getContext(), new LinearInterpolator());
    

    @Override
    protected void onFinishInflate() 
        super.onFinishInflate();
        m_primary = (TextView) findViewById(R.id.primary_text);
    

    public void setPrimaryText(String text) 
        m_primary.setText(text);
    

    @Override
    public boolean onTouchEvent(MotionEvent event) 
        Log.d("GestureDemoView", "onTouchEvent() ");
        m_gestureDetector.onTouchEvent(event);
        if (event.getAction() == MotionEvent.ACTION_UP) 

            // 抬起手指,请求父类可以拦截touch事件
            requestDisallowInterceptTouchEvent(false);

            int scrollX = getScrollX();
            if (scrollX > m_max_scrollX / 2) 
                show_right_view();
             else 
                hide_right_view();
            
        
        // 返回true,将此事件消耗,父类就不会处理onTouchEvent事件
        return true;
    

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) 
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) 
            measureChild(getChildAt(i), widthMeasureSpec, heightMeasureSpec);
            if (i == 1) 
                m_max_scrollX = getChildAt(i).getMeasuredWidth();
            
        
    

    private final GestureDetector.OnGestureListener onGestureListener = new GestureDetector.SimpleOnGestureListener() 

        @Override
        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) 
            Log.d("GestureDemoView", "onScroll() distanceX = " + distanceX + " getScrollX = " + getScrollX() + " max_scrollX = " + m_max_scrollX);
            int scrollX = getScrollX();
            int minScrollX = -scrollX;
            int maxScrollY = m_max_scrollX - scrollX;
            // 对滑动的距离边界控制
            if (distanceX > maxScrollY) 
                distanceX = maxScrollY;
             else if (distanceX < minScrollX) 
                distanceX = minScrollX;
            
            scrollBy((int) distanceX, 0);
            //当前view开始滑动,请求父类不要拦截move事件
            requestDisallowInterceptTouchEvent(true);
            return true;
        

        @Override
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) 
            Log.d("GestureDemoView", "onFling() velocityX = " + velocityX);
            if (velocityX < 0) 
                //快速向左滑动
                show_right_view();
             else 
                hide_right_view();
            
            return super.onFling(e1, e2, velocityX, velocityY);
        
    ;

    private void show_right_view() 
        m_scroller.startScroll(getScrollX(), 0, m_max_scrollX - getScrollX(), 0, 100);
        invalidate();
    

    private void hide_right_view() 
        m_scroller.startScroll(getScrollX(), 0, 0 - getScrollX(), 0, 100);
        invalidate();
    

    @Override
    public void computeScroll() 
        if (m_scroller.computeScrollOffset()) //计算滑动值,并判断是否结束false表示结束
            scrollTo(m_scroller.getCurrX(), m_scroller.getCurrY());
            postInvalidate();
        
    

    public void initViewPosition() 
        scrollTo(0, 0);
    

GestureActivity:

package com.example.y2222.myapplication;

import android.app.Activity;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ListView;

import com.example.y2222.myview.GestureDemoView;

public class GestureActivity extends Activity 

    private ListView m_listView;
    private String[] m_datas = new String[]
            "1", "2", "3", "4", "5",
            "6", "7", "8", "9", "10",
    ;

    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_gesture);

        m_listView = (ListView) findViewById(R.id.listview);
        m_listView.setAdapter(new Adapter());
    

    class Adapter extends BaseAdapter 

        @Override
        public View getView(int position, View convertView, ViewGroup parent) 
            if (convertView == null) 
                convertView = LayoutInflater.from(GestureActivity.this).inflate(R.layout.item_slide_listview, parent, false);
            
            GestureDemoView root = (GestureDemoView) convertView;
            root.setPrimaryText(getItem(position));
            root.initViewPosition();
            return convertView;
        

        @Override
        public int getCount() 
            return m_datas.length;
        

        @Override
        public String getItem(int position) 
            return m_datas[position];
        

        @Override
        public long getItemId(int position) 
            return position;
        
    


item的布局文件:

<?xml version="1.0" encoding="utf-8"?>
<com.example.y2222.myview.GestureDemoView xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="80dp">

</com.example.y2222.myview.GestureDemoView>

Activity的布局文件:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/holo_blue_light"
    tools:context="com.example.y2222.myapplication.GestureActivity">

    <ListView
        android:id="@+id/listview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</RelativeLayout>

以上是关于Android_Scroller滑动动画的主要内容,如果未能解决你的问题,请参考以下文章

您可以测量尚未加载的活动的视图高度吗?

Android Studio之Activity切换动画

新手导航页(小圆点

Android中View滑动实现方式

CSS3动画如何设置滑动到当屏的时候才触发动画播放

JQuery--基础动画滑动动画淡入淡出动画自定义动画