欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

ScrollView反弹效果 仿小米私密短信效果

程序员文章站 2022-05-31 09:36:03
...

转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/28441197

现在很多APP都给ScrollView添加了反弹效果,QQ、小米私密短信等。恰好在网上看到一个类:BounceScrollView ,

原创地址是:http://blog.csdn.NET/h7870181/article/details/8960430 , 可惜作者没有提供一个效果图,于是我发现小米短信列表页往下拉,有反弹效果,且拉到1/3以上时,会打开私密短信列表,小米的用户可以试试。

我在作者BounceScrollView 类的基础上修改了一下,写了一个例子,给大家分享下。

效果图:(模拟器的效果不佳,凑合可以看出来效果)

ScrollView反弹效果 仿小米私密短信效果


1、首先是布局文件:

  1. <RelativeLayout xmlns:android=“http://schemas.android.com/apk/res/android”  
  2.     xmlns:tools=“http://schemas.android.com/tools”  
  3.     android:layout_width=“match_parent”  
  4.     android:layout_height=“match_parent”  
  5.   >  
  6.   
  7.     <com.example.zhy_bouncescrollview02.BounceScrollView  
  8.         android:background=“@drawable/shakehideimg_man”  
  9.         android:layout_width=“fill_parent”  
  10.         android:layout_height=“fill_parent”  
  11.         android:id=“@+id/id_scrollView” >  
  12.   
  13.         <com.example.zhy_bouncescrollview02.MyListView  
  14.             android:background=“#fff”  
  15.             android:id=“@+id/id_listView”  
  16.             android:layout_width=“fill_parent”  
  17.             android:layout_height=“wrap_content” >  
  18.         </com.example.zhy_bouncescrollview02.MyListView>  
  19.     </com.example.zhy_bouncescrollview02.BounceScrollView>  
  20.   
  21. </RelativeLayout>  
<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"
  >

    <com.example.zhy_bouncescrollview02.BounceScrollView
        android:background="@drawable/shakehideimg_man"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:id="@+id/id_scrollView" >

        <com.example.zhy_bouncescrollview02.MyListView
            android:background="#fff"
            android:id="@+id/id_listView"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content" >
        </com.example.zhy_bouncescrollview02.MyListView>
    </com.example.zhy_bouncescrollview02.BounceScrollView>

</RelativeLayout>

一个自定义的ScrollView内部包含着一个自定义的ListView,自定义ScrollView是为了添加反弹效果,自定义ListView是为了解决ScrollView和ListView嵌套产生的问题。

