文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Android ReboundScrollView仿IOS拖拽回弹效果

2022-06-06 06:55

关注

初衷:

其实github上有很多这种ScrollView的项目,但是不得不说功能太多太乱了,我就只是想要一个简单效果的ScrollView,另外监听下滑动距离而已,想想还是自己写了个。

这里先说下思路吧,如果不愿意看的朋友可以直接跳过这一步,看下面的代码:

Android 原生的ScrollView是不支持拉出屏幕外,并且也没有回弹效果的,用户友好度却不不太好,不知道为什么不那么设计。
我想做的事情正如上面所述:

1.希望能拉出屏幕外
2.松手后希望控件回弹

我的思路是对ScrollView的子View进行操作

所有View的滑动控制肯定都受着onTouchEvent控制,所以,理所应当的,我要关注的重点,也就是onTouchEvent这个方法。

回弹的效果,就牵涉到位置的计算,这里我的想法就用简单的TranslateAnimation来实现。

大体思路就是这样了,先把思路确定了,然后代码总是磨出来的。

贴代码:


import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.TranslateAnimation;
import android.widget.ScrollView;
public class ReboundScrollView extends ScrollView {
  private static final float MOVE_DELAY = 0.3f;//当拉出屏幕时的拖拽系数
  private static final int ANIM_TIME = 300;//回弹耗时
  private static final int FLING = 2;//fling 系数
  private View childView;
  private boolean havaMoved;
  private Rect originalRect = new Rect();
  private float startY;
  @Override
  protected void onFinishInflate() {
    super.onFinishInflate();
    if (getChildCount() > 0) {
      childView = getChildAt(0);
    }
  }
  @Override
  public void fling(int velocityY) {
    super.fling(velocityY / 2);
  }
  @Override
  protected void onLayout(boolean changed, int l, int t, int r, int b) {
    super.onLayout(changed, l, t, r, b);
    if (childView == null)
      return;
    originalRect.set(childView.getLeft(), childView.getTop(),
        childView.getRight(), childView.getBottom());
  }
  public ReboundScrollView(Context context, AttributeSet attrs,
               int defStyle) {
    super(context, attrs, defStyle);
  }
  public ReboundScrollView(Context context, AttributeSet attrs) {
    super(context, attrs);
  }
  public ReboundScrollView(Context context) {
    super(context);
  }
  
  @Override
  public boolean dispatchTouchEvent(MotionEvent ev) {
    if (childView == null) {
      return super.dispatchTouchEvent(ev);
    }
    int action = ev.getAction();
    switch (action) {
      case MotionEvent.ACTION_DOWN:
        startY = ev.getY();
        break;
      case MotionEvent.ACTION_UP:
      case MotionEvent.ACTION_CANCEL:
        if (!havaMoved)
          break;
        TranslateAnimation anim = new TranslateAnimation(0, 0,
            childView.getTop(), originalRect.top);
        anim.setDuration(ANIM_TIME);
        childView.startAnimation(anim);
        // 将标志位设回false
        havaMoved = false;
        resetViewLayout();
        break;
      case MotionEvent.ACTION_MOVE:
        float nowY = ev.getY();
        int deltaY = (int) (nowY - startY);
        int offset = (int) (deltaY * MOVE_DELAY);
        childView.layout(originalRect.left, originalRect.top + offset,
            originalRect.right, originalRect.bottom + offset);
        havaMoved = true;
        break;
      default:
        break;
    }
    return super.dispatchTouchEvent(ev);
  }
  public void resetViewLayout() {
    childView.layout(originalRect.left, originalRect.top,
        originalRect.right, originalRect.bottom);
  }
}

把代码贴出来后,再来分析具体的实现:
首先是拉出屏幕,在MOVE的过程中,对于超出部分代码,我使用layout重置子View的位置。

第二个要实现的就是回弹了,拖出去总是要回来的:
这里我定义了一个Rect,在onLayout(boolean changed, int l, int t, int r, int b)方法中,记录下了ScrollView的初始位置,以便于重置回弹。

说了许多,看一下代码里的关键代码
MOVE的代码:


 float nowY = ev.getY();
        int deltaY = (int) (nowY - startY);
        int offset = (int) (deltaY * MOVE_DELAY);
        childView.layout(originalRect.left, originalRect.top + offset,
            originalRect.right, originalRect.bottom + offset);
        havaMoved = true;

这是MotionEvent.ACTION_MOVE的时候要做的,对chlidView的位置重新设置也就是lauout方法,这是基于originalRect的值来的,设定了相应的滑动系数,不然感觉实在是太灵敏了。

回弹的代码:


 if (!havaMoved)
          break;
        TranslateAnimation anim = new TranslateAnimation(0, 0,
            childView.getTop(), originalRect.top);
        anim.setDuration(ANIM_TIME);
        childView.startAnimation(anim);
        // 将标志位设回false
        havaMoved = false;
        resetViewLayout();
...
...
 public void resetViewLayout() {
    childView.layout(originalRect.left, originalRect.top,
        originalRect.right, originalRect.bottom);
  }

当手指 MotionEvent.ACTION_UP或者MotionEvent.ACTION_CANCEL的时候把clildView置于原位,然后设置动画,这就是回弹了。

补充:

这是个非常简单的回弹ScrollView,需要注意的是,我这里没有对在屏幕内正常移动和屏幕外移动作区分,那样就需要判断当前的移动是否需要我们重写了,也就是是否处于上拉,下拉的部分。没有做判断,我现在的代码就会导致你手势移动的距离和控件滚动的距离是不一样的。我是感觉如果你正常使用是不会注意到这一点的。但是不排除有这种需求。

下拉其实不太好判断,因为我这里操作的是ChildView,ScrollView在上拉过程中getScrollY()肯定是0,所以这一点,我也还没想好。不过上拉倒是比较简单。当childView.height <= ScrollView.height + getScrollY的时候就是上拉出屏幕的点了,这个应该能想通吧。

git地址:https://github.com/cjhandroid/ReboundScrollView

源码下载:http://xiazai.jb51.net/201611/yuanma/androidReboundScrollView(jb51.net).rar

您可能感兴趣的文章:IOS Xib控件拖拽与页面跳转实例iOS实现百度地图拖拽后更新位置以及反编码浅谈iOS11新特性:新增拖拽交互体验IOS使用UICollectionView实现无限轮播效果iOS 解决UICollectionView 计算 Cell 大小的问题IOS 自定义UICollectionView的头视图或者尾视图UICollectionReusableViewiOS应用中UICollectionViewCell定制ButtonIOS简单实现瀑布流UICollectionViewiOS中关于Swift UICollectionView横向分页的问题iOS开发UICollectionView实现拖拽效果


阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     807人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     351人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     314人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     433人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     221人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-移动开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