亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Android 自定義底部上拉控件的實(shí)現(xiàn)方法

 更新時(shí)間:2018年01月20日 11:16:09   作者:卻把清梅嗅  
下面小編就為大家分享一篇Android 自定義底部上拉控件的實(shí)現(xiàn)方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧

前言

又到了新的一月,今天提供一個(gè)Android自定義底部上拉布局的實(shí)現(xiàn),起因是自己在項(xiàng)目中需要實(shí)現(xiàn)這樣一個(gè)控件,干脆自己寫一個(gè)練練手。

寫完了覺(jué)得能想到的需求都基本有了(可能會(huì)有其它需求,不過(guò)基本上改吧改吧就行了),又花了一點(diǎn)時(shí)間直接放到了Github上托管,希望能給您一些參考價(jià)值:

SlideBottomLayout-Android 簡(jiǎn)單易上手的Android底部上拉控件

先看一下實(shí)現(xiàn)效果:

分析一下這種控件的基本需求有以下幾種:

1.有一個(gè)部分是能夠作為把手(就是圖中的handle,)進(jìn)行拖拽的,這部分高度是暴露在界面中的 -> 需要實(shí)現(xiàn):Handle按鈕

* 特殊需求特殊分析,比如讓這個(gè)Handle透明實(shí)現(xiàn)無(wú)Handle的效果

2.底部上啦布局是有一定高度限制的,不一定覆蓋設(shè)備的整個(gè)屏幕 -> 需要自定義最大高度

3.當(dāng)從底部上拉一點(diǎn)點(diǎn)時(shí)抬手,布局縮回,若超過(guò)一定高度,自動(dòng)彈到最高,隱藏同理 -> 需要自定義自動(dòng)到達(dá)頂部/隱藏的閾值

直接使用

直接使用也很簡(jiǎn)單,筆者進(jìn)行了簡(jiǎn)單的封裝,以供參考:

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

allprojects {
 repositories {
  ...
  maven { url 'https://jitpack.io' }
 }
}

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

dependencies {
   compile 'com.github.qingmei2:SlideBottomLayout-Android:1.2.3'
}

3.Add view in your layout:

需要注意的是:為了簡(jiǎn)單實(shí)現(xiàn),筆者偷了個(gè)懶,設(shè)定為該布局下只能有一個(gè)直接的子View(類似ScrollView)

因此如果您添加需要一個(gè)布局,請(qǐng)?jiān)谕饷媲短滓粋€(gè)ViewGroup:

<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:該屬性就是您要暴露出來(lái)Handle的高度,詳見(jiàn)下方的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>

實(shí)現(xiàn)步驟

具體代碼如下,其中上述需求的設(shè)置方式都已經(jīng)在代碼中聲明:

先看下屬性聲明:

<?xml version="1.0" encoding="utf-8"?>
<resources>
  <declare-styleable name="SlideBottomLayout">
    <attr name="handler_height" format="dimension"></attr>
  </declare-styleable>
</resources>
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.注意,這個(gè)接口用來(lái)設(shè)置「需要自定義自動(dòng)到達(dá)頂部/隱藏的閾值」
  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中設(shè)置對(duì)應(yīng)屬性來(lái)實(shí)現(xiàn)該效果
  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},
   * 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.
   * <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(), 0, (movedMaxDis - getScrollY()));
    invalidate();
    movedDis = movedMaxDis;
    arriveTop = true;
  }
  public void scroll2BottomImmediate() {
    mScroller.startScroll(0, getScrollY(), 0, -getScrollY());
    postInvalidate();
    movedDis = 0;
    arriveTop = false;
  }
}

注釋也比較明了,如果有疑問(wèn),詳細(xì)請(qǐng)參照SlideBottomLayout-Android 簡(jiǎn)單易上手的Android底部上拉控件

里面有相對(duì)詳細(xì)的使用說(shuō)明,此外,如果還有一些需求,您可以在issue中提出,提前感謝!

