亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

安卓BottomSheet實現——動態加載視圖

2019-11-07 23:57:25
字體:
來源:轉載
供稿:網友

安卓BottomSheet實現——動態加載視圖

在上一節之后,我們可以開始構建視圖了。

構建器

我們可以制作一個構建器,這個構建器主要用來干嘛呢?通過Menu文件來使用適配器等工具來構建整個BottomSheet的視圖。因為我們的控件想要做到不事先在xml中定義,整體都是使用代碼來動態生成,即插即用型,所以我們就有了這么一個構建器來動態生成BottomSheet這一視圖。新建 BottomSheetAdapterBuilder.java

public class BottomSheetAdapterBuilder { PRivate List<BottomSheetItem> mItems; private int mTitles; private int mMode; private Menu mMenu; private boolean mFromMenu; private Context mContext; public BottomSheetAdapterBuilder(Context context) { mContext = context; mItems = new ArrayList<>(); } public void setMenu(Menu menu) { mMenu = menu; mFromMenu = true; } public void setMode(int mode) { mMode = mode; } public void addTitleItem(String title, int titleTextColor) { mItems.add(new BottomSheetHeader(title, titleTextColor)); } public void addDividerItem(int dividerBackground) { mItems.add(new BottomSheetDivider(dividerBackground)); } public void addItem(int id, String title, Drawable icon, int itemTextColor, int itemBackground, int tintColor) { if (mMenu == null) { mMenu = new MenuBuilder(mContext); } MenuItem item = mMenu.add(Menu.NONE, id, Menu.NONE, title); item.setIcon(icon); mItems.add(new BottomSheetMenuItem(item, itemTextColor, itemBackground, tintColor)); } @SuppressLint("InflateParams") public View createView(int titleTextColor, int backgroundDrawable, int backgroundColor, int dividerBackground, int itemTextColor, int itemBackground, int tintColor, BottomSheetItemClickListener itemClickListener) { if (mFromMenu) { mItems = createAdapterItems(dividerBackground, titleTextColor, itemTextColor, itemBackground, tintColor); } LayoutInflater layoutInflater = LayoutInflater.from(mContext); View sheet = mMode == BottomSheetBuilder.MODE_GRID ? layoutInflater.inflate(R.layout.bottomsheetbuilder_sheet_grid, null) : layoutInflater.inflate(R.layout.bottomsheetbuilder_sheet_list, null); final RecyclerView recyclerView = (RecyclerView) sheet.findViewById(R.id.recyclerView); recyclerView.setHasFixedSize(true); if (backgroundDrawable != 0) { sheet.setBackgroundResource(backgroundDrawable); } else { if (backgroundColor != 0) { sheet.setBackgroundColor(backgroundColor); } } // If we only have one title and it's the first item, set it as fixed if (mTitles == 1 && mMode == BottomSheetBuilder.MODE_LIST) { BottomSheetItem header = mItems.get(0); TextView headerTextView = (TextView) sheet.findViewById(R.id.textView); if (header instanceof BottomSheetHeader) { headerTextView.setVisibility(View.VISIBLE); headerTextView.setText(header.getTitle()); if (titleTextColor != 0) { headerTextView.setTextColor(titleTextColor); } mItems.remove(0); } } final BottomSheetItemAdapter adapter = new BottomSheetItemAdapter(mItems, mMode, itemClickListener); if (mMode == BottomSheetBuilder.MODE_LIST) { recyclerView.setLayoutManager(new LinearLayoutManager(mContext)); recyclerView.setAdapter(adapter); } else { final int columns = mContext.getResources().getInteger(R.integer.bottomsheet_grid_columns); GridLayoutManager layoutManager = new GridLayoutManager(mContext, columns); recyclerView.setLayoutManager(layoutManager); recyclerView.post(new Runnable() { @Override public void run() { float margin = mContext.getResources() .getDimensionPixelSize(R.dimen.bottomsheet_grid_horizontal_margin); adapter.setItemWidth((int) ((recyclerView.getWidth() - 2 * margin) / columns)); recyclerView.setAdapter(adapter); } }); } return sheet; } public List<BottomSheetItem> getItems() { return mItems; } private List<BottomSheetItem> createAdapterItems(int dividerBackground, int titleTextColor, int itemTextColor, int itemBackground, int tintColor) { List<BottomSheetItem> items = new ArrayList<>(); mTitles = 0; boolean addedSubMenu = false; for (int i = 0; i < mMenu.size(); i++) { MenuItem item = mMenu.getItem(i); if (item.isVisible()) { if (item.hasSubMenu()) { SubMenu subMenu = item.getSubMenu(); if (i != 0 && addedSubMenu) { if (mMode == BottomSheetBuilder.MODE_GRID) { throw new IllegalArgumentException("MODE_GRID can't have submenus." + " Use MODE_LIST instead"); } items.add(new BottomSheetDivider(dividerBackground)); } CharSequence title = item.getTitle(); if (title != null && !title.equals("")) { items.add(new BottomSheetHeader(title.toString(), titleTextColor)); mTitles++; } for (int j = 0; j < subMenu.size(); j++) { MenuItem subItem = subMenu.getItem(j); if (subItem.isVisible()) { items.add(new BottomSheetMenuItem(subItem, itemTextColor, itemBackground, tintColor)); addedSubMenu = true; } } } else { items.add(new BottomSheetMenuItem(item, itemTextColor, itemBackground, tintColor)); } } } return items; }}

首先我們來看 createAdapterItems 函數,該函數的作用就是從Menu中讀取item來得到items。對于MenuItem是可能有SubMenu的,所以我們也必須要對這個進行檢測。但是對于grid類型的是不能有子菜單的,所以我們會拋出一個錯誤。對于有子菜單的item,我們把他升級為標題類型,然后在讀取子菜單項。

最重要的是 createView 函數,作用當然是動態創建BottomSheet了。首先是從Menu中讀取出items,然后根據類型來構建出一個view對象。接下來有個特殊操作,如果只有一個標題的話,將其固定。

根據類型的不同,也使用不同LayoutManager,這一點也不需要解釋,但是在grid類型中,需要計算每一個item的寬度是多少的小計算,減去兩邊的margin,中間的一除即可。然后搭配上適配器即可。這樣的話整個BottomSheet的視圖就動態搭建完成了。

動態搭建完成后,我們還需要將這個部件放進我們的主要視圖中,這里必須要說明一點,BottomSheet必須要是在CoordinatorLayout中才能使用,因為他的一些behavior折疊操作都是需要在CoordinatorLayout中使用。

如何在主視圖中顯示

這里采用了一個從底部彈起的方式,所以繼承了 BottomSheetDialog

public class BottomSheetMenuDialog extends BottomSheetDialog implements BottomSheetItemClickListener { BottomSheetBehavior.BottomSheetCallback mCallback; BottomSheetBehavior mBehavior; private BottomSheetItemClickListener mClickListener; private AppBarLayout mAppBarLayout; private boolean mExpandOnStart; private boolean mDelayDismiss; boolean mRequestedExpand; boolean mClicked; boolean mRequestCancel; boolean mRequestDismiss; OnCancelListener mOnCancelListener; public BottomSheetMenuDialog(Context context) { super(context); } public BottomSheetMenuDialog(Context context, int theme) { super(context, theme); } /** * Dismiss the BottomSheetDialog while animating the sheet. */ public void dismissWithAnimation() { if (mBehavior != null) { mBehavior.setState(BottomSheetBehavior.STATE_HIDDEN); } } @Override public void setOnCancelListener(OnCancelListener listener) { super.setOnCancelListener(listener); mOnCancelListener = listener; } @Override public void cancel() { mRequestCancel = true; super.cancel(); } @Override public void dismiss() { mRequestDismiss = true; if (mRequestCancel) { dismissWithAnimation(); } else { super.dismiss(); } } @Override protected void onStart() { super.onStart(); final FrameLayout sheet = (FrameLayout) findViewById(R.id.design_bottom_sheet); if (sheet != null) { mBehavior = BottomSheetBehavior.from(sheet); mBehavior.setBottomSheetCallback(mBottomSheetCallback); mBehavior.setSkipCollapsed(true); if (getContext().getResources().getBoolean(R.bool.tablet_landscape)) { CoordinatorLayout.LayoutParams layoutParams = (CoordinatorLayout.LayoutParams) sheet.getLayoutParams(); layoutParams.width = getContext().getResources() .getDimensionPixelSize(R.dimen.bottomsheet_width); sheet.setLayoutParams(layoutParams); } // Make sure the sheet doesn't overlap the appbar if (mAppBarLayout != null) { if (mAppBarLayout.getHeight() == 0) { mAppBarLayout.getViewTreeObserver() .addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { applyAppbarMargin(sheet); } }); } else { applyAppbarMargin(sheet); } } if (getContext().getResources().getBoolean(R.bool.landscape)) { fixLandscapePeekHeight(sheet); } if (mExpandOnStart) { sheet.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { mBehavior.setState(BottomSheetBehavior.STATE_EXPANDED); if (mBehavior.getState() == BottomSheetBehavior.STATE_SETTLING && mRequestedExpand) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { sheet.getViewTreeObserver().removeOnGlobalLayoutListener(this); } else { //noinspection deprecation sheet.getViewTreeObserver().removeGlobalOnLayoutListener(this); } } mRequestedExpand = true; } }); } } } public void setAppBar(AppBarLayout appBar) { mAppBarLayout = appBar; } public void expandOnStart(boolean expand) { mExpandOnStart = expand; } public void delayDismiss(boolean dismiss) { mDelayDismiss = dismiss; } public void setBottomSheetCallback(BottomSheetBehavior.BottomSheetCallback callback) { mCallback = callback; } public void setBottomSheetItemClickListener(BottomSheetItemClickListener listener) { mClickListener = listener; } public BottomSheetBehavior getBehavior() { return mBehavior; } @Override public void onBottomSheetItemClick(MenuItem item) { if (!mClicked) { if (mBehavior != null) { if (mDelayDismiss) { BottomSheetBuilderUtils.delayDismiss(mBehavior); } else { mBehavior.setState(BottomSheetBehavior.STATE_HIDDEN); } } if (mClickListener != null) { mClickListener.onBottomSheetItemClick(item); } mClicked = true; } } private BottomSheetBehavior.BottomSheetCallback mBottomSheetCallback = new BottomSheetBehavior.BottomSheetCallback() { @Override public void onStateChanged(@NonNull View bottomSheet, @BottomSheetBehavior.State int newState) { if (mCallback != null) { mCallback.onStateChanged(bottomSheet, newState); } //noinspection WrongConstant if (newState == BottomSheetBehavior.STATE_HIDDEN) { mBehavior.setBottomSheetCallback(null); try { BottomSheetMenuDialog.super.dismiss(); }catch (IllegalArgumentException e){ // Ignore exception handling } // User dragged the sheet. if (!mClicked && !mRequestDismiss && !mRequestCancel && mOnCancelListener != null) { mOnCancelListener.onCancel(BottomSheetMenuDialog.this); } } } @Override public void onSlide(@NonNull View bottomSheet, float slideOffset) { if (mCallback != null) { mCallback.onSlide(bottomSheet, slideOffset); } } }; private void fixLandscapePeekHeight(final View sheet) { // On landscape, we shouldn't use the 16:9 keyline alignment sheet.post(new Runnable() { @Override public void run() { mBehavior.setPeekHeight(sheet.getHeight() / 2); } }); } private void applyAppbarMargin(View sheet) { CoordinatorLayout.LayoutParams layoutParams = (CoordinatorLayout.LayoutParams) sheet.getLayoutParams(); layoutParams.topMargin = mAppBarLayout.getHeight(); sheet.setLayoutParams(layoutParams); }}

在onStart函數中的 R.id.design_bottom_sheet 是安卓自帶的id來放置dialog。接下來的if判斷,是否處于平板中,若是在平板中,會更改整個dialog的寬度。然后判斷AppBar的高度,使BottomSheet不會蓋過Appbar。還有橫屏模式與自動開啟的邏輯處理。

還有一些關于點擊操作的處理,其中BottomSheetBuilderUtils是我們編寫的一個輔助工具類,用來儲存狀態與延遲關閉,會有0.3秒的時間來延遲,是否開啟這個功能由 mDelayDismiss 來決定。

BottomSheetBuilderUtils.javapublic class BottomSheetBuilderUtils { public static final String SAVED_STATE = "saved_behavior_state"; public static void delayDismiss(final BottomSheetBehavior behavior) { new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { @Override public void run() { behavior.setState(BottomSheetBehavior.STATE_HIDDEN); } }, 300); } public static void saveState(Bundle outState, BottomSheetBehavior behavior) { if (outState != null) { outState.putInt(SAVED_STATE, behavior.getState()); } } public static void restoreState(final Bundle savedInstanceState, final BottomSheetBehavior behavior) { if (savedInstanceState != null) { Handler handler = new Handler(Looper.getMainLooper()); handler.postDelayed(new Runnable() { @Override public void run() { int state = savedInstanceState.getInt(SAVED_STATE); if (state == BottomSheetBehavior.STATE_EXPANDED && behavior != null) { behavior.setState(state); } } }, 300); } }}

加載進主視圖

主要的處理就是構建一個view來放入CoordinatorLayout的view中或dialog中,而構建view之前我們也已經寫好。核心函數就是

public View createView() { if (mMenu == null && mAdapterBuilder.getItems().isEmpty()) { throw new IllegalStateException("You need to provide at least one Menu " + "or an item with addItem"); } if (mCoordinatorLayout == null) { throw new IllegalStateException("You need to provide a coordinatorLayout" + "so the view can be placed on it"); } View sheet = mAdapterBuilder.createView(mTitleTextColor, mBackgroundDrawable, mBackgroundColor, mDividerBackground, mItemTextColor, mItemBackground, mIconTintColor, mItemClickListener); ViewCompat.setElevation(sheet, mContext.getResources() .getDimensionPixelSize(R.dimen.bottomsheet_elevation)); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { sheet.findViewById(R.id.fakeShadow).setVisibility(View.GONE); } CoordinatorLayout.LayoutParams layoutParams = new CoordinatorLayout.LayoutParams(CoordinatorLayout.LayoutParams.MATCH_PARENT, CoordinatorLayout.LayoutParams.WRAP_CONTENT); layoutParams.gravity = Gravity.CENTER_HORIZONTAL; layoutParams.setBehavior(new BottomSheetBehavior()); if (mContext.getResources().getBoolean(R.bool.tablet_landscape)) { layoutParams.width = mContext.getResources() .getDimensionPixelSize(R.dimen.bottomsheet_width); } mCoordinatorLayout.addView(sheet, layoutParams); mCoordinatorLayout.postInvalidate(); return sheet; } public BottomSheetMenuDialog createDialog() { if (mMenu == null && mAdapterBuilder.getItems().isEmpty()) { throw new IllegalStateException("You need to provide at least one Menu " + "or an item with addItem"); } BottomSheetMenuDialog dialog = mTheme == 0 ? new BottomSheetMenuDialog(mContext, R.style.BottomSheetBuilder_DialogStyle) : new BottomSheetMenuDialog(mContext, mTheme); if (mTheme != 0) { setupThemeColors(mContext.obtainStyledAttributes(mTheme, new int[]{ R.attr.bottomSheetBuilderBackgroundColor, R.attr.bottomSheetBuilderItemTextColor, R.attr.bottomSheetBuilderTitleTextColor})); } else { setupThemeColors(mContext.getTheme().obtainStyledAttributes(new int[]{ R.attr.bottomSheetBuilderBackgroundColor, R.attr.bottomSheetBuilderItemTextColor, R.attr.bottomSheetBuilderTitleTextColor,})); } View sheet = mAdapterBuilder.createView(mTitleTextColor, mBackgroundDrawable, mBackgroundColor, mDividerBackground, mItemTextColor, mItemBackground, mIconTintColor, dialog); sheet.findViewById(R.id.fakeShadow).setVisibility(View.GONE); dialog.setAppBar(mAppBarLayout); dialog.expandOnStart(mExpandOnStart); dialog.delayDismiss(mDelayedDismiss); dialog.setBottomSheetItemClickListener(mItemClickListener); if (mContext.getResources().getBoolean(R.bool.tablet_landscape)) { FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(mContext.getResources() .getDimensionPixelSize(R.dimen.bottomsheet_width), ViewGroup.LayoutParams.WRAP_CONTENT); dialog.setContentView(sheet, layoutParams); } else { dialog.setContentView(sheet); } return dialog; }

這樣的話,我們整個BottomSheet就構建完成了。

在項目中使用

public void onShowDialogGridClick() { if (mBottomSheetDialog != null) { mBottomSheetDialog.dismiss(); } mShowingGridDialog = true; mBottomSheetDialog = new BottomSheetBuilder(this, R.style.APPTheme_BottomSheetDialog) .setMode(BottomSheetBuilder.MODE_GRID) .setAppBarLayout(mAppBarLayout) .setMenu(getResources().getBoolean(R.bool.tablet_landscape) ? R.menu.menu_bottom_grid_tablet_sheet : R.menu.menu_bottom_grid_sheet) .expandOnStart(true) .setItemClickListener(new BottomSheetItemClickListener() { @Override public void onBottomSheetItemClick(MenuItem item) { mShowingGridDialog = false; } }) .createDialog(); mBottomSheetDialog.setOnCancelListener(new DialogInterface.OnCancelListener() { @Override public void onCancel(DialogInterface dialog) { mShowingGridDialog = false; } }); mBottomSheetDialog.show(); }

然后設置點擊事件調用函數即可。

本文github地址參考:

https://github.com/rubensousa/BottomSheetBuilder


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
亚洲一区二区自拍| 91精品国产综合久久香蕉的用户体验| 国产男女猛烈无遮挡91| 国产精品成人国产乱一区| 日韩欧美在线网址| 日韩av在线免费看| 国产亚洲精品日韩| 国产亚洲欧美aaaa| 久久久免费在线观看| 2019国产精品自在线拍国产不卡| 国产精品免费久久久| 欧美大片va欧美在线播放| 国产裸体写真av一区二区| 在线看日韩欧美| 亚洲天堂免费视频| 97在线观看免费高清| 亚洲精品视频网上网址在线观看| 理论片在线不卡免费观看| 91精品视频免费观看| 欧美性猛交丰臀xxxxx网站| 亚洲女人天堂av| 疯狂做受xxxx欧美肥白少妇| 国产97在线亚洲| 成人福利视频在线观看| 精品小视频在线| 久久精品成人动漫| 在线观看亚洲视频| 成人精品一区二区三区电影免费| 国产精品老牛影院在线观看| 日本欧美爱爱爱| 欧美日韩国产一区中文午夜| 亚洲成人激情在线| 蜜臀久久99精品久久久久久宅男| 欧美色视频日本高清在线观看| 国产精品高精视频免费| 91chinesevideo永久地址| 2020欧美日韩在线视频| 欧美日韩第一页| 欧美成人在线免费视频| 日韩高清电影好看的电视剧电影| 亚洲欧美激情在线视频| 日产精品久久久一区二区福利| 91九色综合久久| 欧美色视频日本高清在线观看| 国产精品美女呻吟| 欧美午夜无遮挡| 国内精品久久久久影院 日本资源| 欧美色视频日本版| 日本欧美精品在线| 欧美日韩在线免费观看| 欧美第一页在线| 国产视频精品va久久久久久| 国产成人精品免高潮在线观看| 97av在线视频免费播放| 国产欧美日韩专区发布| 另类专区欧美制服同性| 久久久久久中文字幕| 成人av在线亚洲| 日韩av影片在线观看| 国产精品99久久久久久人| 日韩美女在线观看一区| 日韩暖暖在线视频| 91免费视频国产| 亚洲另类图片色| 九九热在线精品视频| 亚洲成人网在线观看| 97国产一区二区精品久久呦| 欧美激情亚洲另类| 色av中文字幕一区| 国产精品人人做人人爽| 全色精品综合影院| 国产v综合ⅴ日韩v欧美大片| zzjj国产精品一区二区| 亚洲黄色片网站| 亚洲视频在线视频| 国产精品96久久久久久| 国产69精品99久久久久久宅男| 欧美日韩国产精品专区| 欧美成人精品在线视频| 久久精品国亚洲| 成人激情视频免费在线| 91久久国产婷婷一区二区| 色老头一区二区三区| 欧美日韩在线观看视频| 亚洲人成网站999久久久综合| 精品无人国产偷自产在线| 国产精品视频一区二区三区四| 日韩av手机在线观看| 日韩一级裸体免费视频| 亚洲黄页视频免费观看| 成人国内精品久久久久一区| 狠狠做深爱婷婷久久综合一区| 国产日本欧美在线观看| 亚洲成人av片| 日韩av片永久免费网站| 成人黄色av播放免费| 欧美一区深夜视频| 欧美极品美女视频网站在线观看免费| 俺去亚洲欧洲欧美日韩| 91精品国产网站| 午夜精品久久久久久久白皮肤| 另类少妇人与禽zozz0性伦| 精品一区电影国产| 欧美日韩午夜剧场| 亚洲xxxx视频| 8090理伦午夜在线电影| 国产亚洲欧洲高清一区| 亚洲free嫩bbb| 久久久久久国产精品三级玉女聊斋| 亚洲日本成人女熟在线观看| 欧美一级视频在线观看| 丁香五六月婷婷久久激情| 久久天堂av综合合色| 国内精品久久久久久久| 成人疯狂猛交xxx| 国产美女久久久| 日韩视频中文字幕| 欧美一性一乱一交一视频| 午夜精品一区二区三区在线| 永久免费看mv网站入口亚洲| 日韩大陆毛片av| 亚洲第一页中文字幕| 亚洲视频综合网| 午夜精品蜜臀一区二区三区免费| 欧美性受xxxx白人性爽| 亚洲二区中文字幕| 亚洲国产日韩一区| 日韩精品免费在线播放| 欧美午夜激情小视频| 国产午夜精品视频免费不卡69堂| 热久久这里只有精品| 久久综合久久88| 日本精品va在线观看| 久久精品亚洲热| 亚洲成人网久久久| 91禁国产网站| 日韩美女在线看| 欧美亚洲国产日韩2020| 国产日韩欧美影视| 九九热最新视频//这里只有精品| 国产精品精品国产| 亚洲美女av黄| 第一福利永久视频精品| 亚洲一区二区自拍| 欧美性开放视频| 久久深夜福利免费观看| 国产亚洲一区精品| 国产原创欧美精品| 亚洲国产精品久久精品怡红院| 欧美在线精品免播放器视频| 精品国产鲁一鲁一区二区张丽| 国产精品自产拍在线观看中文| 一二美女精品欧洲| 96sao精品视频在线观看| 久久精品亚洲94久久精品| 久久久久久久久电影| 亚洲国产一区二区三区在线观看| 久久久久久久亚洲精品| 欧美野外wwwxxx| 大桥未久av一区二区三区| 欧美亚洲一区在线| 91精品国产九九九久久久亚洲| 欧美成人免费全部观看天天性色| 国内外成人免费激情在线视频|