热门标签 | HotTags
当前位置:  开发笔记 > Android > 正文

Android制作简单垂直上拉下滑View效果

这篇文章主要介绍了Android制作简单垂直上拉下滑View效果的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

一、简介

最近朋友公司需要实现一个垂直上拉下滑的View,该View最初只有一部分显示在屏幕最下方,上拉那一部分可以将该View全部拉出来并全部显示在屏幕上,下滑该View可以将该View隐藏在屏幕下。
先看一下最终实现效果吧。

二、实现思路

1、这个效果其实有很多实现方法,为了让松手时有一个viewpager一样的缓慢滑动的效果我选择用scrollBy配合Scroller,应该是既方便又实用的。
2、这个View的设计是这样的:
(1)将这个View的子view通过layout放在该View下面;
(2)通过重写onTouchEvent方法给这个子View滑动效果,在MOVE_UP的动作给这个子View加上Scroller平滑到View的顶部或者底部。
见图:

三、实现

1、先自定义一个属性,表示子View应该有多少部分露在外面,也就是上图中红色和绿色相交的部分。
在res文件夹-values文件夹下面创建一个attrs.xml文件

attrs.xml :


 
  
 


在XML文件中引用该属性:


在代码中调用该属性(该View名字为MyScrollerView,我图方便继承的是LinearLayout,继承ViewGroup或者其他的布局View都可以):

public MyScrollerView(Context context) {
  this(context,null);
 }

 public MyScrollerView(Context context, AttributeSet attrs) {
  this(context, attrs,0);
 }

 public MyScrollerView(Context context, AttributeSet attrs, int defStyleAttr) {
  super(context, attrs, defStyleAttr);

  TypedArray ta = context.obtainStyledAttributes(attrs,R.styleable.MyScrollerView);
  visibilityHeight = ta.getDimension(R.styleable.MyScrollerView_visibility_height,200);
  ta.recycle();

  init(context);
 }

2、重写onFinishInflate方法,重写该方法的原因是我希望我只有一个子View,这样就好确定滑动的高度,不然我还需要重新计算子View们的高度总和,比较麻烦。这个方法会在onMeasure之前调用。

 @Override
 protected void onFinishInflate() {
  super.onFinishInflate();
  if(getChildCount() == 0 || getChildAt(0) == null){
   throw new RuntimeException("没有子控件!");
  }
  if(getChildCount() > 1){
   throw new RuntimeException("只能有一个子控件!");
  }
  mChild = getChildAt(0);
 }

3、init方法里做一些初始化操作,比如说创建一个Scroller对象,给View的背景设为透明:

 private void init(Context context) {
  mScroller = new Scroller(context);
  this.setBackgroundColor(Color.TRANSPARENT);
 }

4、重写onMeasure方法和onLayout方法,确定可以滑动的最大高度,以及子View的排列位置(其实可以不用重写onMeasure,我这样写只是习惯)。

 @Override
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  mScrollHeight = (int) (mChild.getMeasuredHeight() - visibilityHeight);
 }

 @Override
 protected void onLayout(boolean changed, int l, int t, int r, int b) {
  super.onLayout(changed, l, t, r, b);
  mChild.layout(0,mScrollHeight,mChild.getMeasuredWidth(),mChild.getMeasuredHeight() + mScrollHeight);
 }

5、先看我定义的成员变量的含义吧:

 /**
  * downY:手指按下时距离View顶部的距离
  * moveY:手指在屏幕上滑动的距离(不停变化)
  * movedY:手指在屏幕上总共滑动的距离(为了确定手指一共滑动了多少距离,不能超过可滑动的最大距离)
  */
 private int downY,moveY,movedY;

 //子View
 private View mChild;

 private Scroller mScroller;

 //可滑动的最大距离
 private int mScrollHeight;

 //子View是否在顶部
 private boolean isTop = false;

 //最初子View在View内可见的高度
 private float visibilityHeight;

6、重写onTouchEvent方法,做滑动判断,解释都写在注释里了:

 @Override
 public boolean onTouchEvent(MotionEvent event) {
  switch (event.getAction()){
   case MotionEvent.ACTION_DOWN:
    //手指按下时距离View上面的距离
    downY = (int) event.getY();

    //如果子View不在顶部 && 按下的位置在子View没有显示的位置,则不消费此次滑动事件,否则消费
    if(!isTop && downY 0 ,向下滑时deY<0
    int deY = downY - moveY;

    //向上滑动时的处理
    if(deY > 0){
     //将每次滑动的距离相加,为了防止子View的滑动超过View的顶部
     movedY += deY;
     if(movedY > mScrollHeight) movedY = mScrollHeight;

     if(movedY  0){
      scrollBy(0,deY);
     }
     downY = moveY;
     return true;
    }

    break;
   case MotionEvent.ACTION_UP:
    //手指抬起时的处理,如果向上滑动的距离超过了最大可滑动距离的1/4,并且子View不在顶部,就表示想把它拉上去
    if(movedY > mScrollHeight / 4 && !isTop){
     mScroller.startScroll(0,getScrollY(),0,(mScrollHeight - getScrollY()));
     invalidate();
     movedY = mScrollHeight;
     isTop = true;
    }else {
     //否则就表示放弃本次滑动,让它滑到最初的位置
     mScroller.startScroll(0,getScrollY(),0, -getScrollY());
     postInvalidate();
     movedY = 0;
     isTop = false;
    }

    break;
  }
  return super.onTouchEvent(event);
 }

7、最后要重写一个computeScroll方法,该方法是用来配合scroller的:

 @Override
 public void computeScroll() {
  super.computeScroll();
  if(mScroller.computeScrollOffset()){
   scrollTo(0,mScroller.getCurrY());
   postInvalidate();
  }
 }

8、关于scroller的用法,可参考郭霖的这篇博客:http://blog.csdn.net/guolin_blog/article/details/48719871

四、完整代码:

xml:



  
   
    

MyScrollerView:

public class MyScrollerView extends LinearLayout {

 /**
  * downY:手指按下时距离View顶部的距离
  * moveY:手指在屏幕上滑动的距离(不停变化)
  * movedY:手指在屏幕上总共滑动的距离(为了确定手指一共滑动了多少距离,不能超过可滑动的最大距离)
  */
 private int downY,moveY,movedY;

 //子View
 private View mChild;

 private Scroller mScroller;

 //可滑动的最大距离
 private int mScrollHeight;

 //子View是否在顶部
 private boolean isTop = false;

 //最初子View在View内可见的高度
 private float visibilityHeight;

 public MyScrollerView(Context context) {
  this(context,null);
 }

 public MyScrollerView(Context context, AttributeSet attrs) {
  this(context, attrs,0);
 }

 public MyScrollerView(Context context, AttributeSet attrs, int defStyleAttr) {
  super(context, attrs, defStyleAttr);

  TypedArray ta = context.obtainStyledAttributes(attrs,R.styleable.MyScrollerView);
  visibilityHeight = ta.getDimension(R.styleable.MyScrollerView_visibility_height,200);
  ta.recycle();

  init(context);
 }

 @Override
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  mScrollHeight = (int) (mChild.getMeasuredHeight() - visibilityHeight);
 }

 @Override
 protected void onLayout(boolean changed, int l, int t, int r, int b) {
  super.onLayout(changed, l, t, r, b);
  mChild.layout(0,mScrollHeight,mChild.getMeasuredWidth(),mChild.getMeasuredHeight() + mScrollHeight);
 }

 private void init(Context context) {
  mScroller = new Scroller(context);
  this.setBackgroundColor(Color.TRANSPARENT);
 }

 @Override
 protected void onFinishInflate() {
  super.onFinishInflate();
  if(getChildCount() == 0 || getChildAt(0) == null){
   throw new RuntimeException("没有子控件!");
  }
  if(getChildCount() > 1){
   throw new RuntimeException("只能有一个子控件!");
  }
  mChild = getChildAt(0);
 }


 @Override
 public boolean onTouchEvent(MotionEvent event) {
  switch (event.getAction()){
   case MotionEvent.ACTION_DOWN:
    //手指按下时距离View上面的距离
    downY = (int) event.getY();

    //如果子View不在顶部 && 按下的位置在子View没有显示的位置,则不消费此次滑动事件,否则消费
    if(!isTop && downY 0 ,向下滑时deY<0
    int deY = downY - moveY;

    //向上滑动时的处理
    if(deY > 0){
     //将每次滑动的距离相加,为了防止子View的滑动超过View的顶部
     movedY += deY;
     if(movedY > mScrollHeight) movedY = mScrollHeight;

     if(movedY  0){
      scrollBy(0,deY);
     }
     downY = moveY;
     return true;
    }

    break;
   case MotionEvent.ACTION_UP:
    //手指抬起时的处理,如果向上滑动的距离超过了最大可滑动距离的1/4,并且子View不在顶部,就表示想把它拉上去
    if(movedY > mScrollHeight / 4 && !isTop){
     mScroller.startScroll(0,getScrollY(),0,(mScrollHeight - getScrollY()));
     invalidate();
     movedY = mScrollHeight;
     isTop = true;
    }else {
     //否则就表示放弃本次滑动,让它滑到最初的位置
     mScroller.startScroll(0,getScrollY(),0, -getScrollY());
     postInvalidate();
     movedY = 0;
     isTop = false;
    }

    break;
  }
  return super.onTouchEvent(event);
 }

 @Override
 public void computeScroll() {
  super.computeScroll();
  if(mScroller.computeScrollOffset()){
   scrollTo(0,mScroller.getCurrY());
   postInvalidate();
  }
 }
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。


推荐阅读
  • Android 九宫格布局详解及实现:人人网应用示例
    本文深入探讨了人人网Android应用中独特的九宫格布局设计,解析其背后的GridView实现原理,并提供详细的代码示例。这种布局方式不仅美观大方,而且在现代Android应用中较为少见,值得开发者借鉴。 ... [详细]
  • 优化ListView性能
    本文深入探讨了如何通过多种技术手段优化ListView的性能,包括视图复用、ViewHolder模式、分批加载数据、图片优化及内存管理等。这些方法能够显著提升应用的响应速度和用户体验。 ... [详细]
  • 本文详细介绍如何使用arm-eabi-gdb调试Android平台上的C/C++程序。通过具体步骤和实用技巧,帮助开发者更高效地进行调试工作。 ... [详细]
  • 本文总结了2018年的关键成就,包括职业变动、购车、考取驾照等重要事件,并分享了读书、工作、家庭和朋友方面的感悟。同时,展望2019年,制定了健康、软实力提升和技术学习的具体目标。 ... [详细]
  • 在计算机技术的学习道路上,51CTO学院以其专业性和专注度给我留下了深刻印象。从2012年接触计算机到2014年开始系统学习网络技术和安全领域,51CTO学院始终是我信赖的学习平台。 ... [详细]
  • CSS 布局:液态三栏混合宽度布局
    本文介绍了如何使用 CSS 实现液态的三栏布局,其中各栏具有不同的宽度设置。通过调整容器和内容区域的属性,可以实现灵活且响应式的网页设计。 ... [详细]
  • Linux 系统启动故障排除指南:MBR 和 GRUB 问题
    本文详细介绍了 Linux 系统启动过程中常见的 MBR 扇区和 GRUB 引导程序故障及其解决方案,涵盖从备份、模拟故障到恢复的具体步骤。 ... [详细]
  • 本文介绍了如何使用jQuery根据元素的类型(如复选框)和标签名(如段落)来获取DOM对象。这有助于更高效地操作网页中的特定元素。 ... [详细]
  • 本文将详细介绍如何使用剪映应用中的镜像功能,帮助用户轻松实现视频的镜像效果。通过简单的步骤,您可以快速掌握这一实用技巧。 ... [详细]
  • 深入理解Cookie与Session会话管理
    本文详细介绍了如何通过HTTP响应和请求处理浏览器的Cookie信息,以及如何创建、设置和管理Cookie。同时探讨了会话跟踪技术中的Session机制,解释其原理及应用场景。 ... [详细]
  • 本文介绍如何在 Xcode 中使用快捷键和菜单命令对多行代码进行缩进,包括右缩进和左缩进的具体操作方法。 ... [详细]
  • 本文介绍了一款用于自动化部署 Linux 服务的 Bash 脚本。该脚本不仅涵盖了基本的文件复制和目录创建,还处理了系统服务的配置和启动,确保在多种 Linux 发行版上都能顺利运行。 ... [详细]
  • 在Linux系统中配置并启动ActiveMQ
    本文详细介绍了如何在Linux环境中安装和配置ActiveMQ,包括端口开放及防火墙设置。通过本文,您可以掌握完整的ActiveMQ部署流程,确保其在网络环境中正常运行。 ... [详细]
  • Android 渐变圆环加载控件实现
    本文介绍了如何在 Android 中创建一个自定义的渐变圆环加载控件,该控件已在多个知名应用中使用。我们将详细探讨其工作原理和实现方法。 ... [详细]
  • 如何在WPS Office for Mac中调整Word文档的文字排列方向
    本文将详细介绍如何使用最新版WPS Office for Mac调整Word文档中的文字排列方向。通过这些步骤,用户可以轻松更改文本的水平或垂直排列方式,以满足不同的排版需求。 ... [详细]
author-avatar
乖乖雯莉_775
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有