2、BounceScrollView

  1. package com.example.zhy_bouncescrollview02;  
  2.   
  3. import android.content.Context;  
  4. import android.graphics.Rect;  
  5. import android.util.AttributeSet;  
  6. import android.view.MotionEvent;  
  7. import android.view.View;  
  8. import android.view.animation.Animation;  
  9. import android.view.animation.TranslateAnimation;  
  10. import android.widget.ScrollView;  
  11.   
  12. /** 
  13.  * 支持上下反弹效果的ScrollView 
  14.  *  
  15.  * @author zhy 
  16.  *  
  17.  */  
  18. public class BounceScrollView extends ScrollView  
  19. {  
  20.   
  21.     private boolean isCalled ;   
  22.       
  23.     private Callback mCallback;  
  24.   
  25.     /** 
  26.      * 包含的View 
  27.      */  
  28.     private View mView;  
  29.     /** 
  30.      * 存储正常时的位置 
  31.      */  
  32.     private Rect mRect = new Rect();  
  33.   
  34.     /** 
  35.      * y坐标 
  36.      */  
  37.     private int y;  
  38.   
  39.     private boolean isFirst = true;  
  40.   
  41.     public BounceScrollView(Context context, AttributeSet attrs)  
  42.     {  
  43.         super(context, attrs);  
  44.     }  
  45.   
  46.     /*** 
  47.      * 根据 XML 生成视图工作完成.该函数在生成视图的最后调用,在所有子视图添加完之后. 即使子类覆盖了 onFinishInflate 
  48.      * 方法,也应该调用父类的方法,使该方法得以执行. 
  49.      */  
  50.     @Override  
  51.     protected void onFinishInflate()  
  52.     {  
  53.         if (getChildCount() > 0)  
  54.             mView = getChildAt(0);  
  55.         super.onFinishInflate();  
  56.     }  
  57.   
  58.     @Override  
  59.     public boolean onTouchEvent(MotionEvent ev)  
  60.     {  
  61.         if (mView != null)  
  62.         {  
  63.             commonOnTouch(ev);  
  64.         }  
  65.   
  66.         return super.onTouchEvent(ev);  
  67.     }  
  68.   
  69.     private void commonOnTouch(MotionEvent ev)  
  70.     {  
  71.         int action = ev.getAction();  
  72.         int cy = (int) ev.getY();  
  73.         switch (action)  
  74.         {  
  75.         case MotionEvent.ACTION_DOWN:  
  76.             break;  
  77.         /** 
  78.          * 跟随手指移动 
  79.          */  
  80.         case MotionEvent.ACTION_MOVE:  
  81.   
  82.             int dy = cy - y;  
  83.             if (isFirst)  
  84.             {  
  85.                 dy = 0;  
  86.                 isFirst = false;  
  87.             }  
  88.             y = cy;  
  89.   
  90.             if (isNeedMove())  
  91.             {  
  92.                 if (mRect.isEmpty())  
  93.                 {  
  94.                     /** 
  95.                      * 记录移动前的位置 
  96.                      */  
  97.                     mRect.set(mView.getLeft(), mView.getTop(),  
  98.                             mView.getRight(), mView.getBottom());  
  99.                 }  
  100.   
  101.                 mView.layout(mView.getLeft(), mView.getTop() + 2 * dy / 3,  
  102.                         mView.getRight(), mView.getBottom() + 2 * dy / 3);  
  103.   
  104.                 if (shouldCallBack(dy))  
  105.                 {  
  106.                     if (mCallback != null)  
  107.                     {  
  108.                         if(!isCalled)  
  109.                         {  
  110.                             isCalled = true ;   
  111.                             resetPosition();  
  112.                             mCallback.callback();  
  113.                               
  114.                               
  115.                         }  
  116.                     }  
  117.                 }  
  118.             }  
  119.   
  120.             break;  
  121.         /** 
  122.          * 反弹回去 
  123.          */  
  124.         case MotionEvent.ACTION_UP:  
  125.             if (!mRect.isEmpty())  
  126.             {  
  127.                 resetPosition();  
  128.             }  
  129.             break;  
  130.   
  131.         }  
  132.     }  
  133.   
  134.     /** 
  135.      * 当从上往下,移动距离达到一半时,回调接口 
  136.      *  
  137.      * @return 
  138.      */  
  139.     private boolean shouldCallBack(int dy)  
  140.     {  
  141.   
  142.         if (dy > 0 && mView.getTop() > getHeight() / 2)  
  143.             return true;  
  144.         return false;  
  145.     }  
  146.   
  147.     private void resetPosition()  
  148.     {  
  149.         Animation animation = new TranslateAnimation(00, mView.getTop(),  
  150.                 mRect.top);  
  151.         animation.setDuration(200);  
  152.         animation.setFillAfter(true);  
  153.         mView.startAnimation(animation);  
  154.         mView.layout(mRect.left, mRect.top, mRect.right, mRect.bottom);  
  155.         mRect.setEmpty();  
  156.         isFirst = true;  
  157.         isCalled = false ;   
  158.     }  
  159.   
  160.     /*** 
  161.      * 是否需要移动布局 inner.getMeasuredHeight():获取的是控件的总高度 
  162.      *  
  163.      * getHeight():获取的是屏幕的高度 
  164.      *  
  165.      * @return 
  166.      */  
  167.     public boolean isNeedMove()  
  168.     {  
  169.         int offset = mView.getMeasuredHeight() - getHeight();  
  170.         int scrollY = getScrollY();  
  171.         // 0是顶部,后面那个是底部  
  172.         if (scrollY == 0 || scrollY == offset)  
  173.         {  
  174.             return true;  
  175.         }  
  176.         return false;  
  177.     }  
  178.   
  179.     public void setCallBack(Callback callback)  
  180.     {  
  181.         mCallback = callback;  
  182.     }  
  183.   
  184.     interface Callback  
  185.     {  
  186.         void callback();  
  187.     }  
  188.   
  189. }  
package com.example.zhy_bouncescrollview02;

import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.TranslateAnimation;
import android.widget.ScrollView;

/**
 * 支持上下反弹效果的ScrollView
 * 
 * @author zhy
 * 
 */
public class BounceScrollView extends ScrollView
{

    private boolean isCalled ; 

    private Callback mCallback;

