Android 自定义底部上拉控件的实现方法

Android 自定义底部上拉控件的实现方法,第1张

概述前言又到了新的一月,今天提供一个Android自定义底部上拉布局的实现,起因是自己在项目中需要实现这样一个控件,干脆自己写一个练练手。

前言

又到了新的一月,今天提供一个AndroID自定义底部上拉布局的实现,起因是自己在项目中需要实现这样一个控件,干脆自己写一个练练手。

写完了觉得能想到的需求都基本有了(可能会有其它需求,不过基本上改吧改吧就行了),又花了一点时间直接放到了Github上托管,希望能给您一些参考价值:

SlideBottomLayout-Android 简单易上手的Android底部上拉控件

先看一下实现效果:

分析一下这种控件的基本需求有以下几种:

1.有一个部分是能够作为把手(就是图中的handle,)进行拖拽的,这部分高度是暴露在界面中的 -> 需要实现:Handle按钮

* 特殊需求特殊分析,比如让这个Handle透明实现无Handle的效果

2.底部上啦布局是有一定高度限制的,不一定覆盖设备的整个屏幕 -> 需要自定义最大高度

3.当从底部上拉一点点时抬手,布局缩回,若超过一定高度,自动d到最高,隐藏同理 -> 需要自定义自动到达顶部/隐藏的阈值

直接使用

直接使用也很简单,笔者进行了简单的封装,以供参考:

1. 在Project的build.gradle文件中添加:

@H_419_50@allprojects { repositorIEs { ... maven { url 'https://jitpack.io' } }}

2.在Module的build.gradle文件中添加:

@H_419_50@dependencIEs { compile 'com.github.qingmei2:SlIDeBottomLayout-AndroID:1.2.3'}

3.Add vIEw in your layout:

需要注意的是:为了简单实现,笔者偷了个懒,设定为该布局下只能有一个直接的子VIEw(类似ScrollVIEw)

因此如果您添加需要一个布局,请在外面嵌套一个VIEwGroup:

@H_419_50@<com.qingmei2.library.SlIDeBottomLayout androID:ID="@+ID/slIDeLayout" androID:layout_wIDth="match_parent" androID:layout_height="wrap_content" androID:layout_alignParentBottom="true" androID:layout_margintop="200dp" app:handler_height="50dp"> <!--app:handler_height:该属性就是您要暴露出来Handle的高度,详见下方的TextVIEw(ID=handle)--> <!--Just one child--> <linearLayout androID:layout_wIDth="match_parent" androID:layout_height="wrap_content" androID:orIEntation="vertical"> <TextVIEw androID:ID="@+ID/handle" androID:layout_wIDth="match_parent" androID:layout_height="50dp" androID:background="#d95858" androID:gravity="center" androID:text="handle" androID:textcolor="@androID:color/white" androID:textSize="16sp" /> <androID.support.v7.Widget.RecyclerVIEw androID:ID="@+ID/recycler_vIEw" androID:layout_wIDth="match_parent" androID:layout_height="wrap_content"> </androID.support.v7.Widget.RecyclerVIEw> </linearLayout> </com.qingmei2.library.SlIDeBottomLayout>

实现步骤

具体代码如下,其中上述需求的设置方式都已经在代码中声明:

先看下属性声明:

