全网整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:400-708-3566

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、先看我定义的成员变量的含义吧:

 /**
  * 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 < 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 {

 /**
  * 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 < 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垂直上拉View  # Android垂直下滑View  # Android下拉刷新上拉加载控件(适用于所有View)  # android开发教程之实现listview下拉刷新和上拉刷新效果  # Android实现上拉加载更多以及下拉刷新功能(ListView)  # Android RecyclerView实现下拉刷新和上拉加载  # Android RecyclerView 上拉加载更多及下拉刷新功能的实现方法  # android搜索框上下滑动变色效果  # Android中 视频屏幕左半部分上下滑动改变亮度右半部分上下滑动改变声音  # Android RecyclerView下拉刷新和上拉加载更多  # Android自定义listview布局实现上拉加载下拉刷新功能  # Android实战教程第四十三篇之上拉加载与下拉刷新  # 重写  # 按下  # 有一  # 最初  # 屏幕上  # 让它  # 想把  # 大可  # 将该  # 不能超过  # 创建一个  # 为了防止  # 时需  # 的是  # 滑到  # 放在  # 超过了  # 我还  # 是这样  # 有很多 


相关文章: C#怎么创建控制台应用 C# Console App项目创建方法  临沂网站制作企业,临沂第三中学官方网站?  如何高效完成自助建站业务培训?  建站之星会员如何解锁更多建站功能?  如何在云虚拟主机上快速搭建个人网站?  如何制作算命网站,怎么注册算命网站?  如何配置支付宝与微信支付功能?  淘宝制作网站有哪些,淘宝网官网主页?  *服务器网站为何频现安全漏洞?  上海网站制作网页,上海本地的生活网站有哪些?最好包括生活的各个方面的?  建站之星如何助力企业快速打造五合一网站?  如何快速查询域名建站关键信息?  台州网站建设制作公司,浙江手机无犯罪记录证明怎么开?  公司门户网站制作公司有哪些,怎样使用wordpress制作一个企业网站?  网站按钮制作软件,如何实现网页中按钮的自动点击?  广州商城建站系统开发成本与周期如何控制?  建站VPS推荐:2025年高性能服务器配置指南  枣阳网站制作,阳新火车站打的到仙岛湖多少钱?  建站主机选虚拟主机还是云服务器更好?  北京网站制作费用多少,建立一个公司网站的费用.有哪些部分,分别要多少钱?  西安专业网站制作公司有哪些,陕西省建行官方网站?  制作网站软件推荐手机版,如何制作属于自己的手机网站app应用?  建站之星价格显示格式升级,你的预算足够吗?  利用JavaScript实现拖拽改变元素大小  北京企业网站设计制作公司,北京铁路集团官方网站?  如何选择最佳自助建站系统?快速指南解析优劣  装修招标网站设计制作流程,装修招标流程?  重庆网站制作公司哪家好,重庆中考招生办官方网站?  教学论文网站制作软件有哪些,写论文用什么软件 ?  制作网站的软件下载免费,今日头条开宝箱老是需要下载怎么回事?  如何有效防御Web建站篡改攻击?  如何快速搭建响应式可视化网站?  公司网站制作需要多少钱,找人做公司网站需要多少钱?  建站之星logo尺寸如何设置最合适?  建站之星如何实现PC+手机+微信网站五合一建站?  如何通过主机屋免费建站教程十分钟搭建网站?  如何选择适配移动端的WAP自助建站平台?  网站制作公司,橙子建站是合法的吗?  北京网页设计制作网站有哪些,继续教育自动播放怎么设置?  如何通过智能用户系统一键生成高效建站方案?  建站之星后台密码如何安全设置与找回?  企业宣传片制作网站有哪些,传媒公司怎么找企业宣传片项目?  如何正确选择百度移动适配建站域名?  陕西网站制作公司有哪些,陕西凌云电器有限公司官网?  如何用搬瓦工VPS快速搭建个人网站?  制作假网页,招聘网的薪资待遇,会有靠谱的吗?一面试又各种折扣?  网站企业制作流程,用什么语言做企业网站比较好?  如何用虚拟主机快速搭建网站?详细步骤解析  如何快速上传建站程序避免常见错误?  建站之星如何开启自定义404页面避免用户流失? 

您的项目需求

*请认真填写需求信息,我们会在24小时内与您取得联系。