国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

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

安卓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


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 鄄城县| 鸡东县| 玉屏| 霍邱县| 柳河县| 三河市| 佛教| 南川市| 屯留县| 井陉县| 天峨县| 邵阳县| 南郑县| 九江市| 邹城市| 安西县| 南安市| 牟定县| 广河县| 哈巴河县| 湖北省| 邵阳县| 新竹县| 桦南县| 招远市| 洛宁县| 灵武市| 怀来县| 清新县| 康马县| 乃东县| 辽中县| 贵南县| 桑植县| 广元市| 白朗县| 离岛区| 新津县| 太康县| 攀枝花市| 伽师县|