仿IOS底部弹出框
最近在公司的项目中涉及到这方面的需求,就想着封装个类。
首先是资源类drawable
action_sheet_bottom.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/action_sheet_bottom_pressed" android:state_pressed="true"/>
<item android:drawable="@drawable/action_sheet_bottom_pressed" android:state_selected="true"/>
<item android:drawable="@drawable/action_sheet_bottom_normal"/>
</selector>
action_sheet_cancel.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/action_sheet_single_pressed" android:state_pressed="true" />
<item android:drawable="@drawable/action_sheet_single_normal" />
</selector>
action_sheet_middle.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/action_sheet_middle_pressed" android:state_pressed="true" />
<item android:drawable="@drawable/action_sheet_middle_pressed" android:state_selected="true" />
<item android:drawable="@drawable/action_sheet_middle_normal" />
</selector>
action_sheet_single.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/action_sheet_single_pressed" android:state_pressed="true" />
<item android:drawable="@drawable/action_sheet_single_pressed" android:state_selected="true" />
<item android:drawable="@drawable/action_sheet_single_normal" />
</selector>
action_sheet_top.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/action_sheet_top_pressed" android:state_pressed="true"/>
<item android:drawable="@drawable/action_sheet_top_pressed" android:state_selected="true"/>
<item android:drawable="@drawable/action_sheet_top_normal"/>
</selector>
action_sheet_bottom_normal.xml
<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<solid android:color="@color/white" />
<corners
android:bottomLeftRadius="15dp"
android:bottomRightRadius="15dp" />
<padding
android:bottom="12dp"
android:left="24dp"
android:right="24dp"
android:top="12dp" />
</shape>
action_sheet_bottom_pressed.xml
<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/ash" />
<corners
android:bottomLeftRadius="15dp"
android:bottomRightRadius="15dp" />
<padding
android:bottom="12dp"
android:left="24dp"
android:right="24dp"
android:top="12dp" />
</shape>
action_sheet_middle_normal.xml
<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<solid android:color="@color/white" />
<padding
android:bottom="12dp"
android:left="24dp"
android:right="24dp"
android:top="12dp" />
</shape>
action_sheet_middle_pressed.xml
<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<solid android:color="@color/ash" />
<padding
android:bottom="12dp"
android:left="24dp"
android:right="24dp"
android:top="12dp" />
</shape>
action_sheet_single_normal.xml
<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<solid android:color="@color/white" />
<corners android:radius="15dp" />
<padding
android:bottom="12dp"
android:left="24dp"
android:right="24dp"
android:top="12dp" />
</shape>
action_sheet_single_pressed.xml
<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<solid android:color="@color/ash" />
<corners android:radius="15dp" />
<padding
android:bottom="12dp"
android:left="24dp"
android:right="24dp"
android:top="12dp" />
</shape>
action_sheet_top_normal.xml
<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<solid android:color="@color/white" />
<corners
android:topLeftRadius="15dp"
android:topRightRadius="15dp" />
<padding
android:bottom="12dp"
android:left="24dp"
android:right="24dp"
android:top="12dp" />
</shape>
action_sheet_top_pressed.xml
<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<solid android:color="@color/ash" />
<corners
android:topLeftRadius="15dp"
android:topRightRadius="15dp" />
<padding
android:bottom="12dp"
android:left="24dp"
android:right="24dp"
android:top="12dp" />
</shape>
至此,枯燥的资源类已经全部完成,接下来是定义主题样式
相关的两个文件放在values中才能被加载。
actionsheet__attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="ActionSheets">
<attr name="actionSheetStyle" format="reference" />
</declare-styleable>
<declare-styleable name="ActionSheet">
<attr name="actionSheetBackground" format="color|reference" />
<attr name="cancelButtonBackground" format="color|reference" />
<attr name="otherButtonTopBackground" format="color|reference" />
<attr name="otherButtonMiddleBackground" format="color|reference" />
<attr name="otherButtonBottomBackground" format="color|reference" />
<attr name="otherButtonSingleBackground" format="color|reference" />
<attr name="cancelButtonTextColor" format="color|reference" />
<attr name="otherButtonTextColor" format="color|reference" />
<attr name="actionSheetPadding" format="dimension|reference" />
<attr name="otherButtonSpacing" format="dimension|reference" />
<attr name="cancelButtonMarginTop" format="dimension|reference" />
<attr name="actionSheetTextSize" format="dimension|reference" />
</declare-styleable>
</resources>
actionsheet__theme.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="ActionSheetStyle">
<item name="actionSheetBackground">@android:color/transparent</item>
<item name="cancelButtonBackground">@drawable/action_sheet_cancel</item>
<item name="otherButtonTopBackground">@drawable/action_sheet_top</item>
<item name="otherButtonMiddleBackground">@drawable/action_sheet_middle</item>
<item name="otherButtonBottomBackground">@drawable/action_sheet_bottom</item>
<item name="otherButtonSingleBackground">@drawable/action_sheet_single</item>
<item name="cancelButtonTextColor">@color/black</item>
<item name="otherButtonTextColor">@color/black</item>
<item name="actionSheetPadding">10dp</item>
<item name="otherButtonSpacing">0dp</item>
<item name="cancelButtonMarginTop">10dp</item>
<item name="actionSheetTextSize">16sp</item>
</style>
</resources>
准备工作已经全部完成,接下来就是封装了。
/*
* 描述:仿ios底部弹出菜单栏
*/
public class ActionSheet extends Dialog implements View.OnClickListener {
//控件ID
private static final int BG_VIEW_ID = 10;
private static final int CANCEL_BUTTON_ID = 100;
//动画时常
private static final int ALPHA_DURATION = 150;
private static final int TRANSLATE_DURATION = 150;
private Context mContext;
private Attributes mAttrs;
private View mView;
private List<String> items;
private List<MenuItemClickListener> itemsListener;
private View mBg;
private LinearLayout mPanel;
private boolean mDismissed = true;//是否可以弹框
private String cancelTitle = "取消";//底部按钮显示
private boolean mCancelableOnTouchOutside;
private int paddingTop = 0;
public ActionSheet(Context context) {
super(context, android.R.style.Theme_Light_NoTitleBar);
this.mContext = context;
InputMethodHidden();
initViews();
removeBackground();
}
/**
* 添加条目
*/
public void addItem(String item, MenuItemClickListener menuItemClickListener) {
if (null != item) {
items.add(item);
itemsListener.add(menuItemClickListener);
}
}
/**
* 改变底部按钮文字
*/
public ActionSheet setCancelButtonTitle(String tltle) {
this.cancelTitle = tltle;
return this;
}
/**
* 点击外部视图是否消失
*/
public ActionSheet setCancelableOnTouchMenuOutside(boolean cancelable) {
this.mCancelableOnTouchOutside = cancelable;
return this;
}
//视图显示
public void showMenu() {
if (!mDismissed) return;
createItems();
super.show();
getWindow().setContentView(mView);
mDismissed = false;
}
@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) {
final int position = v.getId() - CANCEL_BUTTON_ID - 1;
if (0 <= position && null != itemsListener && position < itemsListener.size()) {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
itemsListener.get(position).onItemClick(position);
}
}, TRANSLATE_DURATION);
}
}
}
/**
* 输入法隐藏
*/
private void InputMethodHidden() {
InputMethodManager imm = (InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
if (null != imm && imm.isActive()) {
View currentFocus = getCurrentFocus();
if (currentFocus != null) imm.hideSoftInputFromWindow(currentFocus.getWindowToken(), 0);
}
}
//初始化
private void initViews() {
mContext.setTheme(R.style.ActionSheetStyle);//动态设置 需隐藏本行
mAttrs = readAttribute();
mView = createView();
mBg.startAnimation(createAlphaInAnimation());
mPanel.startAnimation(createTranslationInAnimation());
items = new ArrayList<>();
itemsListener = new ArrayList<>();
}
/**
* 去掉黑色背景
*/
private void removeBackground() {
getWindow().setGravity(Gravity.BOTTOM);
Drawable drawable = new ColorDrawable();
drawable.setAlpha(0);// 去除黑色背景
getWindow().setBackgroundDrawable(drawable);
}
/**
* 创建背景视图
*/
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 FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
mBg.setBackgroundColor(Color.argb(136, 0, 0, 0));
mBg.setId(BG_VIEW_ID);
mBg.setOnClickListener(this);
final ScrollView scrollView = new ScrollView(mContext);
FrameLayout.LayoutParams mPanelParams = new FrameLayout.LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
mPanelParams.gravity = Gravity.BOTTOM;
scrollView.setLayoutParams(mPanelParams);
scrollView.setVerticalScrollBarEnabled(false);
mPanel = new LinearLayout(mContext);
mPanel.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
ScrollView.LayoutParams layoutParams = new ScrollView.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
mPanel.setLayoutParams(layoutParams);
mPanel.setOrientation(LinearLayout.VERTICAL);
scrollView.addView(mPanel);
parent.addView(mBg);
parent.addView(scrollView);
mPanel.post(new Runnable() {
@Override
public void run() {
paddingTop = scrollView.getHeight() - mPanel.getHeight();
if (paddingTop > 0) {
mPanel.setPadding(mAttrs.padding, paddingTop + mAttrs.padding, mAttrs.padding, mAttrs.padding);
} else {
mPanel.setPadding(mAttrs.padding, mAttrs.padding, mAttrs.padding, mAttrs.padding);
}
}
});
return parent;
}
/**
* 创建条目
*/
private void createItems() {
mPanel.removeAllViews();
ColorStateList color = getContext().getResources().getColorStateList(R.color.black);//条目字体颜色
if (null != items && items.size() > 0) for (int i = 0; i < items.size(); i++) {
TextView textView = new TextView(mContext);
textView.setId(CANCEL_BUTTON_ID + i + 1);
textView.setOnClickListener(this);
textView.setBackgroundDrawable(getItemBackground(items.toArray(new String[items.size()]), i));
textView.setGravity(Gravity.CENTER);
textView.setText(items.get(i));
textView.setTextColor(color);
textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, mAttrs.actionSheetTextSize);
if (i > 0) {
View line = new View(mContext);
line.setBackgroundColor(0xFFD9D9DE);
line.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 1));
mPanel.addView(line);
}
mPanel.addView(textView);
}
TextView textView = new TextView(mContext);
textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, mAttrs.actionSheetTextSize);
textView.setId(CANCEL_BUTTON_ID);
textView.setBackgroundDrawable(mAttrs.cancelButtonBackground);
textView.setGravity(Gravity.CENTER);
textView.setText(cancelTitle);
textView.setTextColor(color);
textView.setOnClickListener(this);
LinearLayout.LayoutParams params = createButtonLayoutParams();
params.topMargin = mAttrs.cancelButtonMarginTop;
mPanel.addView(textView, params);
mPanel.setBackgroundDrawable(mAttrs.background);
if (paddingTop > 0) {
mPanel.setPadding(mAttrs.padding, paddingTop + mAttrs.padding, mAttrs.padding, mAttrs.padding);
} else {
mPanel.setPadding(mAttrs.padding, mAttrs.padding, mAttrs.padding, mAttrs.padding);
}
}
/**
* 销毁视图
*/
private void dismissMenu() {
if (mDismissed) {
return;
}
onDismiss();
mDismissed = true;
}
private void onDismiss() {
mPanel.startAnimation(createTranslationOutAnimation());
mBg.startAnimation(createAlphaOutAnimation());
}
/**
* 读取自定义attr属性
*/
private Attributes readAttribute() {
Attributes attributes = 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 (null != background) attributes.background = background;
Drawable cancelButtonBackground = a.getDrawable(R.styleable.ActionSheet_cancelButtonBackground);
if (null != cancelButtonBackground)
attributes.cancelButtonBackground = cancelButtonBackground;
Drawable otherButtonTopBackground = a.getDrawable(R.styleable.ActionSheet_otherButtonTopBackground);
if (null != otherButtonTopBackground)
attributes.otherButtonTopBackground = otherButtonTopBackground;
Drawable otherButtonMiddleBackground = a.getDrawable(R.styleable.ActionSheet_otherButtonMiddleBackground);
if (null != otherButtonMiddleBackground)
attributes.otherButtonMiddleBackground = otherButtonMiddleBackground;
Drawable otherButtonBottomBackground = a.getDrawable(R.styleable.ActionSheet_otherButtonBottomBackground);
if (null != otherButtonBottomBackground)
attributes.otherButtonBottomBackground = otherButtonBottomBackground;
Drawable otherButtonSingleBackground = a.getDrawable(R.styleable.ActionSheet_otherButtonSingleBackground);
if (null != otherButtonSingleBackground)
attributes.otherButtonSingleBackground = otherButtonSingleBackground;
attributes.cancelButtonTextColor = a.getColor(R.styleable.ActionSheet_cancelButtonTextColor, attributes.cancelButtonTextColor);
attributes.otherButtonTextColor = a.getColor(R.styleable.ActionSheet_otherButtonTextColor, attributes.otherButtonTextColor);
attributes.padding = (int) a.getDimension(R.styleable.ActionSheet_actionSheetPadding, attributes.padding);
attributes.otherButtonSpacing = (int) a.getDimension(R.styleable.ActionSheet_otherButtonSpacing, attributes.otherButtonSpacing);
attributes.cancelButtonMarginTop = (int) a.getDimension(R.styleable.ActionSheet_cancelButtonMarginTop, attributes.cancelButtonMarginTop);
attributes.actionSheetTextSize = a.getDimensionPixelSize(R.styleable.ActionSheet_actionSheetTextSize, (int) attributes.actionSheetTextSize);
a.recycle();
return attributes;
}
/**
* 条目样式
*/
private Drawable getItemBackground(String[] titles, int item) {
Log.d("getItemBackground", titles.length + "--" + item);
if (1 == titles.length) return mAttrs.otherButtonSingleBackground;
else if (2 == titles.length) switch (item) {
case 0:
return mAttrs.otherButtonTopBackground;
case 1:
return mAttrs.otherButtonBottomBackground;
default:
return null;
}
else if (2 < titles.length) if (0 == item) return mAttrs.otherButtonTopBackground;
else if (item == (titles.length - 1)) return mAttrs.otherButtonBottomBackground;
else return mAttrs.getOtherButtonMiddleBackground();
return null;
}
/**
* 加载视图-透明度动画
*/
private Animation createAlphaInAnimation() {
AlphaAnimation an = new AlphaAnimation(0, 1);
an.setDuration(ALPHA_DURATION);
return an;
}
/**
* 加载视图-位移动画
*/
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 createAlphaOutAnimation() {
AlphaAnimation an = new AlphaAnimation(1, 0);
an.setDuration(ALPHA_DURATION);
an.setFillAfter(true);
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);
an.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation arg0) {
// TODO Auto-generated method stub
}
@Override
public void onAnimationRepeat(Animation arg0) {
// TODO Auto-generated method stub
}
@Override
public void onAnimationEnd(Animation arg0) {
dismiss();
}
});
return an;
}
/**
* 底部按钮样式
*/
private LinearLayout.LayoutParams createButtonLayoutParams() {
return new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
}
/**
* 自定义控件主题
*/
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;
Attributes(Context context) {
this.mContext = context;
background = new ColorDrawable(Color.TRANSPARENT);//背景透明
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);
}
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;
}
//像素转换
private int dp2px(int dp) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, mContext.getResources()
.getDisplayMetrics());
}
}
//回调接口
public interface MenuItemClickListener {
void onItemClick(int position);
}
}
最后一步,附上调用代码就完成了。
ActionSheet actionSheet = new ActionSheet(this).setCancelableOnTouchMenuOutside(false).setCancelButtonTitle("退出");
for (int i = 0; i < 3; i++) {
actionSheet.addItem("第" + (i + 1) + "条", new ActionSheet.MenuItemClickListener() {
@Override
public void onItemClick(int position) {
Toast.makeText(MainActivity.this, "第" + (position + 1) + "条", Toast.LENGTH_SHORT).show();
}
});
}
actionSheet.showMenu();