Android果冻效果滑动控件

类似于iOS有阻尼效果的滑动控件,一般我们比较亲切地称之为果冻控件

比较常见的地方,如微信里[我]的那个面板模块,即使没有再多的选项,也不会很生硬的不允许用户滑动。

微信是的处理方法是让用户滑动,但最终还是回滚到最初的地方,这样的效果很生动(毕竟成功还是取决于细节)。那么在安卓我们要怎么弄呢。下面为大家介绍一下JellyScrollView,是我继承ScrollView的一个有阻尼的效果的果冻滑动控件。废话少说,看图…

这里写图片描述

(在虚拟机或者真机跑起来是很流畅,可能是录制视频做成gif的时候有点卡顿。)
实现原理:其实只需要重写下它的拦截方法的逻辑就好了,ScrollView的拦截方法onInterceptTouchEvent一般情况下都默认地返回false,表示不拦截该事件。(如果不了解事件机制的同学先补补基础touch事件的分发和消费机制)我们只需要在用户滑动了界面的情况下,拦截下来并移动布局就好了,当用户松开手就恢复布局。很简单

第一步:集成ScrollView重写几个构造方法;
第二步:重写下onFinishInflate方法,获得第一个子view;
第三步:在拦截方法里面处理上面所说的逻辑;

public class JellyScrollView extends ScrollView { 
          private View inner;// 子View 
          private float y;// 点击时y坐标 
          private Rect normal = new Rect();// 矩形(这里只是个形式,只是用于判断是否需要动画.) 
          private boolean isCount = false;// 是否开始计算 
          private boolean isMoving = false;// 是否开始移动. 
          private int top;// 拖动时时高度。 
          private int mTouchSlop;//系统最少滑动距离 

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

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

          public JellyScrollView(Context context, AttributeSet attrs, int defStyleAttr) { 
                  super(context, attrs, defStyleAttr); 
                  mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
         } 

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

          /**拦截事件*/ 
        @Override public boolean 
        onInterceptTouchEvent(MotionEvent ev) { 
              if (inner != null) { 
                  int action = ev.getAction(); 
                  switch (action) { 
                      case MotionEvent.ACTION_DOWN: 
                        y = ev.getY(); 
                        top = 0; 
                      break;

                      case MotionEvent.ACTION_UP: // 手指松开. 
                         isMoving = false; 
                         if (isNeedAnimation()) { animation(); } 
                      break; 

                    /*** * 排除出第一次移动计算,因为第一次无法得知y坐标, 在MotionEvent.ACTION_DOWN中获取不到, * 因为此时是ScrollView的touch事件传递到到了ListView的子item上面.所以从第二次计算开始. * 然而我们也要进行初始化,就是第一次移动的时候让滑动距离归0. 之后记录准确了就正常执行. */ 
                      case MotionEvent.ACTION_MOVE: 
                        final float preY = y;// 按下时的y坐标 
                        float nowY = ev.getY();// 每时刻y坐标 
                        int deltaY = (int) (nowY - preY);// 滑动距离 
                        if (!isCount) { deltaY = 0; // 在这里要归0. } 
                        if (Math.abs(deltaY) < mTouchSlop && top <= 0) return true; // 当滚动到最上或者最下时就不会再滚动,这时移动布局 isNeedMove(); 
                        
                        if (isMoving) { 
                          // 初始化头部矩形 
                          if (normal.isEmpty()) { 
                                // 保存正常的布局位置 
                                normal.set(inner.getLeft(), inner.getTop(), inner.getRight(), inner.getBottom()); 
                          } 

                          // 移动布局 
                          inner.layout(inner.getLeft(), inner.getTop() + deltaY / 3, inner.getRight(), inner.getBottom() + deltaY / 3); 
                          top += (deltaY / 6); 
                         } 
                        isCount = true; 
                        y = nowY; 
                      break; 
                  } 
            } 

            return super.onInterceptTouchEvent(ev); 
         } 

         /*** * 回缩动画 */ 
         public void animation() { 
              // 开启移动动画 
              TranslateAnimation ta = new TranslateAnimation(0, 0, inner.getTop(), normal.top);     
              ta.setDuration(200);
              inner.startAnimation(ta); 
              // 设置回到正常的布局位置 
              inner.layout(normal.left, normal.top, normal.right, normal.bottom); normal.setEmpty(); 
              // 手指松开要归0. 
              isCount = false; 
              y = 0; 
         } 


        // 是否需要开启动画 
        public boolean isNeedAnimation() { 
              return !normal.isEmpty(); 
        } 

        /***
        * 是否需要移动布局 * 
        * inner.getMeasuredHeight():获取的是控件的总高度 * 
        * getHeight():获取的是屏幕的高度*/ 
        public void isNeedMove() { 
              int offset = inner.getMeasuredHeight() - getHeight(); 
              int scrollY = getScrollY(); 
              // scrollY == 0是顶部 // scrollY == offset是底部 
              if (scrollY == 0 || scrollY == offset) { isMoving = true; } 
        }
}

然后在布局里面在最外层就使用我们的JellyScrollView(为了方便展示,我只是大概写了一部分布局代码)

<?xml version="1.0" encoding="utf-8"?>
<cn.ichengxi.fang.view.JellyScrollView 
    xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:background="@color/bg" 
    android:scrollbars="none"> 
      <LinearLayout 
        android:layout_width="match_parent" 
        android:layout_height="wrap_content" 
        android:orientation="vertical"> 
          <TextView 
            android:id="@+id/personal_setting_txt" 
            android:layout_width="match_parent" 
            android:layout_height="wrap_content" 
            android:alpha="0.8" 
            android:gravity="center_vertical" 
            android:text="设置" 
            android:textColor="@android:color/black" /> 
      </LinearLayout>
</cn.ichengxi.fang.view.JellyScrollView>```
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,172评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,346评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,788评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,299评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,409评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,467评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,476评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,262评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,699评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,994评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,167评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,827评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,499评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,149评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,387评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,028评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,055评论 2 352

推荐阅读更多精彩内容