    /**
     * 包含的View
     */
    private View mView;
    /**
     * 存储正常时的位置
     */
    private Rect mRect = new Rect();

    /**
     * y坐标
     */
    private int y;

    private boolean isFirst = true;

    public BounceScrollView(Context context, AttributeSet attrs)
    {
        super(context, attrs);
    }

    /***
     * 根据 XML 生成视图工作完成.该函数在生成视图的最后调用,在所有子视图添加完之后. 即使子类覆盖了 onFinishInflate
     * 方法,也应该调用父类的方法,使该方法得以执行.
     */
    @Override
    protected void onFinishInflate()
    {
        if (getChildCount() > 0)
            mView = getChildAt(0);
        super.onFinishInflate();
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev)
    {
        if (mView != null)
        {
            commonOnTouch(ev);
        }

        return super.onTouchEvent(ev);
    }

    private void commonOnTouch(MotionEvent ev)
    {
        int action = ev.getAction();
        int cy = (int) ev.getY();
        switch (action)
        {
        case MotionEvent.ACTION_DOWN:
            break;
        /**
         * 跟随手指移动
         */
        case MotionEvent.ACTION_MOVE:

            int dy = cy - y;
            if (isFirst)
            {
                dy = 0;
                isFirst = false;
            }
            y = cy;

            if (isNeedMove())
            {
                if (mRect.isEmpty())
                {
                    /**
                     * 记录移动前的位置
                     */
                    mRect.set(mView.getLeft(), mView.getTop(),
                            mView.getRight(), mView.getBottom());
                }

                mView.layout(mView.getLeft(), mView.getTop() + 2 * dy / 3,
                        mView.getRight(), mView.getBottom() + 2 * dy / 3);

                if (shouldCallBack(dy))
                {
                    if (mCallback != null)
                    {
                        if(!isCalled)
                        {
                            isCalled = true ; 
                            resetPosition();
                            mCallback.callback();


                        }
                    }
                }
            }

            break;
        /**
         * 反弹回去
         */
        case MotionEvent.ACTION_UP:
            if (!mRect.isEmpty())
            {
                resetPosition();
            }
            break;

        }
    }

    /**
     * 当从上往下,移动距离达到一半时,回调接口
     * 
     * @return
     */
    private boolean shouldCallBack(int dy)
    {

        if (dy > 0 && mView.getTop() > getHeight() / 2)
            return true;
        return false;
    }

    private void resetPosition()
    {
        Animation animation = new TranslateAnimation(0, 0, mView.getTop(),
                mRect.top);
        animation.setDuration(200);
        animation.setFillAfter(true);
        mView.startAnimation(animation);
        mView.layout(mRect.left, mRect.top, mRect.right, mRect.bottom);
        mRect.setEmpty();
        isFirst = true;
        isCalled = false ; 
    }

    /***
     * 是否需要移动布局 inner.getMeasuredHeight():获取的是控件的总高度
     * 
     * getHeight():获取的是屏幕的高度
     * 
     * @return
     */
    public boolean isNeedMove()
    {
        int offset = mView.getMeasuredHeight() - getHeight();
        int scrollY = getScrollY();
        // 0是顶部,后面那个是底部
        if (scrollY == 0 || scrollY == offset)
        {
            return true;
        }
        return false;
    }

    public void setCallBack(Callback callback)
    {
        mCallback = callback;
    }

    interface Callback
    {
        void callback();
    }

}

主要就是监听onTouchEvent,当MOVE时,ScrollView中的控件跟随手指移动,UP时恢复原来的位置;当达到1/2时,会调用用户设置的回调,细节就自己看代码了。

3、MyListView

  1. package com.example.zhy_bouncescrollview02;  
  2.   
  3. import android.content.Context;  
  4. import android.util.AttributeSet;  
  5. import android.widget.ListView;  
  6.   
  7. /** 
  8.  * 解决ScrollView与ListView的嵌套问题 
  9.  * @author zhy 
  10.  *  
  11.  */  
  12. public class MyListView extends ListView  
  13. {  
  14.   
  15.     public MyListView(Context context, AttributeSet attrs)  
  16.     {  
  17.         super(context, attrs);  
  18.     }  
  19.   
  20.     public MyListView(Context context, AttributeSet attrs, int defStyle)  
  21.     {  
  22.         super(context, attrs, defStyle);  
  23.     }  
  24.   
  25.     public MyListView(Context context)  
  26.     {  
  27.         super(context);  
  28.     }  
  29.   
  30.     @Override  
  31.     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)  
  32.     {  
  33.   
  34.         /** 
  35.          * 解决ScrollView与ListView的嵌套问题 
  36.          */  
  37.         int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,  
  38.                 MeasureSpec.AT_MOST);  
  39.   
  40.         super.onMeasure(widthMeasureSpec, expandSpec);  
  41.     }  
  42.   
  43. }  