@H_419_50@<?xml version="1.0" enCoding="utf-8"?><resources> <declare-styleable name="SlIDeBottomLayout"> <attr name="handler_height" format="dimension"></attr> </declare-styleable></resources>@H_419_50@public class SlIDeBottomLayout extends linearLayout { public voID setShortSlIDeListener(ShortSlIDeListener Listener) { this.shortSlIDeListener = Listener; } private ShortSlIDeListener shortSlIDeListener; /** * The {@link MotionEvent#ACTION_DOWN} gesture location. */ private int downY; /** * The {@link MotionEvent#ACTION_MOVE} gesture location. */ private int moveY; /** * the value of moved distance by the gesture. When the value was modifIEd and not exceed * the {@link #movedMaxdis},then make this VIEwGroup move. */ private int moveddis; /** * The max distance that the {@link SlIDeBottomLayout} can scroll to,it used to compare with the * {@link #downY},determine whether it can slIDe by the gesture. */ private int movedMaxdis; /** * ChildVIEw of the {@link SlIDeBottomLayout},you can set a Layout such as the {@link linearLayout}、 * {@link androID.Widget.relativeLayout} ect. * We set the rules that {@link SlIDeBottomLayout} just can have one child-vIEw,or else get a * {@link RuntimeException} at {@link #onFinishInflate()} */ private VIEw childVIEw; /** * The control {@link SlIDeBottomLayout} automatically switches the threshold of the state. if * this VIEwGroup moved distance more than {@link #movedMaxdis} * it,switch the state of * {@link #arrivetop} right Now. * </p> * See the {@link #touchActionUp(float)}. */ private float hIDeWeight = 0.25f; //3.注意,这个接口用来设置「需要自定义自动到达顶部/隐藏的阈值」 public voID setHIDeWeight(float hIDeWeight) { if (hIDeWeight <= 0 || hIDeWeight > 1) throw new IllegalArgumentException("hIDeWeight should belong (0f,1f]"); this.hIDeWeight = hIDeWeight; } private Scroller mScroller; /** * It means the {@link #childVIEw} is arriving the top of parent or else. */ private boolean arrivetop = false; /** * the {@link #childVIEw} Initially visible height */ private float visibilityHeight; //1.初始化Handle显示高度,建议您在xml中设置对应属性来实现该效果 public voID setVisibilityHeight(float visibilityHeight) { this.visibilityHeight = visibilityHeight; } public SlIDeBottomLayout(@NonNull Context context) { super(context); } public SlIDeBottomLayout(@NonNull Context context,@Nullable AttributeSet attrs) { super(context,attrs); initAttrs(context,attrs); } public SlIDeBottomLayout(@NonNull Context context,@Nullable AttributeSet attrs,int defStyleAttr) { super(context,attrs,defStyleAttr); initAttrs(context,attrs); } /** * Get the config from {@link R.styleable},then init other configrations{@link #initConfig(Context)}. * * @param context the {@link Context} * @param attrs the configs in layout attrs. */ private voID initAttrs(Context context,AttributeSet attrs) { final TypedArray ta = context.obtainStyledAttributes(attrs,R.styleable.SlIDeBottomLayout); visibilityHeight = ta.getDimension(R.styleable.SlIDeBottomLayout_handler_height,0); ta.recycle(); initConfig(context); } private voID initConfig(Context context) { if (mScroller == null) mScroller = new Scroller(context); this.setBackgroundcolor(color.transparent); } /** * It start a judgement for ensure the child-vIEw be unique in this method,then assgin it * to {{@link #childVIEw}. * this method will be called before the {@link #onMeasure(int,int)} */ @OverrIDe protected voID onFinishInflate() { super.onFinishInflate(); if (getChildCount() == 0 || getChildAt(0) == null) { throw new RuntimeException("there have no child-VIEw in the SlIDeBottomLayout!"); } if (getChildCount() > 1) { throw new RuntimeException("there just alow one child-VIEw in the SlIDeBottomLayout!"); } childVIEw = getChildAt(0); } @OverrIDe protected voID onMeasure(int wIDthMeasureSpec,int heightmeasureSpec) { super.onMeasure(wIDthMeasureSpec,heightmeasureSpec); movedMaxdis = (int) (childVIEw.getMeasuredHeight() - visibilityHeight); } @OverrIDe protected voID onLayout(boolean changed,int l,int t,int r,int b) { super.onLayout(changed,l,t,r,b); childVIEw.layout(0,movedMaxdis,childVIEw.getMeasureDWIDth(),childVIEw.getMeasuredHeight() + movedMaxdis); } @OverrIDe public boolean ontouchEvent(MotionEvent event) { final float dy = event.getY(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: if (touchActionDown(dy)) return true; break; case MotionEvent.ACTION_MOVE: if (touchActionMove(dy)) return true; break; case MotionEvent.ACTION_UP: if (touchActionUp(dy)) return true; break; } return super.ontouchEvent(event); } @OverrIDe public voID computeScroll() { super.computeScroll(); if (mScroller == null) mScroller = new Scroller(getContext()); if (mScroller.computeScrollOffset()) { scrollTo(0,mScroller.getCurrY()); postInvalIDate(); } } /** * When the touch event is {@link MotionEvent#ACTION_UP},* then judge the state of vIEw group and control the {@link Scroller} to scroll. * <p> * In this VIEwGroup,we set the rules that is if this scroll gesture's move distance * more than {@link #movedMaxdis} * {@link #hIDeWeight}(default hIDeWeight value is 1/4 heights * of this VIEwGroup),then call {@link #hIDe()} or {@link #show()} right Now. which method will * be call depends on {@link #arrivetop}. * <p * if the scroll gesture's move distance don't reach the goal value,then call the * {@link ShortSlIDeListener#onShortSlIDe(float)} if you call {@link #setShortSlIDeListener(ShortSlIDeListener)} * init this VIEwGroup. else will call {@link #hIDe()}. * * @param eventY The location of trigger * @return Be used to determine consume this event or else. */ public boolean touchActionUp(float eventY) { if (moveddis > movedMaxdis * hIDeWeight) { switchVisible(); } else { if (shortSlIDeListener != null) { shortSlIDeListener.onShortSlIDe(eventY); } else { hIDe(); } } return true; } /** * When the touch event is {@link MotionEvent#ACTION_MOVE},we set the rules that is if this scroll gesture's move distance * more than {@link #movedMaxdis} * {@link #hIDeWeight}(default hIDeWeight value is 1/4 heights of this VIEwGroup),* then call {@link #hIDe()} or {@link #show()} right Now. * <p> * * @param eventY The location of trigger * @return Be used to determine consume this event or else. */ public boolean touchActionMove(float eventY) { moveY = (int) eventY; //the dy is sum of the move distance,the value > 0 means scroll up,the value < 0 means scroll down. final int dy = downY - moveY; if (dy > 0) { //scroll up moveddis += dy; if (moveddis > movedMaxdis) moveddis = movedMaxdis; if (moveddis < movedMaxdis) { scrollBy(0,dy); downY = moveY; return true; } } else { //scroll down moveddis += dy; if (moveddis < 0) moveddis = 0; if (moveddis > 0) { scrollBy(0,dy); } downY = moveY; return true; } return false; } /** * When the touch event is {@link MotionEvent#ACTION_DOWN},* Record the location of this action. * * @param eventY The location of trigger * @return Be used to determine consume this event or else. */ public boolean touchActionDown(float eventY) { downY = (int) eventY; //Whether custom this gesture. if (!arrivetop && downY < movedMaxdis) { return false; } else return true; } /** * the extand method for showing {@link SlIDeBottomLayout} */ public voID show() { scroll2topImmediate(); } /** * the extand method for hIDing {@link SlIDeBottomLayout} */ public voID hIDe() { scroll2BottomImmediate(); } /** * @return The VIEwGroup is arrive top or else. */ public boolean switchVisible() { if (arrivetop()) hIDe(); else show(); return arrivetop(); } public boolean arrivetop() { return this.arrivetop; } public voID scroll2topImmediate() { mScroller.startScroll(0,getScrollY(),(movedMaxdis - getScrollY())); invalIDate(); moveddis = movedMaxdis; arrivetop = true; } public voID scroll2BottomImmediate() { mScroller.startScroll(0,-getScrollY()); postInvalIDate(); moveddis = 0; arrivetop = false; }}

注释也比较明了,如果有疑问,详细请参照SlideBottomLayout-Android 简单易上手的Android底部上拉控件

里面有相对详细的使用说明,此外,如果还有一些需求,您可以在issue中提出,提前感谢!

以上这篇AndroID 自定义底部上拉控件的实现方法就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持编程小技巧。

您可能感兴趣的文章:Android自定义控件实现底部菜单(上)Android自定义控件实现底部菜单(下)Android自定义控件实现随手指移动的小球Android自定义控件实现滑动开关效果Android编程实现自定义控件的方法示例 总结

以上是内存溢出为你收集整理的Android 自定义底部上拉控件的实现方法全部内容,希望文章能够帮你解决Android 自定义底部上拉控件的实现方法所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/1143078.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-31
下一篇 2022-05-31

发表评论

登录后才能评论

评论列表(0条)

保存