Android 使用自定義Dialog打造ActionSheet選單
阿新 • • 發佈:2019-01-25
前言
這幾天用到了github上的一個仿IOS的ActionSheet(ActionSheetForAndroid)控制元件,它是使用Fragment實現的,由於我的主介面也是由幾個Fragment來做,所以這樣會造成FragmentManager出現一些問題。所以就把它的實現方式改為由自定義Dialog來實現,保留它的主題屬性方便修改樣式,同時去除Fragmrnt部分,增加可用性。
實現
package com.roc.actionsheet; import java.util.Arrays; import java.util.List; import android.app.Activity; import android.app.Dialog; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Color; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.StateListDrawable; import android.util.TypedValue; import android.view.Gravity; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup.LayoutParams; import android.view.animation.AlphaAnimation; import android.view.animation.Animation; import android.view.animation.TranslateAnimation; import android.view.inputmethod.InputMethodManager; import android.widget.Button; import android.widget.FrameLayout; import android.widget.LinearLayout; /** * ActionSheet * * @author Mr.Zheng * @date 2014年9月7日 下午11:15:54 */ public class ActionSheet extends Dialog implements OnClickListener { /* 控制元件的id */ private static final int CANCEL_BUTTON_ID = 100; private static final int BG_VIEW_ID = 10; private static final int TRANSLATE_DURATION = 300; private static final int ALPHA_DURATION = 300; private Context mContext; private Attributes mAttrs; private MenuItemClickListener mListener; private View mView; private LinearLayout mPanel; private View mBg; private List<String> items; private String cancelTitle = ""; private boolean mCancelableOnTouchOutside; private boolean mDismissed = true; private boolean isCancel = true; public ActionSheet(Context context) { super(context, android.R.style.Theme_Light_NoTitleBar);// 全屏 this.mContext = context; initViews(); getWindow().setGravity(Gravity.BOTTOM); Drawable drawable = new ColorDrawable(); drawable.setAlpha(0);// 設定透明背景 getWindow().setBackgroundDrawable(drawable); } public void initViews() { /* 隱藏軟鍵盤 */ InputMethodManager imm = (InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE); if (imm.isActive()) { View focusView = ((Activity) mContext).getCurrentFocus(); if (focusView != null) imm.hideSoftInputFromWindow(focusView.getWindowToken(), 0); } mAttrs = readAttribute();// 獲取主題屬性 mView = createView(); mBg.startAnimation(createAlphaInAnimation()); mPanel.startAnimation(createTranslationInAnimation()); } private Animation createTranslationInAnimation() { int type = TranslateAnimation.RELATIVE_TO_SELF; TranslateAnimation an = new TranslateAnimation(type, 0, type, 0, type, 1, type, 0); an.setDuration(TRANSLATE_DURATION); return an; } private Animation createAlphaInAnimation() { AlphaAnimation an = new AlphaAnimation(0, 1); an.setDuration(ALPHA_DURATION); return an; } private Animation createTranslationOutAnimation() { int type = TranslateAnimation.RELATIVE_TO_SELF; TranslateAnimation an = new TranslateAnimation(type, 0, type, 0, type, 0, type, 1); an.setDuration(TRANSLATE_DURATION); an.setFillAfter(true); return an; } private Animation createAlphaOutAnimation() { AlphaAnimation an = new AlphaAnimation(1, 0); an.setDuration(ALPHA_DURATION); an.setFillAfter(true); return an; } /** * 建立基本的背景檢視 */ private View createView() { FrameLayout parent = new FrameLayout(mContext); FrameLayout.LayoutParams parentParams = new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); parentParams.gravity = Gravity.BOTTOM; parent.setLayoutParams(parentParams); mBg = new View(mContext); mBg.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); mBg.setBackgroundColor(Color.argb(136, 0, 0, 0)); mBg.setId(BG_VIEW_ID); mBg.setOnClickListener(this); mPanel = new LinearLayout(mContext); FrameLayout.LayoutParams mPanelParams = new FrameLayout.LayoutParams( FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.WRAP_CONTENT); mPanelParams.gravity = Gravity.BOTTOM; mPanel.setLayoutParams(mPanelParams); mPanel.setOrientation(LinearLayout.VERTICAL); parent.addView(mBg); parent.addView(mPanel); return parent; } /** * 建立MenuItem */ private void createItems() { if (items != null && items.size() > 0) for (int i = 0; i < items.size(); i++) { Button bt = new Button(mContext); bt.setId(CANCEL_BUTTON_ID + i + 1); bt.setOnClickListener(this); bt.setBackgroundDrawable(getOtherButtonBg(items.toArray(new String[items.size()]), i)); bt.setText(items.get(i)); bt.setTextColor(mAttrs.otherButtonTextColor); bt.setTextSize(TypedValue.COMPLEX_UNIT_PX, mAttrs.actionSheetTextSize); if (i > 0) { LinearLayout.LayoutParams params = createButtonLayoutParams(); params.topMargin = mAttrs.otherButtonSpacing; mPanel.addView(bt, params); } else mPanel.addView(bt); } Button bt = new Button(mContext); bt.getPaint().setFakeBoldText(true); bt.setTextSize(TypedValue.COMPLEX_UNIT_PX, mAttrs.actionSheetTextSize); bt.setId(CANCEL_BUTTON_ID); bt.setBackgroundDrawable(mAttrs.cancelButtonBackground); bt.setText(cancelTitle); bt.setTextColor(mAttrs.cancelButtonTextColor); bt.setOnClickListener(this); LinearLayout.LayoutParams params = createButtonLayoutParams(); params.topMargin = mAttrs.cancelButtonMarginTop; mPanel.addView(bt, params); mPanel.setBackgroundDrawable(mAttrs.background); mPanel.setPadding(mAttrs.padding, mAttrs.padding, mAttrs.padding, mAttrs.padding); } public LinearLayout.LayoutParams createButtonLayoutParams() { LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); return params; } /** * item按鈕的顏色 * * @param titles * @param i * @return */ private Drawable getOtherButtonBg(String[] titles, int i) { if (titles.length == 1) return mAttrs.otherButtonSingleBackground; else if (titles.length == 2) switch (i) { case 0: return mAttrs.otherButtonTopBackground; case 1: return mAttrs.otherButtonBottomBackground; } else if (titles.length > 2) { if (i == 0) return mAttrs.otherButtonTopBackground; else if (i == (titles.length - 1)) return mAttrs.otherButtonBottomBackground; return mAttrs.getOtherButtonMiddleBackground(); } return null; } public void showMenu() { if (!mDismissed) return; show(); getWindow().setContentView(mView); mDismissed = false; } /** * dissmiss Menu選單 */ public void dismissMenu() { if (mDismissed) return; dismiss(); onDismiss(); mDismissed = true; } /** * dismiss時的處理 */ private void onDismiss() { mPanel.startAnimation(createTranslationOutAnimation()); mBg.startAnimation(createAlphaOutAnimation()); } /** * 取消按鈕的標題文字 * * @param title * @return */ public ActionSheet setCancelButtonTitle(String title) { this.cancelTitle = title; return this; } /** * 取消按鈕的標題文字 * * @param strId * @return */ public ActionSheet setCancelButtonTitle(int strId) { return setCancelButtonTitle(mContext.getString(strId)); } /** * 點選外部邊緣是否可取消 * * @param cancelable * @return */ public ActionSheet setCancelableOnTouchMenuOutside(boolean cancelable) { mCancelableOnTouchOutside = cancelable; return this; } public ActionSheet addItems(String... titles) { if (titles == null || titles.length == 0) return this; items = Arrays.asList(titles); createItems(); return this; } public ActionSheet setItemClickListener(MenuItemClickListener listener) { this.mListener = listener; return this; } private Attributes readAttribute() { Attributes attrs = new Attributes(mContext); TypedArray a = mContext.getTheme().obtainStyledAttributes(null, R.styleable.ActionSheet, R.attr.actionSheetStyle, 0); Drawable background = a.getDrawable(R.styleable.ActionSheet_actionSheetBackground); if (background != null) attrs.background = background; Drawable cancelButtonBackground = a.getDrawable(R.styleable.ActionSheet_cancelButtonBackground); if (cancelButtonBackground != null) attrs.cancelButtonBackground = cancelButtonBackground; Drawable otherButtonTopBackground = a.getDrawable(R.styleable.ActionSheet_otherButtonTopBackground); if (otherButtonTopBackground != null) attrs.otherButtonTopBackground = otherButtonTopBackground; Drawable otherButtonMiddleBackground = a .getDrawable(R.styleable.ActionSheet_otherButtonMiddleBackground); if (otherButtonMiddleBackground != null) attrs.otherButtonMiddleBackground = otherButtonMiddleBackground; Drawable otherButtonBottomBackground = a .getDrawable(R.styleable.ActionSheet_otherButtonBottomBackground); if (otherButtonBottomBackground != null) attrs.otherButtonBottomBackground = otherButtonBottomBackground; Drawable otherButtonSingleBackground = a .getDrawable(R.styleable.ActionSheet_otherButtonSingleBackground); if (otherButtonSingleBackground != null) attrs.otherButtonSingleBackground = otherButtonSingleBackground; attrs.cancelButtonTextColor = a.getColor(R.styleable.ActionSheet_cancelButtonTextColor, attrs.cancelButtonTextColor); attrs.otherButtonTextColor = a.getColor(R.styleable.ActionSheet_otherButtonTextColor, attrs.otherButtonTextColor); attrs.padding = (int) a.getDimension(R.styleable.ActionSheet_actionSheetPadding, attrs.padding); attrs.otherButtonSpacing = (int) a.getDimension(R.styleable.ActionSheet_otherButtonSpacing, attrs.otherButtonSpacing); attrs.cancelButtonMarginTop = (int) a.getDimension(R.styleable.ActionSheet_cancelButtonMarginTop, attrs.cancelButtonMarginTop); attrs.actionSheetTextSize = a.getDimensionPixelSize(R.styleable.ActionSheet_actionSheetTextSize, (int) attrs.actionSheetTextSize); a.recycle(); return attrs; } @Override public void onClick(View v) { if (v.getId() == BG_VIEW_ID && !mCancelableOnTouchOutside) return; dismissMenu(); if (v.getId() != CANCEL_BUTTON_ID && v.getId() != BG_VIEW_ID) { if (mListener != null) mListener.onItemClick(v.getId() - CANCEL_BUTTON_ID - 1); isCancel = false; } } /** * 自定義屬性的控制元件主題 * * @author Mr.Zheng * @date 2014年9月7日 下午10:47:06 */ private class Attributes { private Context mContext; private Drawable background; private Drawable cancelButtonBackground; private Drawable otherButtonTopBackground; private Drawable otherButtonMiddleBackground; private Drawable otherButtonBottomBackground; private Drawable otherButtonSingleBackground; private int cancelButtonTextColor; private int otherButtonTextColor; private int padding; private int otherButtonSpacing; private int cancelButtonMarginTop; private float actionSheetTextSize; public Attributes(Context context) { mContext = context; this.background = new ColorDrawable(Color.TRANSPARENT); this.cancelButtonBackground = new ColorDrawable(Color.BLACK); ColorDrawable gray = new ColorDrawable(Color.GRAY); this.otherButtonTopBackground = gray; this.otherButtonMiddleBackground = gray; this.otherButtonBottomBackground = gray; this.otherButtonSingleBackground = gray; this.cancelButtonTextColor = Color.WHITE; this.otherButtonTextColor = Color.BLACK; this.padding = dp2px(20); this.otherButtonSpacing = dp2px(2); this.cancelButtonMarginTop = dp2px(10); this.actionSheetTextSize = dp2px(16); } private int dp2px(int dp) { return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, mContext.getResources() .getDisplayMetrics()); } public Drawable getOtherButtonMiddleBackground() { if (otherButtonMiddleBackground instanceof StateListDrawable) { TypedArray a = mContext.getTheme().obtainStyledAttributes(null, R.styleable.ActionSheet, R.attr.actionSheetStyle, 0); otherButtonMiddleBackground = a .getDrawable(R.styleable.ActionSheet_otherButtonMiddleBackground); a.recycle(); } return otherButtonMiddleBackground; } } public static interface MenuItemClickListener { void onItemClick(int itemPosition); } }
程式碼中要注意的地方就是dialog的自定義view要在底部出現,背景要透明,dialog要全屏,這些主要在ActionSheet構造方法裡設定,即55到61行,然後動態新增控制元件時注意一下Params就好。其它地方也沒什麼改動了。
使用
主題我也是直接使用它的,資源配置檔案就不貼上來了,其中的屬性代表的對應控制元件去參看ActionSheetForAndroid吧,最後貼上原始碼地址,改大小、顏色、背景什麼的到主題裡設定一下換下圖片就行了。
我們可以在style直接設定ActionSheet的主題:
<resources> <style name="AppBaseTheme" parent="android:Theme.Light.NoTitleBar"></style> <style name="AppTheme" parent="AppBaseTheme"> <!-- ActionSheet --> <item name="actionSheetBackground">@android:color/transparent</item> <item name="cancelButtonBackground">@drawable/actionsheet_slt_as_ios7_cancel_bt</item> <item name="otherButtonTopBackground">@drawable/actionsheet_slt_as_ios7_other_bt_top</item> <item name="otherButtonMiddleBackground">@drawable/actionsheet_slt_as_ios7_other_bt_middle</item> <item name="otherButtonBottomBackground">@drawable/actionsheet_slt_as_ios7_other_bt_bottom</item> <item name="otherButtonSingleBackground">@drawable/actionsheet_slt_as_ios7_other_bt_single</item> <item name="cancelButtonTextColor">#1E82FF</item> <item name="otherButtonTextColor">#1E82FF</item> <item name="actionSheetPadding">10dp</item> <item name="otherButtonSpacing">0dp</item> <item name="cancelButtonMarginTop">10dp</item> <item name="actionSheetTextSize">16sp</item> </style> </resources>
也可以在程式碼裡才去設定主題:
package com.roc.actionsheet; import android.os.Bundle; import android.support.v4.app.FragmentActivity; import android.view.View; import android.widget.Toast; import com.roc.actionsheet.ActionSheet.MenuItemClickListener; /** * @author Mr.Zheng * @date 2014年9月8日 上午12:08:55 */ public class MainActivity extends FragmentActivity implements MenuItemClickListener { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } public void onClick(View v) { switch (v.getId()) { case R.id.ios6: setTheme(R.style.ActionSheetStyleIOS6); break; case R.id.ios7: setTheme(R.style.ActionSheetStyleIOS7); break; } showActionSheet(); } public void showActionSheet() { ActionSheet menuView = new ActionSheet(this); menuView.setCancelButtonTitle("cancel");// before add items menuView.addItems("Item1", "Item2", "Item3", "Item4"); menuView.setItemClickListener(this); menuView.setCancelableOnTouchMenuOutside(true); menuView.showMenu(); } @Override public void onItemClick(int itemPosition) { Toast.makeText(this, (itemPosition + 1) + " click", 0).show(); } }
是不是挺簡單的。下面來看下效果
效果是和用Fragment實現一樣的。