package com.example.zhy_bouncescrollview02;

import android.content.Context;
import android.util.AttributeSet;
import android.widget.ListView;

/**
 * 解决ScrollView与ListView的嵌套问题
 * @author zhy
 * 
 */
public class MyListView extends ListView
{

    public MyListView(Context context, AttributeSet attrs)
    {
        super(context, attrs);
    }

    public MyListView(Context context, AttributeSet attrs, int defStyle)
    {
        super(context, attrs, defStyle);
    }

    public MyListView(Context context)
    {
        super(context);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
    {

        /**
         * 解决ScrollView与ListView的嵌套问题
         */
        int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,
                MeasureSpec.AT_MOST);

        super.onMeasure(widthMeasureSpec, expandSpec);
    }

}

4、主Activity

  1. package com.example.zhy_bouncescrollview02;  
  2.   
  3. import java.util.ArrayList;  
  4. import java.util.Arrays;  
  5.   
  6. import android.app.Activity;  
  7. import android.content.Intent;  
  8. import android.os.Bundle;  
  9. import android.widget.ArrayAdapter;  
  10. import android.widget.ListView;  
  11. import android.widget.Toast;  
  12.   
  13. import com.example.zhy_bouncescrollview02.BounceScrollView.Callback;  
  14.   
  15. public class MainActivity extends Activity  
  16. {  
  17.   
  18.     private ListView mListView;  
  19.     private BounceScrollView mScrollView;  
  20.   
  21.     @Override  
  22.     protected void onCreate(Bundle savedInstanceState)  
  23.     {  
  24.         super.onCreate(savedInstanceState);  
  25.         setContentView(R.layout.activity_main);  
  26.         mScrollView = (BounceScrollView) findViewById(R.id.id_scrollView);  
  27.         mScrollView.setCallBack(new Callback()  
  28.         {  
  29.   
  30.             @Override  
  31.             public void callback()  
  32.             {  
  33.                 Toast.makeText(MainActivity.this“you can do something!”0)  
  34.                         .show();  
  35.                 Intent intent = new Intent(MainActivity.this,  
  36.                         SecondActivity.class);  
  37.                 startActivity(intent);  
  38.                 overridePendingTransition(R.anim.fade_in, R.anim.fade_out);  
  39.             }  
  40.         });  
  41.         mListView = (ListView) findViewById(R.id.id_listView);  
  42.         mListView.setAdapter(new ArrayAdapter<String>(this,  
  43.                 android.R.layout.simple_list_item_1, new ArrayList<String>(  
  44.                         Arrays.asList(”Hello”“World”“Welcome”“Java”,  
  45.                                 ”Android”“Lucene”“C++”“C#”“HTML”,  
  46.                                 ”Welcome”“Java”“Android”“Lucene”“C++”,  
  47.                                 ”C#”“HTML”))));  
  48.     }  
  49.   
  50. }  
package com.example.zhy_bouncescrollview02;

import java.util.ArrayList;
import java.util.Arrays;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;

import com.example.zhy_bouncescrollview02.BounceScrollView.Callback;

public class MainActivity extends Activity
{

    private ListView mListView;
    private BounceScrollView mScrollView;

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mScrollView = (BounceScrollView) findViewById(R.id.id_scrollView);
        mScrollView.setCallBack(new Callback()
        {

            @Override
            public void callback()
            {
                Toast.makeText(MainActivity.this, "you can do something!", 0)
                        .show();
                Intent intent = new Intent(MainActivity.this,
                        SecondActivity.class);
                startActivity(intent);
                overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
            }
        });
        mListView = (ListView) findViewById(R.id.id_listView);
        mListView.setAdapter(new ArrayAdapter<String>(this,
                android.R.layout.simple_list_item_1, new ArrayList<String>(
                        Arrays.asList("Hello", "World", "Welcome", "Java",
                                "Android", "Lucene", "C++", "C#", "HTML",
                                "Welcome", "Java", "Android", "Lucene", "C++",
                                "C#", "HTML"))));
    }

}

MainActivity代码也很简单,初始化两个控件,设置了下ScrollView的Callback。



好了,好几天没写代码了,就这样吧。


源码点击下载








转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/28441197

现在很多APP都给ScrollView添加了反弹效果,QQ、小米私密短信等。恰好在网上看到一个类:BounceScrollView ,

原创地址是:http://blog.csdn.NET/h7870181/article/details/8960430 , 可惜作者没有提供一个效果图,于是我发现小米短信列表页往下拉,有反弹效果,且拉到1/3以上时,会打开私密短信列表,小米的用户可以试试。

我在作者BounceScrollView 类的基础上修改了一下,写了一个例子,给大家分享下。

效果图:(模拟器的效果不佳,凑合可以看出来效果)

ScrollView反弹效果 仿小米私密短信效果


1、首先是布局文件:

  1. <RelativeLayout xmlns:android=“http://schemas.android.com/apk/res/android”  
  2.     xmlns:tools=“http://schemas.android.com/tools”  
  3.     android:layout_width=“match_parent”  
  4.     android:layout_height=“match_parent”  
  5.   >  
  6.   
  7.     <com.example.zhy_bouncescrollview02.BounceScrollView  
  8.         android:background=“@drawable/shakehideimg_man”  
  9.         android:layout_width=“fill_parent”  
  10.         android:layout_height=“fill_parent”  
  11.         android:id=“@+id/id_scrollView” >  
  12.   
  13.         <com.example.zhy_bouncescrollview02.MyListView  
  14.             android:background=“#fff”  
  15.             android:id=“@+id/id_listView”  
  16.             android:layout_width=“fill_parent”  
  17.             android:layout_height=“wrap_content” >  
  18.         </com.example.zhy_bouncescrollview02.MyListView>  
  19.     </com.example.zhy_bouncescrollview02.BounceScrollView>  
  20.   
  21. </RelativeLayout>  
<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"
  >

    <com.example.zhy_bouncescrollview02.BounceScrollView
        android:background="@drawable/shakehideimg_man"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:id="@+id/id_scrollView" >

        <com.example.zhy_bouncescrollview02.MyListView
            android:background="#fff"
            android:id="@+id/id_listView"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content" >
        </com.example.zhy_bouncescrollview02.MyListView>
    </com.example.zhy_bouncescrollview02.BounceScrollView>

</RelativeLayout>

一个自定义的ScrollView内部包含着一个自定义的ListView,自定义ScrollView是为了添加反弹效果,自定义ListView是为了解决ScrollView和ListView嵌套产生的问题。

2、BounceScrollView

  1. package com.example.zhy_bouncescrollview02;  
  2.   
  3. import android.content.Context;  
  4. import android.graphics.Rect;  
  5. import android.util.AttributeSet;  
  6. import android.view.MotionEvent;  
  7. import android.view.View;  
  8. import android.view.animation.Animation;  
  9. import android.view.animation.TranslateAnimation;  
  10. import android.widget.ScrollView;  
  11.   
  12. /** 
  13.  * 支持上下反弹效果的ScrollView 
  14.  *  
  15.  * @author zhy 
  16.  *  
  17.  */  
  18. public class BounceScrollView extends ScrollView  
  19. {  
  20.   
  21.     private boolean isCalled ;   
  22.       
  23.     private Callback mCallback;  
  24.   
  25.     /** 
  26.      * 包含的View 
  27.      */  
  28.     private View mView;  
  29.     /** 
  30.      * 存储正常时的位置 
  31.      */  
  32.     private Rect mRect = new Rect();  
  33.   
  34.     /** 
  35.      * y坐标 
  36.      */  
  37.     private int y;  
  38.   
  39.     private boolean isFirst = true;  
  40.   
  41.     public BounceScrollView(Context context, AttributeSet attrs)  
  42.     {  
  43.         super(context, attrs);  
  44.     }  
  45.   
  46.     /*** 
  47.      * 根据 XML 生成视图工作完成.该函数在生成视图的最后调用,在所有子视图添加完之后. 即使子类覆盖了 onFinishInflate 
  48.      * 方法,也应该调用父类的方法,使该方法得以执行. 
  49.      */  
  50.     @Override  
  51.     protected void onFinishInflate()  
  52.     {  
  53.         if (getChildCount() > 0)  
  54.             mView = getChildAt(0);  
  55.         super.onFinishInflate();  
  56.     }  
  57.   
  58.     @Override  
  59.     public boolean onTouchEvent(MotionEvent ev)  
  60.     {  
  61.         if (mView != null)  
  62.         {  
  63.             commonOnTouch(ev);  
  64.         }  
  65.   
  66.         return super.onTouchEvent(ev);  
  67.     }  
  68.   
  69.     private void commonOnTouch(MotionEvent ev)  
  70.     {  
  71.         int action = ev.getAction();  
  72.         int cy = (int) ev.getY();  
  73.         switch (action)  
  74.         {  
  75.         case MotionEvent.ACTION_DOWN:  
  76.             break;  
  77.         /** 
  78.          * 跟随手指移动 
  79.          */  
  80.         case MotionEvent.ACTION_MOVE:  
  81.   
  82.             int dy = cy - y;  
  83.             if (isFirst)  
  84.             {  
  85.                 dy = 0;  
  86.                 isFirst = false;  
  87.             }  
  88.             y = cy;  
  89.   
  90.             if (isNeedMove())  
  91.             {  
  92.                 if (mRect.isEmpty())  
  93.                 {  
  94.                     /** 
  95.                      * 记录移动前的位置 
  96.                      */  
  97.                     mRect.set(mView.getLeft(), mView.getTop(),  
  98.                             mView.getRight(), mView.getBottom());  
  99.                 }  
  100.   
  101.                 mView.layout(mView.getLeft(), mView.getTop() + 2 * dy / 3,  
  102.                         mView.getRight(), mView.getBottom() + 2 * dy / 3);  
  103.   
  104.                 if (shouldCallBack(dy))  
  105.                 {  
  106.                     if (mCallback != null)  
  107.                     {  
  108.                         if(!isCalled)  
  109.                         {  
  110.                             isCalled = true ;   
  111.                             resetPosition();  
  112.                             mCallback.callback();  
  113.                               
  114.                               
  115.                         }  
  116.                     }  
  117.                 }  
  118.             }  
  119.   
  120.             break;  
  121.         /** 
  122.          * 反弹回去 
  123.          */  
  124.         case MotionEvent.ACTION_UP:  
  125.             if (!mRect.isEmpty())  
  126.             {  
  127.                 resetPosition();  
  128.             }  
  129.             break;  
  130.   
  131.         }  
  132.     }  
  133.   
  134.     /** 
  135.      * 当从上往下,移动距离达到一半时,回调接口 
  136.      *  
  137.      * @return 
  138.      */  
  139.     private boolean shouldCallBack(int dy)  
  140.     {  
  141.   
  142.         if (dy > 0 && mView.getTop() > getHeight() / 2)  
  143.             return true;  
  144.         return false;  
  145.     }  
  146.   
  147.     private void resetPosition()  
  148.     {  
  149.         Animation animation = new TranslateAnimation(00, mView.getTop(),  
  150.                 mRect.top);  
  151.         animation.setDuration(200);  
  152.         animation.setFillAfter(true);  
  153.         mView.startAnimation(animation);  
  154.         mView.layout(mRect.left, mRect.top, mRect.right, mRect.bottom);  
  155.         mRect.setEmpty();  
  156.         isFirst = true;  
  157.         isCalled = false ;   
  158.     }  
  159.   
  160.     /*** 
  161.      * 是否需要移动布局 inner.getMeasuredHeight():获取的是控件的总高度 
  162.      *  
  163.      * getHeight():获取的是屏幕的高度 
  164.      *  
  165.      * @return 
  166.      */  
  167.     public boolean isNeedMove()  
  168.     {  
  169.         int offset = mView.getMeasuredHeight() - getHeight();  
  170.         int scrollY = getScrollY();  
  171.         // 0是顶部,后面那个是底部  
  172.         if (scrollY == 0 || scrollY == offset)  
  173.         {  
  174.             return true;  
  175.         }  
  176.         return false;  
  177.     }  
  178.   
  179.     public void setCallBack(Callback callback)  
  180.     {  
  181.         mCallback = callback;  
  182.     }  
  183.   
  184.     interface Callback  
  185.     {  
  186.         void callback();  
  187.     }  
  188.   
  189. }  
package com.example.zhy_bouncescrollview02;

import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.TranslateAnimation;
import android.widget.ScrollView;

/**
 * 支持上下反弹效果的ScrollView
 * 
 * @author zhy
 * 
 */
public class BounceScrollView extends ScrollView
{

    private boolean isCalled ; 

    private Callback mCallback;

    /**
     * 包含的View
     */
    private View mView;
    /**
     * 存储正常时的位置
     */
    private Rect mRect = new Rect();

    /**
     * y坐标
     */
    private int y;

    private boolean isFirst = true;

    public BounceScrollView(Context context, AttributeSet attrs)
    {
        super(context, attrs);
    }

    /***
     * 根据 XML 生成视图工作完成.该函数在生成视图的最后调用,在所有子视图添加完之后. 即使子类覆盖了 onFinishInflate
     * 方法,也应该调用父类的方法,使该方法得以执行.
     */
    @Override
    protected void onFinishInflate()
    {
        if (getChildCount() > 0)
            mView = getChildAt(0);
        super.onFinishInflate();
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev)
    {
        if (mView != null)
        {
            commonOnTouch(ev);
        }

        return super.onTouchEvent(ev);
    }

    private void commonOnTouch(MotionEvent ev)
    {
        int action = ev.getAction();
        int cy = (int) ev.getY();
        switch (action)
        {
        case MotionEvent.ACTION_DOWN:
            break;
        /**
         * 跟随手指移动
         */
        case MotionEvent.ACTION_MOVE:

            int dy = cy - y;
            if (isFirst)
            {
                dy = 0;
                isFirst = false;
            }
            y = cy;

            if (isNeedMove())
            {
                if (mRect.isEmpty())
                {
                    /**
                     * 记录移动前的位置
                     */
                    mRect.set(mView.getLeft(), mView.getTop(),
                            mView.getRight(), mView.getBottom());
                }

                mView.layout(mView.getLeft(), mView.getTop() + 2 * dy / 3,
                        mView.getRight(), mView.getBottom() + 2 * dy / 3);

                if (shouldCallBack(dy))
                {
                    if (mCallback != null)
                    {
                        if(!isCalled)
                        {
                            isCalled = true ; 
                            resetPosition();
                            mCallback.callback();


                        }
                    }
                }
            }

            break;
        /**
         * 反弹回去
         */
        case MotionEvent.ACTION_UP:
            if (!mRect.isEmpty())
            {
                resetPosition();
            }
            break;

        }
    }

    /**
     * 当从上往下,移动距离达到一半时,回调接口
     * 
     * @return
     */
    private boolean shouldCallBack(int dy)
    {

        if (dy > 0 && mView.getTop() > getHeight() / 2)
            return true;
        return false;
    }

    private void resetPosition()
    {
        Animation animation = new TranslateAnimation(0, 0, mView.getTop(),
                mRect.top);
        animation.setDuration(200);
        animation.setFillAfter(true);
        mView.startAnimation(animation);
        mView.layout(mRect.left, mRect.top, mRect.right, mRect.bottom);
        mRect.setEmpty();
        isFirst = true;
        isCalled = false ; 
    }

    /***
     * 是否需要移动布局 inner.getMeasuredHeight():获取的是控件的总高度
     * 
     * getHeight():获取的是屏幕的高度
     * 
     * @return
     */
    public boolean isNeedMove()
    {
        int offset = mView.getMeasuredHeight() - getHeight();
        int scrollY = getScrollY();
        // 0是顶部,后面那个是底部
        if (scrollY == 0 || scrollY == offset)
        {
            return true;
        }
        return false;
    }

    public void setCallBack(Callback callback)
    {
        mCallback = callback;
    }

    interface Callback
    {
        void callback();
    }

}

主要就是监听onTouchEvent,当MOVE时,ScrollView中的控件跟随手指移动,UP时恢复原来的位置;当达到1/2时,会调用用户设置的回调,细节就自己看代码了。

3、MyListView

  1. package com.example.zhy_bouncescrollview02;  
  2.   
  3. import android.content.Context;  
  4. import android.util.AttributeSet;  
  5. import android.widget.ListView;  
  6.   
  7. /** 
  8.  * 解决ScrollView与ListView的嵌套问题 
  9.  * @author zhy 
  10.  *  
  11.  */  
  12. public class MyListView extends ListView  
  13. {  
  14.   
  15.     public MyListView(Context context, AttributeSet attrs)  
  16.     {  
  17.         super(context, attrs);  
  18.     }  
  19.   
  20.     public MyListView(Context context, AttributeSet attrs, int defStyle)  
  21.     {  
  22.         super(context, attrs, defStyle);  
  23.     }  
  24.   
  25.     public MyListView(Context context)  
  26.     {  
  27.         super(context);  
  28.     }  
  29.   
  30.     @Override  
  31.     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)  
  32.     {  
  33.   
  34.         /** 
  35.          * 解决ScrollView与ListView的嵌套问题 
  36.          */  
  37.         int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,  
  38.                 MeasureSpec.AT_MOST);  
  39.   
  40.         super.onMeasure(widthMeasureSpec, expandSpec);  
  41.     }  
  42.   
  43. }  
package com.example.zhy_bouncescrollview02;

import android.content.Context;
import android.util.AttributeSet;
import android.widget.ListView;

/**
 * 解决ScrollView与ListView的嵌套问题
 * @author zhy
 * 
 */
public class MyListView extends ListView
{

    public MyListView(Context context, AttributeSet attrs)
    {
        super(context, attrs);
    }

    public MyListView(Context context, AttributeSet attrs, int defStyle)
    {
        super(context, attrs, defStyle);
    }

    public MyListView(Context context)
    {
        super(context);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
    {

        /**
         * 解决ScrollView与ListView的嵌套问题
         */
        int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,
                MeasureSpec.AT_MOST);

        super.onMeasure(widthMeasureSpec, expandSpec);
    }

}

4、主Activity

  1. package com.example.zhy_bouncescrollview02;  
  2.   
  3. import java.util.ArrayList;  
  4. import java.util.Arrays;  
  5.   
  6. import android.app.Activity;  
  7. import android.content.Intent;  
  8. import android.os.Bundle;  
  9. import android.widget.ArrayAdapter;  
  10. import android.widget.ListView;  
  11. import android.widget.Toast;  
  12.   
  13. import com.example.zhy_bouncescrollview02.BounceScrollView.Callback;  
  14.   
  15. public class MainActivity extends Activity  
  16. {  
  17.   
  18.     private ListView mListView;  
  19.     private BounceScrollView mScrollView;  
  20.   
  21.     @Override  
  22.     protected void onCreate(Bundle savedInstanceState)  
  23.     {  
  24.         super.onCreate(savedInstanceState);  
  25.         setContentView(R.layout.activity_main);  
  26.         mScrollView = (BounceScrollView) findViewById(R.id.id_scrollView);  
  27.         mScrollView.setCallBack(new Callback()  
  28.         {  
  29.   
  30.             @Override  
  31.             public void callback()  
  32.             {  
  33.                 Toast.makeText(MainActivity.this“you can do something!”0)  
  34.                         .show();  
  35.                 Intent intent = new Intent(MainActivity.this,  
  36.                         SecondActivity.class);  
  37.                 startActivity(intent);  
  38.                 overridePendingTransition(R.anim.fade_in, R.anim.fade_out);  
  39.             }  
  40.         });  
  41.         mListView = (ListView) findViewById(R.id.id_listView);  
  42.         mListView.setAdapter(new ArrayAdapter<String>(this,  
  43.                 android.R.layout.simple_list_item_1, new ArrayList<String>(  
  44.                         Arrays.asList(”Hello”“World”“Welcome”“Java”,  
  45.                                 ”Android”“Lucene”“C++”“C#”“HTML”,  
  46.                                 ”Welcome”“Java”“Android”“Lucene”“C++”,  
  47.                                 ”C#”“HTML”))));  
  48.     }  
  49.   
  50. }  
package com.example.zhy_bouncescrollview02;

import java.util.ArrayList;
import java.util.Arrays;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;

import com.example.zhy_bouncescrollview02.BounceScrollView.Callback;

public class MainActivity extends Activity
{

    private ListView mListView;
    private BounceScrollView mScrollView;

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mScrollView = (BounceScrollView) findViewById(R.id.id_scrollView);
        mScrollView.setCallBack(new Callback()
        {

            @Override
            public void callback()
            {
                Toast.makeText(MainActivity.this, "you can do something!", 0)
                        .show();
                Intent intent = new Intent(MainActivity.this,
                        SecondActivity.class);
                startActivity(intent);
                overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
            }
        });
        mListView = (ListView) findViewById(R.id.id_listView);
        mListView.setAdapter(new ArrayAdapter<String>(this,
                android.R.layout.simple_list_item_1, new ArrayList<String>(
                        Arrays.asList("Hello", "World", "Welcome", "Java",
                                "Android", "Lucene", "C++", "C#", "HTML",
                                "Welcome", "Java", "Android", "Lucene", "C++",
                                "C#", "HTML"))));
    }

}

MainActivity代码也很简单,初始化两个控件,设置了下ScrollView的Callback。



好了,好几天没写代码了,就这样吧。


源码点击下载