zoukankan      html  css  js  c++  java
  • 自定义阻尼下拉回弹布局

    overscroll功能真正的实现分别在ScrollView、AbsListView、HorizontalScrollView和WebView中各有一份。ScrollView实现阻尼回弹,但是是FrameLayout布局,有些场合不适用。listview和webview适用范围也很有限。接下来,我们自定义一个LinearLayout的布局,带有回弹效果。

       首先用到了OverScroller类,这个类相当于一个控制器。比如调用它的方法springBack( this.getScrollX( ), this.getScrollY( ), 0, 0, 0, 0)时,只要告诉当前的页面偏移和页面的目标偏移后,它会自动计算在回弹过程中每一个时间点的位置。它要配合computeScroll方法使用。为了易于控制滑屏过程,Android框架提供了 computeScroll()方法去控制这个流程。在绘制View时,会在draw()过程调用该方法。因此, 再配合使用Scroller实例,我们就可以获得当前应该的偏移坐标,手动使View/ViewGroup偏移至该处。

      还有一个函数onOverScrolled,被overScrollBy(int, int, int, int, int, int, int, int, boolean)调用,来对一个over-scroll操作的结果进行响应。参见overScrollBy的源代码,并不复杂。其它的参看源码吧。

    package com.zte.allowance.views;
    
    import android.content.Context;
    import android.graphics.PointF;
    import android.util.AttributeSet;
    import android.util.Log;
    import android.view.MotionEvent;
    import android.widget.LinearLayout;
    import android.widget.OverScroller;
    
    public class CustomScrollView extends LinearLayout
    {
        public static final int  OVERSCROLL_DISTANCE = 50;
        protected static final int  INVALID_POINTER_ID  = -1;
    
        private OverScroller        fScroller;
        // The ‘active pointer’ is the one currently moving our object.
        private int                 fTranslatePointerId = INVALID_POINTER_ID;
        private PointF              fTranslateLastTouch = new PointF( );
        
        private float firstX;
        private float firstY;
    
        public CustomScrollView(Context context, AttributeSet attrs)
        {
            super( context, attrs );
            this.initView( context, attrs );
        }
    
        public CustomScrollView(Context context, AttributeSet attrs, int defStyle)
        {
            super( context, attrs, defStyle );
            this.initView( context, attrs );
        }
    
        protected void initView(Context context, AttributeSet attrs)
        {
            fScroller = new OverScroller( this.getContext( ) );
    
            this.setOverScrollMode( OVER_SCROLL_ALWAYS );
        }
    
        
        @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) 
        {
            int action = ev.getAction();
            switch ( action & MotionEvent.ACTION_MASK )
            {
                case MotionEvent.ACTION_MOVE:
                {
                    final float translateX = ev.getX( );
                    final float translateY = ev.getY( );
               
                    //距离小于5认为是单击事件,传递给子控件
                    if((firstX - translateX < -5) || (firstX - translateX > 5) ||
                        (firstY - translateY < -5) || (firstY - translateY > 5))
                    {
                        return true;
                    }
                    else
                    {
                        return false;
                    }
                }
                case MotionEvent.ACTION_DOWN:
                {
    
                    if ( !fScroller.isFinished( ) )
                        fScroller.abortAnimation( );
    
                    
                    final float x = ev.getX( );
                    final float y = ev.getY( );
                    firstX = x;
                    firstY = y;
                    fTranslateLastTouch.set( x, y );
                    
                    //记录第一个手指按下时的ID
                    fTranslatePointerId = ev.getPointerId( 0 );
                
                    return false;
                }
                default:
                {
                    return false;
                }
            }
        }
        
        @Override
        public boolean onTouchEvent(MotionEvent event)
        {
            final int action = event.getAction( );
            switch ( action & MotionEvent.ACTION_MASK )
            {
                case MotionEvent.ACTION_DOWN:
                {
                    if ( !fScroller.isFinished( ) )
                        fScroller.abortAnimation( );
    
                    final float x = event.getX( );
                    final float y = event.getY( );
    
                    fTranslateLastTouch.set( x, y );
                    
                    //记录第一个手指按下时的ID
                    fTranslatePointerId = event.getPointerId( 0 );
                    break;
                }
    
                case MotionEvent.ACTION_MOVE:
                {
                    /**
                     * 取第一个触摸点的位置
                     */
                    final int pointerIndexTranslate = event.findPointerIndex( fTranslatePointerId );
                    if ( pointerIndexTranslate >= 0 )
                    {
                        float translateX = event.getX( pointerIndexTranslate );
                        float translateY = event.getY( pointerIndexTranslate );
    
                        //Log.i("com.zte.allowance", "fTranslatePointerId = " + fTranslatePointerId);
                        /**
                         * deltaX 将要在X轴方向上移动距离
                         * scrollX 滚动deltaX之前,x轴方向上的偏移
                         * scrollRangeX 在X轴方向上最多能滚动的距离
                         * maxOverScrollX 在x轴方向上,滚动到边界时,还能超出的滚动距离
                         */
                        //Log.i("com.zte.allowance", "delta y = " + (fTranslateLastTouch.y - translateY));
                        this.overScrollBy(
                                (int) (fTranslateLastTouch.x - translateX),
                                (int) (fTranslateLastTouch.y - translateY)/4,
                                this.getScrollX( ),
                                this.getScrollY( ),
                                0,
                                0,
                                0,
                                OVERSCROLL_DISTANCE,
                                true );
    
                        fTranslateLastTouch.set( translateX, translateY );
    
                        this.invalidate( );
                    }
    
                    break;
                }
    
                case MotionEvent.ACTION_UP:
                {
                    /**
                     * startX 回滚开始时x轴上的偏移
                     * minX 和maxX 当前位置startX在minX和manX之 间时就不再回滚  
                     * 
                     * 此配置表示X和Y上的偏移都必须复位到0
                     */
                    if (fScroller.springBack( this.getScrollX( ), this.getScrollY( ), 0, 0, 0, 0))
                        this.invalidate( );
    
                    fTranslatePointerId = INVALID_POINTER_ID;
                    break;
                }
            }
    
            return true;
        }
    
        @Override
        public void computeScroll()
        {
            if ( fScroller != null && fScroller.computeScrollOffset( ) )
            {
                int oldX = this.getScrollX( );
                int oldY = this.getScrollY( );
                
                /**
                 * 根据动画开始及持续时间计算出当前时间下,view的X.Y方向上的偏移量
                 * 参见OverScroller computeScrollOffset 的SCROLL_MODE
                 */
                int x = fScroller.getCurrX( );
                int y = fScroller.getCurrY( );
    
                if ( oldX != x || oldY != y )
                {
                    //Log.i("com.zte.allowance", oldY + "  " + y);
                    this.overScrollBy(
                            x - oldX,
                            (y - oldY),
                            oldX,
                            oldY,
                            0,
                            0,
                            0,
                            OVERSCROLL_DISTANCE,
                            false );
                }
    
                this.postInvalidate( );
            }
        }
    
        @Override
        protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY)
        {
            // Treat animating scrolls differently; see #computeScroll() for why.
            if ( !fScroller.isFinished( ) )
            {
                super.scrollTo( scrollX, scrollY );
    
                if ( clampedX || clampedY )
                {
                    fScroller.springBack( this.getScrollX( ), this.getScrollY( ), 0, 0, 0, 0);
                }
            }
            else
            {
                super.scrollTo( scrollX, scrollY );
            }
            awakenScrollBars( );
        }
    
        @Override
        protected int computeHorizontalScrollExtent()
        {
            return this.getWidth( );
        }
    
        @Override
        protected int computeHorizontalScrollOffset()
        {
            return this.getScrollX( );
        }
    
        @Override
        protected int computeVerticalScrollExtent()
        {
            return this.getHeight( );
        }
    
    
        @Override
        protected int computeVerticalScrollOffset()
        {
            return this.getScrollY( );
        }
    
    
    }

    当然,不仅仅可以是LinearLayout,还可以是别的布局。

    layout文件如下:

    <CustomScrollView
          xmlns:android="http://schemas.android.com/apk/res/android"
          android:id="@+id/scroll_view"
          android:layout_width="fill_parent"
          android:layout_height="fill_parent"
          android:orientation="vertical"
          >
            <LinearLayout
                android:id="@+id/message_text"
                android:layout_width="match_parent"
                android:layout_height="200dp"
                android:layout_marginTop="-50px"
                android:background="@drawable/title_leaf"
                android:orientation="vertical">
                
                
            </LinearLayout>
    
        
        <RelativeLayout
          android:id="@+id/content_id"
          android:layout_width="match_parent"
          android:layout_height="fill_parent"
          android:background="@color/window_bg"
          android:padding="5dp">
            <Button 
                android:layout_width = "wrap_content"
                android:layout_height = "wrap_content"/>
        </RelativeLayout>
    </CustomScrollView>

    当把第一个textview设置成

    android:layout_marginTop="-50px" 时就可以实现隐藏头部下拉可见的效果了。
  • 相关阅读:
    POJ 1300 Open Door
    POJ 2230 Watchcow
    codevs 1028 花店橱窗布置
    codevs 1021 玛丽卡
    codevs 1519 过路费
    codevs 3287 货车运输
    codevs 3305 水果姐逛水果街二
    codevs 1036 商务旅行
    codevs 4605 LCA
    POJ 1330 Nearest Common Ancestors
  • 原文地址:https://www.cnblogs.com/chyl411/p/3847930.html
Copyright © 2011-2022 走看看