Android 自定义LinearLayout实现滑动下拉抽屉的功能
发布日期:2021-06-29 11:46:24 浏览次数:3 分类:技术文章

本文共 5469 字,大约阅读时间需要 18 分钟。

先看效果图:

先来说说思路:我们把该页面分为两部分,分别是头部的抽屉布局(海洋色背景)和主内容布局(白色背景),这两部分的布局是呈线性关系,即抽屉在上,主页面在下,并且它们的父布局应该是一个可滑动的LinearLayout线性布局

所以,我们的目标就是自定义一个可滑动的LinearLayout,并且设置它的子布局都向上移动一个自定义LinearLayout的高度

一、自定义控件的测量和布局

自定义LinearLayout,假设为MyPullDownLayout:

public class MyPullDownLayout extends LinearLayout {    public MyPullDownLayout(Context context, @Nullable AttributeSet attrs) {        super(context, attrs);    }}

我们先对子控件进行简单的测量:

@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {    super.onMeasure(widthMeasureSpec, heightMeasureSpec);    //测量子控件的大小    for(int i=0;i

在onLayout中设置每一个子控件的位置:

@Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) {    super.onLayout(changed, l,t, r, b);    int height=getVerticalHeight();    //设置子控件的位置    for(int i=0;i

int height = getVerticalHeight() 即为这个自定义控件的高度,并让每个子控件都向上移动一个height的高度

 二、自定义控件的滑动实现

我们创建一个手势检测器(GestureDetector)来辅助我们滑动控件:

private int currentPos=0;private GestureDetector detector=new GestureDetector(new GestureDetector.SimpleOnGestureListener(){    public boolean onScroll(MotionEvent e1, MotionEvent e2,                            float distanceX, float distanceY) {        //相对滑动:Y方向滑动多少距离,view就跟着滑动多少距离        //手指向上滑动        if(e2.getY()
=0){ //滑到的y值超过0时,反弹回去 if(getScrollY()>0)scrollBy(0,-getScrollY()); //否则什么也不做 } else if(getScrollY()<=0){ //如果手指向下滑动并且没有超过抽屉页的滑动范围,就滑动页面 if(!(e2.getY()>e1.getY()&&getScrollY()==-getVerticalHeight())){ scrollBy(0,(int)distanceY); //手指向下滑动 if(e2.getY()>e1.getY())currentPos=getScrollY()/(getVerticalHeight()/4); //手指向上滑动 else currentPos=getScrollY()/(4*getVerticalHeight()/5); if(currentPos>0)currentPos=0; if(currentPos<-1)currentPos=-1; } } return super.onScroll(e1,e2,distanceX,distanceY); }});@Overridepublic boolean onTouchEvent(MotionEvent event) { //将event信息传给detector; detector.onTouchEvent(event);}private int getVerticalHeight(){ return getHeight()-getPaddingBottom()-getPaddingTop();}

getScrollY():y轴方向已经滑动的距离。如果在抽屉页,则getScrollY()为负数

currentPos:手指离开屏幕后,控件所在的位置(抽屉页或者主页面)

currentPos=getScrollY()/(getVerticalHeight()/4):只需滑动1/4控件高度的距离就可以自动滑动到抽屉页

currentPos=getScrollY()/(4*getVerticalHeight()/5):只需滑动1-4/5=1/5控件高度的距离就可以自动滑动到主页面 

 创建Scorller对象来实现页面的自动滑动效果:

private Scroller scroller;public MyPullDownLayout(Context context, @Nullable AttributeSet attrs) {    super(context, attrs);    init();}private void init(){    scroller=new Scroller(getContext());}@Overridepublic boolean onTouchEvent(MotionEvent event) {    //将event信息传给detector;    detector.onTouchEvent(event);    switch (event.getAction()){        case MotionEvent.ACTION_UP:            //手指离开屏幕后开始自动滑动            scroller.startScroll(0,getScrollY(),0,getVerticalHeight()*currentPos-getScrollY());            invalidate();            break;    }    return true;}@Overridepublic void computeScroll() {    if(scroller.computeScrollOffset()){        scrollTo(0,scroller.getCurrY());        postInvalidateDelayed(10);    }}

scroller.startScroll():前两个参数是x,y方向已经滑动的距离,后两个参数是x,y方向还需要滑动的距离

computeScroll():利用scroller.getCurrY()每次滑动一点点距离,调用postInvalidateDelayed(10)后会回调该方法

三、XML布局文件注意事项

 为了体现抽屉控件的宽高占整个父控件宽高的效果,需要把抽屉布局(Layout)的width和height设为match_parent。然后主页面布局部分就放在抽屉布局的下方,具体代码如下:


自定义LinearLayout完整代码如下:

public class MyPullDownLayout extends LinearLayout {    private int currentPos=0;    private Scroller scroller;    public MyPullDownLayout(Context context, @Nullable AttributeSet attrs) {        super(context, attrs);        init();    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        super.onMeasure(widthMeasureSpec, heightMeasureSpec);        //测量子控件的大小        for(int i=0;i
=0){ //滑到的y值超过0时,反弹回去 if(getScrollY()>0)scrollBy(0,-getScrollY()); //否则什么也不做 } else if(getScrollY()<=0){ //如果手指向下滑动并且没有超过抽屉页的滑动范围,就滑动页面 if(!(e2.getY()>e1.getY()&&getScrollY()==-getVerticalHeight())){ scrollBy(0,(int)distanceY); //手指向下滑动 if(e2.getY()>e1.getY())currentPos=getScrollY()/(getVerticalHeight()/4); //手指向上滑动 else currentPos=getScrollY()/(4*getVerticalHeight()/5); if(currentPos>0)currentPos=0; if(currentPos < -1)currentPos=-1; } } return super.onScroll(e1,e2,distanceX,distanceY); } }); @Override public boolean onTouchEvent(MotionEvent event) { //将event信息传给detector; detector.onTouchEvent(event); switch (event.getAction()){ case MotionEvent.ACTION_UP: scroller.startScroll(0,getScrollY(),0,getVerticalHeight()*currentPos-getScrollY()); invalidate(); break; } return true; } @Override public void computeScroll() { if(scroller.computeScrollOffset()){ scrollTo(0,scroller.getCurrY()); postInvalidateDelayed(10); } } private int getVerticalHeight(){ return getHeight()-getPaddingBottom()-getPaddingTop(); }}

本篇到这里就结束,下一篇我们来解决该自定义Layout与RecyclerView滑动冲突的问题:

转载地址:https://blog.csdn.net/zz51233273/article/details/108320445 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:Android 自定义LinearLayout实现滑动下拉抽屉的功能(解决滑动冲突)
下一篇:Android 自定义View一个酷炫又无厘头的动画

发表评论

最新留言

逛到本站,mark一下
[***.202.152.39]2024年04月06日 13时38分30秒