我的编程空间,编程开发者的网络收藏夹
学习永远不晚

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

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

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

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 :


<resources>
 <declare-styleable name="MyScrollerView">
  <attr name="visibility_height" format="dimension"></attr>
 </declare-styleable>
</resources>

在XML文件中引用该属性:


<com.zw.myfirstapp.MyScrollerView
   android:layout_width="match_parent"
   android:layout_height="wrap_content"
   android:layout_alignParentBottom="true"
   android:background="@android:color/transparent"
   android:id="@+id/msv"
   app:visibility_height="100dp"
   ></com.zw.myfirstapp.MyScrollerView>

在代码中调用该属性(该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、先看我定义的成员变量的含义吧:


 
 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 < mScrollHeight ){
     return super.onTouchEvent(event);
    }
    return true;
   case MotionEvent.ACTION_MOVE:
    moveY = (int) event.getY();
    //deY是滑动的距离,向上滑时deY>0 ,向下滑时deY<0
    int deY = downY - moveY;
    //向上滑动时的处理
    if(deY > 0){
     //将每次滑动的距离相加,为了防止子View的滑动超过View的顶部
     movedY += deY;
     if(movedY > mScrollHeight) movedY = mScrollHeight;
     if(movedY < mScrollHeight){
      scrollBy(0,deY);
      downY = moveY;
      return true;
     }
    }
    //向下滑动时的处理,向下滑动时需要判断子View是否在顶部,如果不在顶部则不消费此次事件
    if(deY < 0 && isTop){
     movedY += deY;
     if(movedY < 0 ) movedY = 0;
     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:


<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:tools="http://schemas.android.com/tools"
 xmlns:app="http://schemas.android.com/apk/res-auto"
 android:id="@+id/activity_main"
 android:layout_width="match_parent"
 android:layout_height="match_parent">
  <com.zw.myfirstapp.MyScrollerView
   android:layout_width="match_parent"
   android:layout_height="wrap_content"
   android:layout_alignParentBottom="true"
   android:background="@android:color/transparent"
   android:id="@+id/msv"
   app:visibility_height="100dp"
   >
   <LinearLayout
    android:layout_width="match_parent"
    android:layout_height="300dp"
    android:background="@mipmap/b"
    android:gravity="center"
    android:orientation="vertical">
    <Button
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:id="@+id/btn"
     android:text="我是一个按钮"/>
   </LinearLayout>
  </com.zw.myfirstapp.MyScrollerView>
</RelativeLayout>

MyScrollerView:


public class MyScrollerView extends LinearLayout {
 
 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 < mScrollHeight ){
     return super.onTouchEvent(event);
    }
    return true;
   case MotionEvent.ACTION_MOVE:
    moveY = (int) event.getY();
    //deY是滑动的距离,向上滑时deY>0 ,向下滑时deY<0
    int deY = downY - moveY;
    //向上滑动时的处理
    if(deY > 0){
     //将每次滑动的距离相加,为了防止子View的滑动超过View的顶部
     movedY += deY;
     if(movedY > mScrollHeight) movedY = mScrollHeight;
     if(movedY < mScrollHeight){
      scrollBy(0,deY);
      downY = moveY;
      return true;
     }
    }
    //向下滑动时的处理,向下滑动时需要判断子View是否在顶部,如果不在顶部则不消费此次事件
    if(deY < 0 && isTop){
     movedY += deY;
     if(movedY < 0 ) movedY = 0;
     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下拉刷新上拉加载控件(适用于所有View)android开发教程之实现listview下拉刷新和上拉刷新效果Android实现上拉加载更多以及下拉刷新功能(ListView)Android RecyclerView实现下拉刷新和上拉加载Android RecyclerView 上拉加载更多及下拉刷新功能的实现方法android搜索框上下滑动变色效果Android中 视频屏幕左半部分上下滑动改变亮度右半部分上下滑动改变声音Android RecyclerView下拉刷新和上拉加载更多Android自定义listview布局实现上拉加载下拉刷新功能Android实战教程第四十三篇之上拉加载与下拉刷新


免责声明:

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

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

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

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

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

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

Android开发中使用View实现一个垂直上拉下滑功能

Android开发中使用View实现一个垂直上拉下滑功能?很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。效果二、实现思路 1、这个效果其实有很多实现方法,为了让松
2023-05-31

编程热搜

  • Android:VolumeShaper
    VolumeShaper(支持版本改一下,minsdkversion:26,android8.0(api26)进一步学习对声音的编辑,可以让音频的声音有变化的播放 VolumeShaper.Configuration的三个参数 durati
    Android:VolumeShaper
  • Android崩溃异常捕获方法
    开发中最让人头疼的是应用突然爆炸,然后跳回到桌面。而且我们常常不知道这种状况会何时出现,在应用调试阶段还好,还可以通过调试工具的日志查看错误出现在哪里。但平时使用的时候给你闹崩溃,那你就欲哭无泪了。 那么今天主要讲一下如何去捕捉系统出现的U
    Android崩溃异常捕获方法
  • android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
    系统的设置–>电池–>使用情况中,统计的能耗的使用情况也是以power_profile.xml的value作为基础参数的1、我的手机中power_profile.xml的内容: HTC t328w代码如下:
    android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
  • Android SQLite数据库基本操作方法
    程序的最主要的功能在于对数据进行操作,通过对数据进行操作来实现某个功能。而数据库就是很重要的一个方面的,Android中内置了小巧轻便,功能却很强的一个数据库–SQLite数据库。那么就来看一下在Android程序中怎么去操作SQLite数
    Android SQLite数据库基本操作方法
  • ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
    工作的时候为了方便直接打开编辑文件,一些常用的软件或者文件我们会放在桌面,但是在ubuntu20.04下直接直接拖拽文件到桌面根本没有效果,在进入桌面后发现软件列表中的软件只能收藏到面板,无法复制到桌面使用,不知道为什么会这样,似乎并不是很
    ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
  • android获取当前手机号示例程序
    代码如下: public String getLocalNumber() { TelephonyManager tManager =
    android获取当前手机号示例程序
  • Android音视频开发(三)TextureView
    简介 TextureView与SurfaceView类似,可用于显示视频或OpenGL场景。 与SurfaceView的区别 SurfaceView不能使用变换和缩放等操作,不能叠加(Overlay)两个SurfaceView。 Textu
    Android音视频开发(三)TextureView
  • android获取屏幕高度和宽度的实现方法
    本文实例讲述了android获取屏幕高度和宽度的实现方法。分享给大家供大家参考。具体分析如下: 我们需要获取Android手机或Pad的屏幕的物理尺寸,以便于界面的设计或是其他功能的实现。下面就介绍讲一讲如何获取屏幕的物理尺寸 下面的代码即
    android获取屏幕高度和宽度的实现方法
  • Android自定义popupwindow实例代码
    先来看看效果图:一、布局
  • Android第一次实验
    一、实验原理 1.1实验目标 编程实现用户名与密码的存储与调用。 1.2实验要求 设计用户登录界面、登录成功界面、用户注册界面,用户注册时,将其用户名、密码保存到SharedPreference中,登录时输入用户名、密码,读取SharedP
    Android第一次实验

目录