猛禽洛的程式筆記庫

[Android] 製作類似Pixel Launcher由下往上拖拉畫面的效果 (使用 BottomSheetBehavior)

在有些桌面軟體或應用程式中,會看到可由螢幕下方拖拉出畫面的效果 (如Google Map),像這樣↓↓

來源:https://stackoverflow.com/questions/44274457/how-to-make-bottom-sheet-bottomsheetbehavior-expandable-to-arbitrary-position

這邊介紹簡單的製作方式:

首先以 GitHub 上的 PixelSlide 專案作為範例

原理就是在 CoordinatorLayout 中加入 include 區域,然後將另一個 view 引導進來

主要程式碼說明如下:

public class MainActivity extends AppCompatActivity {

  private View mBottomSheet;
  private BottomSheetBehavior mBottomSheetBehavior;
  private ExpandIconView mExpandIconView;

  private float mSlideOffset = 0;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    findViews();
    setUpViews();
  }

  private void findViews() {
    mBottomSheet = findViewById(R.id.bottom_sheet);
    //這邊是初始化拖拉畫面中的元件
    mExpandIconView = (ExpandIconView) mBottomSheet.findViewById(R.id.expandIconView);
  }

  private void setUpViews() {
    //設定箭頭icon的狀態(套件)
    mExpandIconView.setState(ExpandIconView.LESS, true);
    //將mBottomSheet綁定給BottomSheetBehavior
    mBottomSheetBehavior = BottomSheetBehavior.from(mBottomSheet);
    //監聽BottomSheetBehavior拖動過程
    mBottomSheetBehavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
      @Override
      public void onStateChanged(@NonNull View bottomSheet, int newState) {
        //拖動的畫面狀態切換
        if (newState == BottomSheetBehavior.STATE_COLLAPSED) {
          mExpandIconView.setState(ExpandIconView.LESS, true);
        } else if (newState == BottomSheetBehavior.STATE_EXPANDED) {
          mExpandIconView.setState(ExpandIconView.MORE, true);
        }
      }

      @Override
      public void onSlide(@NonNull View bottomSheet, final float slideOffset) {
        //這裡是整個拖動過程
        mSlideOffset = slideOffset;
        //控制箭頭套件的狀態而已
        new Handler().postDelayed(new Runnable() {
          @Override
          public void run() {
            float dis = (mSlideOffset - slideOffset) * 10;
            if (dis > 1) {
              dis = 1;
            } else if (dis < -1) {
              dis = -1;
            }
            if (mBottomSheetBehavior.getState() == BottomSheetBehavior.STATE_DRAGGING) {
              mExpandIconView.setFraction(.5f + dis * .5f, false);
            }
          }
        }, 150);
      }
    });
    //設定拖拉畫面收起時的突出大小
    //這邊將dp轉換為px
    mBottomSheetBehavior.setPeekHeight((int) convertDpToPixel(70, this));
    //設定拖拉視窗的狀態 (STATE_COLLAPSED = 縮小, STATE_EXPANDED = 展開)
    mBottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
  }
  
  //dp轉px
  public float convertDpToPixel(float dp, Context context) {
    return dp * (getDisplayMetrics(context).densityDpi / 160f);
  }
  
  public DisplayMetrics getDisplayMetrics(Context context) {
    Resources resources = context.getResources();
    return resources.getDisplayMetrics();
  }
}

這邊可以來做一些修改,讓他更貼近實際使用。

  • 設計圓角,讓畫面更好看

在res資料夾筆下新增drawable資料夾,裡面新增一個xml,例:drawable_round_edge.xml

在檔案中新增一個<shape>,設定背景顏色與圓角大小,如下

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <!--填充設定-->
    <solid android:color="#ff333333"/>

    <!--圓角設定-->
    <corners
        android:topLeftRadius="20dp"
        android:topRightRadius="20dp"/>

</shape>

在專案的bottom_sheet layout中,也就是你的拖拉畫面佈局中,在最頂層View設定背景為drawable_round_edge.xml

 

  • 限制展開大小

在預設的設計中,展開拖拉畫面布局時,會蓋住整個畫面,就像下圖一樣。

大部分會希望能看到部分背景,所以我們要限制拖拉畫面的高度,讓他可以看見部分背景佈局,如下圖。

增加程式碼功能:

首先增加一個計算畫面高度的功能:

public float convertPercentageToPixel(float percentage, Context context) {
  return getDisplayMetrics(context).heightPixels * percentage;
}

這邊會抓取app畫面的高度,然後再乘以input進來的percentage比例,結果就是我們想要顯示的比例高度(上圖範例為占用70%)

然後在findViews()中增加拖拉布局的頂層畫面物件:

private void findViews() {
  mBottomSheet = findViewById(R.id.bottom_sheet);
  mExpandIconView = (ExpandIconView) mBottomSheet.findViewById(R.id.expandIconView);
  //---加入此行---
  mbottom_sheet = (FrameLayout) mBottomSheet.findViewById(R.id.bottom_sheet);
}

接下來在setUpViews()中設定mbottom_sheet布局的高度:

private void setUpViews() {
  //---加入這區---
  //取得View的參數
  ViewGroup.LayoutParams params = mbottom_sheet.getLayoutParams();
  //設定View的高度,按比例計算
  params.height = (int)convertPercentageToPixel(0.7f, this);
  //設定View的參數
  mbottom_sheet.setLayoutParams(params);
  //---加入這區---

  mExpandIconView.setState(ExpandIconView.LESS, true);
  mBottomSheetBehavior = BottomSheetBehavior.from(mBottomSheet);
  mBottomSheetBehavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {

    @Override
    public void onStateChanged(@NonNull View bottomSheet, int newState) {
      if (newState == BottomSheetBehavior.STATE_COLLAPSED) {
        mExpandIconView.setState(ExpandIconView.LESS, true);
      } else if (newState == BottomSheetBehavior.STATE_EXPANDED) {
        mExpandIconView.setState(ExpandIconView.MORE, true);
      }
...
...

這樣在拖拉佈局產生之後,就會按畫面高度比例去設定拖拉布局的最大高度了!

-END-

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *