Android自定义View--ScrollView实现回弹效果
2016-12-08 10:13
633 查看
需要实现的功能
当下滑或上滑到尽头时还能继续滑动,释放手指后能自动回弹到原来的位置需要用到的知识点
getScrollY()getScrollY是当前view的左上角相对于母视图(这里是ScrollView)的左上角的Y轴偏移量,上拉值增加,反之亦然。
getMeasuredHeight()
getHeight()
1.getMeasuredHeight()返回的是原始测量高度,与屏幕无关,getHeight()返回的是在屏幕上显示的高度,项目利用子View的getMeasuredHeight()减去父类(ScrollView)的高度,判断是否滑动到底部。
MyScrollView的实现
package rc.loveq.myscrollview; import android.content.Context; import android.graphics.Rect; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.view.animation.Animation; import android.view.animation.TranslateAnimation; import android.widget.ScrollView; /** * Author:Rc * Csdn:http://blog.csdn.net/loveqrc * 0n 2016/12/8 08:46 * Email:664215432@qq.com */ public class MyScrollView extends ScrollView { private static final String TAG ="MyScrollView" ; private static final long RESTORE_TIME = 300;//回弹的时间 public static final int DRAG_RATE=2;//mChildView移动的距离=手指移动的的距离/DRAG_RATE private View mChildView; private float mDownY; private Rect mRect=new Rect();//用于保存子View的位置 private boolean isRestoring=false;//是否正在回弹 public MyScrollView(Context context) { super(context); } public MyScrollView(Context context, AttributeSet attrs) { super(context, attrs); } public MyScrollView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override protected void onFinishInflate() { super.onFinishInflate(); if (getChildCount()>0){//保证要有子View mChildView = getChildAt(0); } } @Override public boolean onTouchEvent(MotionEvent ev) { if (!isRestoring){ handleTouchEvent(ev);//监听触摸事件 } //因为我们要做的是当scrollView下滑或上滑到尽头还能继续滑动 //所以只有这两种情况我们才自己处理 //其他情况交由父类默认实现 Log.e(TAG, "onTouchEvent: getScrollY:"+getScrollY()); return super.onTouchEvent(ev); } private void handleTouchEvent(MotionEvent ev) { switch (ev.getAction()){ case MotionEvent.ACTION_DOWN: mDownY = ev.getY(); break; case MotionEvent.ACTION_MOVE: float moveY = ev.getY(); int deltaY= (int) (moveY-mDownY); if (isNeedLoadMore()){ if (mRect.isEmpty()){//如果没有记录子view位置,那么我们记录 mRect.set(mChildView.getLeft(), mChildView.getTop() , mChildView.getRight(), mChildView.getBottom()); } mChildView.layout(mChildView.getLeft(), mChildView.getTop()+deltaY/DRAG_RATE, mChildView.getRight(), mChildView.getBottom()+deltaY/DRAG_RATE); } mDownY=moveY;//更新位置 break; case MotionEvent.ACTION_UP://当手指释放的时候,需要回弹到原来的位置 mDownY=0; if (isNeedRestore()){ restoreState(); } break; } } /** * 恢复到原来的位置 */ private void restoreState() { //TranslateAnimation移动的相对位置 TranslateAnimation ta=new TranslateAnimation(0,0,0,mRect.top- mChildView.getTop()); ta.setDuration(RESTORE_TIME); mChildView.startAnimation(ta); ta.setAnimationListener(new Animation.AnimationListener() { @Override public void onAnimationStart(Animation animation) { isRestoring=true; } @Override public void onAnimationEnd(Animation animation) { isRestoring=false; mChildView.clearAnimation(); //保证回到原来的位置 mChildView.layout(mRect.left,mRect.top,mRect.right,mRect.bottom); mRect.setEmpty(); } @Override public void onAnimationRepeat(Animation animation) { } }); } /** * 判断是否需要回弹 * @return */ private boolean isNeedRestore() { return !mRect.isEmpty();//如果保存过位置,说明需要回弹 } /** * 是否已经滑动尽头了 * @return */ private boolean isNeedLoadMore() { int offset = mChildView.getMeasuredHeight() - getHeight(); int scrollY = getScrollY(); if (scrollY==0||offset==scrollY){//当scrollY==0表示向下滑滑到尽头了 return true; // offset==scrollY 表示向上滑滑到尽头了 } return false; } }
布局代码
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" > <rc.loveq.myscrollview.MyScrollView android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <TextView android:layout_width="match_parent" android:layout_height="60dp" android:textSize="36sp" android:text="这是第1个数据"/> <TextView android:layout_width="match_parent" android:layout_height="60dp" android:textSize="36sp" android:text="这是第2个数据"/> <TextView android:layout_width="match_parent" android:layout_height="60dp" android:textSize="36sp" android:text="这是第3个数据"/> <TextView android:layout_width="match_parent" android:layout_height="60dp" android:textSize="36sp" android:text="这是第4个数据"/> <TextView android:layout_width="match_parent" android:layout_height="60dp" android:textSize="36sp" android:text="这是第5个数据"/> <TextView android:layout_width="match_parent" android:layout_height="60dp" android:textSize="36sp" android:text="这是第6个数据"/> <TextView android:layout_width="match_parent" android:layout_height="60dp" android:textSize="36sp" android:text="这是第7个数据"/> <TextView android:layout_width="match_parent" android:layout_height="60dp" android:textSize="36sp" android:text="这是第8个数据"/> <TextView android:layout_width="match_parent" android:layout_height="60dp" android:textSize="36sp" android:text="这是第9个数据"/> <TextView android:layout_width="match_parent" android:layout_height="60dp" android:textSize="36sp" android:text="这是第10个数据"/> </LinearLayout> </rc.loveq.myscrollview.MyScrollView> </RelativeLayout>
下载地址
相关文章推荐
- iTOP-4418开发板编译Android镜像
- Android开发TextView实现长按复制文本功能的方法
- View绘制流程以及自定义控件
- API 25 (Android 7.1.1 API) Manifest.permission_group——权限组
- Android中visibility属性VISIBLE、INVISIBLE、GONE的区别
- Android Studio "Manifest merger failed with multiple errors, see logs"
- 【Android】Android手机通过wifi进行数据传输
- Android AsyncTask异步任务(二)
- Android 5.1长按电源键添加重启功能
- MVC设计模式
- Android之尺寸getDimension、getDimensionPixelOffset 和 getDimensionPixelSize
- ANDROID STUDIO系列教程二--基本设置与运行
- Android 中onSaveInstanceState()解析
- Android Studio配置,加快编译速度
- Android之---RecycleView简单介绍(各种用法的简介)
- ANDROID STUDIO系列教程四--GRADLE基础
- Android 基于Socket的聊天应用实例(二)
- Android enum
- Android国际化
- Android makefile编写基础