一、简介
最近朋友公司需要实现一个垂直上拉下滑的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(); } } }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持小牛知识库。
本文向大家介绍JavaScript简单下拉菜单特效,包括了JavaScript简单下拉菜单特效的使用技巧和注意事项,需要的朋友参考一下 本文实例为大家分享了js下拉菜单特效,供大家参考,具体内容如下 实例1:联动的省市下拉菜单 onchange 事件会在域的内容改变时发生。 实例2:三级联动的省市县下拉菜单 以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持呐喊教程。
我做了一个范围滑块位置垂直。但它没有反应。我签入了chrome默认移动设备。当调整范围滑块时,它将滚动页面 我已经在这里添加了我的代码。 null null
本文向大家介绍jQuery实现简单下拉导航效果,包括了jQuery实现简单下拉导航效果的使用技巧和注意事项,需要的朋友参考一下 本文实例讲述了jQuery实现简单下拉导航效果。分享给大家供大家参考。具体如下: 这里粗略做一款基于jquery的下拉导航菜单效果,我觉得挺不错,虽然没有怎么美化,但是代码易懂,另外修补的空间也比较大,相信对学习CSS以及jquery的朋友有不小的帮助。 运行效果截图如下
我正在尝试定制一个范围滑块根据我的需要我几乎在那里除了它的方向,目前它是水平对齐的,应该是垂直的。 我尝试添加和以及 不走运。 当前代码库如下:Style 标记 是不是我漏了什么?如有任何帮助,我们将不胜感激。谢谢
本文向大家介绍Android实现垂直跑马灯效果,包括了Android实现垂直跑马灯效果的使用技巧和注意事项,需要的朋友参考一下 在我们开发过程中,跑马灯这个功能非常实用的,在实现这个功能的时候,这个时候我们通常需要找demo来实现这个方法,我从github上面找到这个demo感觉很好用,所以就要实现了这个功能喽MarqueeView,看这个工具类,因为我找这个类的时候是没有点击事件的,所以我给它加
本文向大家介绍基于jQuery制作小图标上下滑动特效,包括了基于jQuery制作小图标上下滑动特效的使用技巧和注意事项,需要的朋友参考一下 一个小图标特效,挺有趣的,代码也很容易懂。 jQ小图标上下滑动特效: 代码如下: