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" 时就可以实现隐藏头部下拉可见的效果了。
  • 相关阅读:
    转载--如果不想一辈子做工程师!
    转-----FPGA工程师:持守梦想or屈于现实
    TIMEQUEST学习之黑金动力(四)
    fir 窗口设计法
    CORDIC逼近算法
    sram的读和写
    ad采样后幅度的衰减
    verilog 之数字电路 边沿检测电路
    verilog 之数字电路 寄存器,触发器。
    da分布式算法
  • 原文地址:https://www.cnblogs.com/chyl411/p/3847930.html
Copyright © 2011-2022 走看看