• ScrollView的顶部下拉和底部上拉回弹效果


    要实现ScrollView的回弹效果,需要对其进行触摸事件处理。先来看一下简单的效果:

    根据Android的View事件分发处理机制,下面对dispatchTouchEvent进行详细分析:

    在加载布局完成之后,获取ScrollView的第一个子元素,保存它的参数,left top right bottom参数,根据顶部下拉操作和底部上拉操作进行子View的布局参数根据滑动距离改变,ACTION_UP的时候判断是否存在回弹,如果需要则进行动画回弹到原来的位置,可以添加一个回弹结束监听,比如监听回弹处理跳转到其他的页面的操作等。

    具体的实现如下,添加了是否禁用顶部和底部回弹的参数设置,以及回弹效果结束监听。

    /**
     * A Simple Rebound ScrollView
     * @author Denluoyia
     */
    public class ReboundScrollView extends ScrollView{
    
        private boolean mEnableTopRebound = true;
        private boolean mEnableBottomRebound = true;
        private OnReboundEndListener mOnReboundEndListener;
        private View mContentView;
        private Rect mRect = new Rect();
    
        public ReboundScrollView(Context context) {
            super(context);
        }
    
        public ReboundScrollView(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        public ReboundScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
        }
    
        /** after inflating view, we can get the width and height of view */
        @Override
        protected void onFinishInflate() {
            super.onFinishInflate();
            mContentView = getChildAt(0);
        }
    
        @Override
        protected void onLayout(boolean changed, int l, int t, int r, int b) {
            super.onLayout(changed, l, t, r, b);
            if (mContentView == null) return;
            // to remember the location of mContentView
            mRect.set(mContentView.getLeft(), mContentView.getTop(), mContentView.getRight(), mContentView.getBottom());
        }
    
        public ReboundScrollView setOnReboundEndListener(OnReboundEndListener onReboundEndListener){
            this.mOnReboundEndListener = onReboundEndListener;
            return this;
    
        }
    
        public ReboundScrollView setEnableTopRebound(boolean enableTopRebound){
            this.mEnableTopRebound = enableTopRebound;
            return this;
        }
    
        public ReboundScrollView setEnableBottomRebound(boolean mEnableBottomRebound){
            this.mEnableBottomRebound = mEnableBottomRebound;
            return this;
        }
    
        private int lastY;
        private boolean rebound = false;
        private int reboundDirection = 0; //<0 表示下部回弹  >0 表示上部回弹 0表示不回弹
    
        @Override
        public boolean dispatchTouchEvent(MotionEvent ev) {
            if (mContentView == null){
                return super.dispatchTouchEvent(ev);
            }
            switch (ev.getAction()){
                case MotionEvent.ACTION_DOWN:
                    lastY = (int) ev.getY();
                    break;
    
                case MotionEvent.ACTION_MOVE:
                    if (!isScrollToTop() && !isScrollToBottom()){
                        lastY = (int) ev.getY();
                        break;
                    }
                    //处于顶部或者底部
                    int deltaY = (int) (ev.getY() - lastY);
                    //deltaY > 0 下拉  deltaY < 0 上拉
    
    
                    //disable top or bottom rebound
                    if ((!mEnableTopRebound && deltaY > 0) || (!mEnableBottomRebound && deltaY < 0)){
                        break;
                    }
    
                    int offset = (int) (deltaY * 0.48);
                    mContentView.layout(mRect.left, mRect.top + offset, mRect.right, mRect.bottom + offset);
                    rebound = true;
                    break;
    
                case MotionEvent.ACTION_UP:
                    if (!rebound) break;
                    reboundDirection = mContentView.getTop() - mRect.top;
                    TranslateAnimation animation = new TranslateAnimation(0, 0, mContentView.getTop(), mRect.top);
                    animation.setDuration(300);
                    animation.setAnimationListener(new Animation.AnimationListener() {
                        @Override
                        public void onAnimationStart(Animation animation) {
    
                        }
    
                        @Override
                        public void onAnimationEnd(Animation animation) {
                            if (mOnReboundEndListener != null){
                               if (reboundDirection > 0){
                                   mOnReboundEndListener.onReboundTopComplete();
                               }
                               if (reboundDirection < 0){
                                   mOnReboundEndListener.onReboundBottomComplete();
                               }
                               reboundDirection = 0;
                            }
                        }
    
                        @Override
                        public void onAnimationRepeat(Animation animation) {
    
                        }
                    });
                    mContentView.startAnimation(animation);
                    mContentView.layout(mRect.left, mRect.top, mRect.right, mRect.bottom);
                    rebound = false;
                    break;
            }
            return super.dispatchTouchEvent(ev);
        }
    
        @Override
        public void setFillViewport(boolean fillViewport) {
            super.setFillViewport(true); //默认是填充ScrollView 或者再XML布局文件中设置fillViewport属性
        }
    
        /**
         * 判断当前ScrollView是否处于顶部
         */
        private boolean isScrollToTop(){
            return getScrollY() == 0;
        }
    
        /**
         * 判断当前ScrollView是否已滑到底部
         */
        private boolean isScrollToBottom(){
            return mContentView.getHeight() <= getHeight() + getScrollY();
        }
    
        /**
         * listener for top and bottom rebound
         * do your implement in the following methods
         */
        public interface OnReboundEndListener{
    
            void onReboundTopComplete();
    
            void onReboundBottomComplete();
        }
    }

     使用:

    直接在XML布局文件中把ScrollView替换成ReboundScrollView就可以了。还可以拓展把回弹顶部和底部添加其他的动画效果(之后再拓展试下)。

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout 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"
        android:orientation="vertical"
        tools:context=".TestActivity">
    
        <com.denluoyia.dtils.widget.ReboundScrollView
            android:id="@+id/reboundScrollView"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
    
            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:background="#eefade"
                android:padding="16dp">
    
                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:textSize="15sp"
                    android:lineSpacingExtra="5dp"
                    android:text="@string/content"/>
            </LinearLayout>
        </com.denluoyia.dtils.widget.ReboundScrollView>
    
    </LinearLayout>

     如果需要禁用回弹,可以直接设置enableTopRebound和enableBottomRebound参数,同样设置回弹结束(或开始)监听。

    public class TestActivity extends AppCompatActivity {
    
        private ReboundScrollView reboundScrollView;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_test);
    
            reboundScrollView = findViewById(R.id.reboundScrollView);
            //reboundScrollView.setEnableTopRebound(false);
            //reboundScrollView.setEnableBottomRebound(false);
            reboundScrollView.setOnReboundEndListener(new ReboundScrollView.OnReboundEndListener() {
                @Override
                public void onReboundTopComplete() {
                    Toast.makeText(TestActivity.this, "顶部回弹", Toast.LENGTH_SHORT).show();
                }
    
                @Override
                public void onReboundBottomComplete() {
                    Toast.makeText(TestActivity.this, "底部回弹", Toast.LENGTH_SHORT).show();
                }
            });
        }
    }
  • 相关阅读:
    【git】git命令集合
    【maven】【IDEA】idea中使用maven编译项目,报错java: 错误: 找不到符号 【2】
    【mybatis】mybatis传参的几种方式
    【EasyExcel】使用easyExcel过程中,项目报错的解决集合
    【Maven】【IDEA】在idea中开发web项目,解决maven的jar包冲突的方法
    【Easyexcel】java导入导出超大数据量的xlsx文件 解决方法
    【maven】idea的pom文件修改,引入新的jar包,无效,本地仓库始终没有下载新jar包的问题解决【idea pom Dependency not found】
    【java】javac编译多个有依赖关系的java文件为class文件
    【java】javac命令在win10不可用,提示javac不是内部或外部命令,也不是可运行的程序【解决方法】
    【POI】java服务生成List数据集合,后台服务生成xlsx临时文件,并将临时文件上传到腾讯云上,并实现xlsx浏览器预览
  • 原文地址:https://www.cnblogs.com/denluoyia/p/8995589.html
Copyright © 2020-2023  润新知