ScrollView 反弹效果

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

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

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

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

 

布局文件main.xml:

<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.topcsa.test_android3.BounceScrollView
        android:id="@+id/id_scrollView"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:background="@drawable/ic_launcher" >

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

</RelativeLayout>

BounceScrollView.java:

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

MyListView.java:

package com.topcsa.test_android3;

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

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

MainActivity.java:

package com.topcsa.test_android3;

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

import com.topcsa.test_android3.BounceScrollView.Callback;

import android.app.Activity;
import android.app.ActionBar;
import android.app.Fragment;
import android.content.Intent;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;
import android.os.Build;

public class MainActivity extends Activity {
    private ListView mListView;
    private BounceScrollView mScrollView;

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

            public void callback() {
                Toast.makeText(MainActivity.this, "you can do something!", 0)
                        .show();
            }
        });
        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"))));
    }

}

运行即可。

源码下载地址:源码下载

 本文转载自:http://blog.csdn.net/lmj623565791/article/details/28441197

posted @ 2014-08-04 20:20  银色的流星  阅读(187)  评论(0编辑  收藏  举报