转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/28441197
现在很多APP都给ScrollView添加了反弹效果,QQ、小米私密短信等。恰好在网上看到一个类:BounceScrollView ,
原创地址是:http://blog.csdn.net/h7870181/article/details/8960430 , 可惜作者没有提供一个效果图,于是我发现小米短信列表页往下拉,有反弹效果,且拉到1/3以上时,会打开私密短信列表,小米的用户可以试试。
我在作者BounceScrollView 类的基础上修改了一下,写了一个例子,给大家分享下。
效果图:(模拟器的效果不佳,凑合可以看出来效果)
1、首先是布局文件:
<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
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
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
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。
好了,好几天没写代码了,就这样吧。