以上這篇Android 自定義底部上拉控件的實(shí)現(xiàn)方法就是小編分享給大家的全部?jī)?nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • 探究Android客戶端網(wǎng)絡(luò)預(yù)連接優(yōu)化機(jī)制

    探究Android客戶端網(wǎng)絡(luò)預(yù)連接優(yōu)化機(jī)制

    一般情況下,我們都是用一些封裝好的網(wǎng)絡(luò)框架去請(qǐng)求網(wǎng)絡(luò),對(duì)底層實(shí)現(xiàn)不甚關(guān)注,而大部分情況下也不需要特別關(guān)注處理。了解底層的一些實(shí)現(xiàn),有益于我們對(duì)網(wǎng)絡(luò)加載進(jìn)行優(yōu)化。本文就是關(guān)于根據(jù)http的連接復(fù)用機(jī)制來(lái)優(yōu)化網(wǎng)絡(luò)加載速度的原理與細(xì)節(jié)
    2021-06-06
  • Android從Fragment跳轉(zhuǎn)到其他Activity的簡(jiǎn)單實(shí)例

    Android從Fragment跳轉(zhuǎn)到其他Activity的簡(jiǎn)單實(shí)例

    這篇文章主要介紹了Android從Fragment跳轉(zhuǎn)到其他Activity的簡(jiǎn)單實(shí)例,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-02-02
  • Android仿微博個(gè)人詳情頁(yè)滾動(dòng)到頂部的實(shí)例代碼

    Android仿微博個(gè)人詳情頁(yè)滾動(dòng)到頂部的實(shí)例代碼

    這篇文章主要介紹了Android仿微博個(gè)人詳情頁(yè)滾動(dòng)到頂部的實(shí)例代碼,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒家,需要的朋友可以參考下
    2019-05-05
  • Android實(shí)戰(zhàn)打飛機(jī)游戲之子彈生成與碰撞以及爆炸效果(5)

    Android實(shí)戰(zhàn)打飛機(jī)游戲之子彈生成與碰撞以及爆炸效果(5)

    這篇文章主要為大家詳細(xì)介紹了Android實(shí)戰(zhàn)打飛機(jī)游戲之子彈生成與碰撞以及爆炸效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-07-07
  • Android實(shí)現(xiàn)隨機(jī)生成驗(yàn)證碼

    Android實(shí)現(xiàn)隨機(jī)生成驗(yàn)證碼

    在登錄注冊(cè)軟件時(shí),經(jīng)常會(huì)要求填寫隨機(jī)驗(yàn)證碼,這篇文章為大家詳細(xì)主要介紹了Android實(shí)現(xiàn)隨機(jī)生成驗(yàn)證碼,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-08-08
  • Android 實(shí)現(xiàn)沉浸式狀態(tài)欄的方法

    Android 實(shí)現(xiàn)沉浸式狀態(tài)欄的方法

    沉浸式狀態(tài)欄的來(lái)源就是很多手機(jī)用的是實(shí)體按鍵,沒(méi)有虛擬鍵,于是開了沉浸模式就只有狀態(tài)欄消失了。下面腳本之家小編給大家介紹Android 實(shí)現(xiàn)沉浸式狀態(tài)欄,需要的朋友可以參考下
    2015-09-09
  • 完美解決Android App啟動(dòng)頁(yè)有白屏閃過(guò)的問(wèn)題

    完美解決Android App啟動(dòng)頁(yè)有白屏閃過(guò)的問(wèn)題

    這篇文章主要介紹了完美解決Android App啟動(dòng)頁(yè)有白屏閃過(guò)的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-08-08
  • Android MediaPlayer 音頻倍速播放 調(diào)整播放速度問(wèn)題

    Android MediaPlayer 音頻倍速播放 調(diào)整播放速度問(wèn)題

    這篇文章主要介紹了Android MediaPlayer 音頻倍速播放,調(diào)整播放速度,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2019-09-09
  • Android圖片加載框架Gilde源碼層深入分析

    Android圖片加載框架Gilde源碼層深入分析

    Glide就像Picasso,可以從多個(gè)源去加載和顯示圖片,同時(shí)也兼顧緩存和在做圖片處理的時(shí)候維持一個(gè)低內(nèi)存消耗。它已經(jīng)在Google官方APP中使用了,就和Picasso一樣受歡迎
    2022-11-11
  • Android Bitmap詳細(xì)介紹

    Android Bitmap詳細(xì)介紹

    Android中Bitmap的常見(jiàn)操作整理一覽,需要的朋友可以參考下
    2012-12-12

最新評(píng)論