• UI--仿IOS控件之ActionSheet样式 and more..


    ## 《代码里的世界》UI篇

    用文字札记描绘自己 android学习之路

    转载请保留出处 by Qiao
    http://blog.csdn.net/qiaoidea/article/details/46417747

    【导航】


    1.概述

      在讲述了弹出式对话框和对其 源代码分析之后,我们尝试来模仿一下ios中常见的弹出式按钮选项——ActionSheet。事实上样式也比較简单。从底部弹出几个按钮,提供选项菜单,同一时候出现半透明背景蒙版。

    详细详情及效果參考IOS设备。这里展示下我们最后实现的各种样式及效果图:
      
      仿IOS ActionSheet效果图
      


    2.分析研究

      动手实现之前,先简单说两句。实现这个样式,详细要怎么做,能做成什么样,究竟该怎么用,究竟好不好用?额。不知道,似乎都看你的编写技巧和封装了。

    鄙人愚笨。且写且摸索。
      当然先从熟悉的自己定义View入手。使用一个线性布局LinearLayout嵌套N个Button实现。

    黑色半透明背景採用单独一个View,便于包装使用。做出最主要的效果之后。封装对外接口,用String[]数组来存取每一个Button item的文本。并定义一个itemListener,设置监听item的点击事件。
      OK。完毕这个并不难,假设我们想更进一层做好扩展,最好还是尝试使用DialogFragment再做一遍,另外,前面还有提到AlertDialog中使用的Builder模式,我们也来做一下,看看效果怎样。

    那么,为什么不更灵活一点儿,使用我们自己定义的样式。能够切换ActionSheet风格。比方IOS6和IOS7?


      详细怎么做。来理下思路。首先继承自Fragment,在OnCreateView中实现自己定义View,当然,在自己定义View中使用我们的自己定义属性,控制风格样式,另外呢。定义一个静态Builder类。负责设置数据与交互逻辑,最后通过Argument绑定到Fragment中去,实现终于效果。


    3.详细实现

    3.1 自己定义View实现

      第一想法是用LinerLayout包指定个数的button,然后点击从底部弹出。

    然后设置背景变暗。后来发现其有用到黑色透明背景的地方貌似非常多。弹窗。弹菜单,消息提示。差点儿都是。
      因此这里先定义一个通用的MaskView,作用就是在窗体最前端弹出一个黑色半透明的遮罩蒙层。

    1. MaskView黑色半透明蒙版

      MaskView全局变量

    public class MaskView extends RelativeLayout {
        protected ViewGroup targetView; //将要加入到的目标view
        protected boolean isShowing; //是否显示
        protected long durationMillis; //显示动画持续时长
        protected boolean canCancel; //能否点击外部隐藏 touchCancelOutside
        protected MaskListener maskListener; //点击事件
    
        //...
    
        public interface MaskListener { //点击事件监听接口
            void onShow();//显示
            void onHide();//隐藏
        }
     }

      构造方法和初始化。设置MaskView背景黑色,75%透明度,加入到目标view,并绑定点击事件。

    public MaskView(Context context, ViewGroup targetView) {
            super(context);
            this.targetView = targetView;
            initialize();
        }
    
        protected void initialize() {
            setBackgroundColor(0x88000000);
            setVisibility(View.GONE);
            LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
            targetView.addView(this, lp);
            /**
            * 设置点击事件,假设能够点击空白区域隐藏,则点击隐藏
            */
            setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    if (canCancel) { 
                        hide();
                    }
                }
            });
        }

      显示show()和隐藏hide()。

    使用透明动画AlphaAnimation。当动画结束运行相应监听事件。

     public void show() {
            if (isShowing)
                return;
            isShowing = true;
            clearAnimation();
            setVisibility(View.VISIBLE);
            AlphaAnimation an = new AlphaAnimation(0, 1);
            an.setDuration(durationMillis);
            startAnimation(an);
            if (maskListener != null)
                maskListener.onShow();
        }
    
        public void hide() {
            if (!isShowing)
                return;
            isShowing = false;
            clearAnimation();
            AlphaAnimation an = new AlphaAnimation(1, 0);
            an.setDuration(durationMillis);
            an.setAnimationListener(new AnimationListener() {
                @Override
                public void onAnimationStart(Animation animation) {
                }
    
                @Override
                public void onAnimationRepeat(Animation animation) {
                }
    
                @Override
                public void onAnimationEnd(Animation animation) {
                    setVisibility(View.GONE);
                }
            });
            startAnimation(an);
            if (maskListener != null)
                maskListener.onHide();
        }

    2. ActionSheet底部弹出菜单

      那么这里就打算做成一个RelativeLayout。包裹一个MaskView和一个LinearLayout,这个linearLayout就是前面说的菜单按钮集合了。


      打算做成什么样才干使我们调用方便?我期望是

    final ActionSheet actionSheet = new ActionSheet(MainActivity.this);
            actionSheet.show("确定要退出么?",new String[]{"退出" },new Action1<Integer>(){
                @Override
                public void invoke(Integer index) {
                    actionSheet.hide();
                    if(index==0){
                        MainActivity.this.finish();
                    }
                }
            });

      构造出来一个ActionSheet对象,然后显示的时候,显示

    show(String title , String[] displayStrings, Action1 callback)

    title 标题,
    displayStrings 即各行item,
    callback 回调,它传回的參数int表示第几个item被选中

      详细实现:
      ActionSheet 全局变量:

    public class ActionSheet extends RelativeLayout {
        protected final static long durationMillis = 200;
        protected WindowManager windowManager;
        protected GestureDetector gestureDetector; //手势识别
        protected MaskView maskView; 
        protected LinearLayout actionSheetView;  //实际展示线性布局
        protected Button cancelButton; //取消按钮
        //...
    }

      构造方法都运行initalize()实现初始化。首先绑定MaskView,加入显示/消失事件监听。

    接着初始化线性布局actionSheetView,默认不可见。位于视图底部并设置间距。其次,加入手势监听和按键监听,单点屏幕消失,按返回按钮消失。

        protected void initialize() {
            //初始化MaskView
            maskView = new MaskView(getContext(), this);
            maskView.setCanCancel(true);
            maskView.setDurationMillis(durationMillis);
            maskView.setOnMaskListener(new MaskListener() {
                @Override
                public void onShow() {
                }
    
                @Override
                public void onHide() {
                    hide();
                }
            });
    
            //初始化线性布局容器actionSheetView
            actionSheetView = new LinearLayout(getContext());
            actionSheetView.setOrientation(LinearLayout.VERTICAL);
            actionSheetView.setVisibility(View.GONE);
            RelativeLayout.LayoutParams rlp = new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
            rlp.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM, RelativeLayout.TRUE); //位于父View底部
            rlp.leftMargin = rlp.rightMargin = (int)applyDimension(getContext(), TypedValue.COMPLEX_UNIT_DIP, 8); //左右间距8dip
            addView(actionSheetView, rlp); //加入布局
    
            //初始化windowManager
            windowManager = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
            //初始化手势识别事件,单点消失
            gestureDetector = new GestureDetector(getContext(), new SimpleOnGestureListener() {
                @Override
                public boolean onSingleTapUp(MotionEvent e) {
                    hide();
                    return super.onSingleTapUp(e);
                }
            });
            //初始化按键监听。按下返回则消失
            setOnKeyListener(new OnKeyListener() {
                @Override
                public boolean onKey(View v, int keyCode, KeyEvent event) {
                    if (KeyEvent.KEYCODE_BACK == keyCode && KeyEvent.ACTION_DOWN == event.getAction()) {
                        hide();
                        return true;
                    }
                    return false;
                }
            });
            setFocusable(true);
            setFocusableInTouchMode(true);
        }

      包装接口。

    提供了各种缺省方法。达到不同的显示效果。最后运行的都是

    show(String title,String[] displayStrings, boolean[] isShow,final Action1 callback, boolean hasCancelButton)

      先看这些缺省方法:

        //仅仅显示各行item以及点击回调
         public void show(String[] displayStrings, Action1<Integer> callback) {
            show(null,displayStrings, callback, true);
        }
    
        //显示各行item和点击回调,添加ishow[]的Boolean控制某行是否显示
        public void show(String[] displayStrings,boolean[] isShow,Action1<Integer> callback){
             show(null,displayStrings, isShow,callback, true);
        }
    
        //显示标题头,各行item以及回调
        public void show(String title,String[] displayStrings, Action1<Integer> callback) {
            show(title,displayStrings, callback, true);
        }
    
        //显示标题头,各行item以及回调,添加ishow[]的Boolean控制某行是否显示
        public void show(String title,String[] displayStrings,boolean[] isShow,Action1<Integer> callback){
             show(title,displayStrings, isShow,callback, true);
        }

      来看终于运行的show方法:

    public void show(String title,String[] displayStrings, boolean[] isShow,final Action1<Integer> callback, boolean hasCancelButton) {
            /**
            * 假设当前view是首次被加入到窗体。则构造WindowManager并加入到根视图
            * 初始化WindowManager.LayoutParams,设置背景透明
            * 左上角对齐,填充父容器
            */
            if (getParent() == null) {
                WindowManager.LayoutParams wlp = new WindowManager.LayoutParams();
                wlp.type = WindowManager.LayoutParams.TYPE_APPLICATION;
                wlp.format = PixelFormat.TRANSPARENT; //透明背景
                wlp.gravity = Gravity.LEFT | Gravity.TOP; //左上角
                wlp.width = LayoutParams.MATCH_PARENT;
                wlp.height = LayoutParams.MATCH_PARENT;
                windowManager.addView(this, wlp); //加入填充布局
            }
    
            maskView.show(); //显示透明蒙版
    
            int mrg = (int) applyDimension(getContext(), TypedValue.COMPLEX_UNIT_DIP, 10);
            LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
            lp.setMargins(0, mrg, 0, mrg);
    
            actionSheetView.setVisibility(View.VISIBLE); //使actionView显示可见
            actionSheetView.removeAllViews(); //清空view
    
            /**
            * 假设title不为空而且不为空格,则添加一个标题头
            */
            if(null!=title&&!title.trim().equals("")){
                titleTextView = new TextView(getContext());
                titleTextView.setBackgroundColor(Color.TRANSPARENT);
                titleTextView.setGravity(Gravity.CENTER);
                titleTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 12);
                titleTextView.setText(title);
                titleTextView.setTextColor(Color.WHITE);
                actionSheetView.addView(titleTextView,lp);
            }
    
            /**
            * 逐行添加displayStrings中的选项到item,
            * 为每一行加入点击监听,调用回调
            * 依据isShow[]来控制每行显示
            */
            for (int i = 0, len = displayStrings.length; i < len; i++) {
                final int index = i;
                Button button = new Button(getContext());
                button.setBackgroundResource(R.drawable.actionsheet_red_btn);
                button.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18);
                button.setText(displayStrings[index]);
                button.setTextColor(Color.WHITE);
                button.setOnClickListener(new OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        callback.invoke(index);//点击调用回调
                    }
                });
                if(isShow[i]) { //控制显示
                    button.setVisibility(View.GONE);
                }
                actionSheetView.addView(button, lp);
            }
    
            /**
            * 依据是否有取消按钮来加入点击取消
            */
            if (hasCancelButton) {
                Button button = new Button(getContext());
                button.setBackgroundResource(R.drawable.actionsheet_black_btn);
                button.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18);
                button.setTextColor(Color.WHITE);
                button.setText("取消");
                button.setOnClickListener(new OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        hide();
                    }
                });
                actionSheetView.addView(button, lp);
            }
    
            //清除动画,加入从底部 显示动画效果
            actionSheetView.clearAnimation();
            TranslateAnimation an = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 1, Animation.RELATIVE_TO_SELF, 0);
            an.setDuration(durationMillis);
            actionSheetView.startAnimation(an);
        }

      隐藏菜单hide()方法:

     public void hide() {
            maskView.hide(); //隐藏MaskView
            actionSheetView.clearAnimation();
            TranslateAnimation an = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 1);
            an.setDuration(durationMillis);
            an.setAnimationListener(new AnimationListener() {
                @Override
                public void onAnimationStart(Animation animation) {
                }
    
                @Override
                public void onAnimationRepeat(Animation animation) {
                }
    
                @Override
                public void onAnimationEnd(Animation animation) {
                    actionSheetView.setVisibility(View.GONE);
                    if (getParent() != null)
                        windowManager.removeView(ActionSheet.this); //动画结束。从窗体移除当前View
                }
            });
            actionSheetView.startAnimation(an);
        }

      最后看看用到的几个监听事件接口和像素转换方法:

        //像素转换
        public static float applyDimension(Context context, int unit, float size) {
            Resources r = context.getResources();
            return TypedValue.applyDimension(unit, size, r.getDisplayMetrics());
        }
    
        //点击监听事件
        public interface Action1<T1> {
            void invoke(T1 p);
        }

      至此我们的自己定义View实现ActionSheet也OK了,基本能胜任日常使用了。
      
      当然,作为一个有逼格的猿,要尝试用更方便的方法,并符合fragmentDialog的要求来做出相同的效果。

    3.2 Fragment实现

      使用Fragment的优点这里不再赘述,仅仅简述下要会而且能够秀的关键点:

    • styleable 自己定义主题样式。可选IOS6/IOS7风格以及相应的详细配置
    • Argument数据交互 通过Argument实现fragment交互数据。减少数据耦合度
    • Builder模式 利用Builder构造Fragment,达到简约使用和高速构建
        明白上述三点后。我们来理一下实现思路。

      首先定义xml样式和相应读取Attributes,接着利用在Fragment中初始化和创建界面及动画,提供展示数据和关闭(消除销毁)的接口,然后使用builder构建高速模板。最后呢。就是验收測试和移植到应用了。

    1. declare-styleable和 使用Attributes读取

     (1) 定义样式

      先定义Actionsheet详细各个用于界面展示的可配置项,再定义两种相应配置项的两种不同实现样式,最后定义一个可选择项。用来选择使用哪种样式(即内置两种样式之中的一个)。
    1.定义ActionSheet本身须要配置样式的可选项。(即在界面中同意外部配置的的地方)

     <declare-styleable name="ActionSheet">
            <attr name="actionSheetBackground" format="color|reference" />
            <attr name="cancelButtonBackground" format="color|reference" />
            <attr name="topItemBackground" format="color|reference" />
            <attr name="middleItemBackground" format="color|reference" />
            <attr name="bottomItemBackground" format="color|reference" />
            <attr name="singleItemBackground" format="color|reference" />
            <attr name="cancelButtonTextColor" format="color|reference" />
            <attr name="titleColor" format="color|reference" />
            <attr name="itemTextColor" format="color|reference" />
            <attr name="actionSheetPadding" format="dimension|reference" />
            <attr name="itemSpacing" format="dimension|reference" />
            <attr name="cancelButtonMarginTop" format="dimension|reference" />
            <attr name="actionSheetTextSize" format="dimension|reference" />
        </declare-styleable>

    2.基于ActionSheet配置项来定义两种样式IOS6/IOS7

    
        <style name="ActionSheetStyleIOS6">
            <item name="actionSheetBackground">#80000000</item>
            <item name="cancelButtonBackground">@drawable/as_cancel_bt_bg</item>
            <item name="topItemBackground">@drawable/as_other_bt_bg</item>
            <item name="middleItemBackground">@drawable/as_other_bt_bg</item>
            <item name="bottomItemBackground">@drawable/as_other_bt_bg</item>
            <item name="singleItemBackground">@drawable/as_other_bt_bg</item>
            <item name="cancelButtonTextColor">@android:color/white</item>
            <item name="itemTextColor">@android:color/black</item>
            <item name="titleColor">@android:color/white</item>
            <item name="actionSheetPadding">20dp</item>
            <item name="itemSpacing">5dp</item>
            <item name="cancelButtonMarginTop">20dp</item>
            <item name="actionSheetTextSize">16sp</item>
        </style>
    
        <style name="ActionSheetStyleIOS7">
            <item name="actionSheetBackground">@android:color/transparent</item>
            <item name="cancelButtonBackground">@drawable/slt_as_ios7_cancel_bt</item>
            <item name="topItemBackground">@drawable/slt_as_ios7_other_bt_top</item>
            <item name="middleItemBackground">@drawable/slt_as_ios7_other_bt_middle</item>
            <item name="bottomItemBackground">@drawable/slt_as_ios7_other_bt_bottom</item>
            <item name="singleItemBackground">@drawable/slt_as_ios7_other_bt_single</item>
            <item name="cancelButtonTextColor">#1E82FF</item>
            <item name="itemTextColor">#1E82FF</item>
            <item name="titleColor">@android:color/white</item>
            <item name="actionSheetPadding">10dp</item>
            <item name="itemSpacing">0dp</item>
            <item name="cancelButtonMarginTop">10dp</item>
            <item name="actionSheetTextSize">16sp</item>
        </style>

    3.在styleable中定义一个选择样式的选项Actionsheets。其类型为reference,可选择的样式(可选ios6/ios7)将实现ActionSheet的详细配置项;

        <declare-styleable name="ActionSheets">
            <attr name="actionSheetStyle" format="reference" />
        </declare-styleable>
     (2) 样式读取

      首先相同定义一个java类,保存各种配置项和相应的值,然后我们现依据配置的样式选项来确定详细使用的样式方案,最后读取该方案下的详细配置并赋值。
      1.定义详细属性样式类

        /**
         * actionsheet各种属性样式
         */
        private static class Attributes {
            Context mContext;
            Drawable background;
            Drawable cancelButtonBackground;
            Drawable topItemBackground;
            Drawable middleItemBackground;
            Drawable bottomItemBackground;
            Drawable singleItemBackground;
            int cancelButtonTextColor;
            int itemTextColor;
            int titleColor;
            int padding;
            int itemSpacing;
            int margin;
            int cancelButtonMarginTop;
            float textSize;
    
            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.topItemBackground = gray;
                this.middleItemBackground = gray;
                this.bottomItemBackground = gray;
                this.singleItemBackground = gray;
                this.cancelButtonTextColor = Color.WHITE;
                this.titleColor = Color.WHITE;
                this.itemTextColor = Color.BLACK;
                this.padding = dp2px(20);
                this.itemSpacing = dp2px(2);
                this.margin = dp2px(5);
                this.cancelButtonMarginTop = dp2px(10);
                this.textSize = dp2px(16);
            }
    
            /**
             * dp转像素类
             * @param dp
             * @return
             */
            private int dp2px(int dp){
                return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
                        dp, mContext.getResources().getDisplayMetrics());
            }
    
            /**
             * 获取items按键状态相应背景
             * @return
             */
            public Drawable getMiddleItemBackground() {
                if (middleItemBackground instanceof StateListDrawable) {
                    TypedArray a = mContext.getTheme().obtainStyledAttributes(null,
                            R.styleable.ActionSheet, R.attr.actionSheetStyle, 0);
                    middleItemBackground = a
                            .getDrawable(R.styleable.ActionSheet_middleItemBackground);
                    a.recycle();
                }
                return middleItemBackground;
            }
        }

      2.读取样式并赋值

    protected Attributes initAttrs(){
            Attributes attrs = new Attributes(getActivity());
            TypedArray a = getActivity().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 itemTopBackground = a
                    .getDrawable(R.styleable.ActionSheet_topItemBackground);
            if (itemTopBackground != null) {
                attrs.topItemBackground = itemTopBackground;
            }
            Drawable itemMiddleBackground = a
                    .getDrawable(R.styleable.ActionSheet_middleItemBackground);
            if (itemMiddleBackground != null) {
                attrs.middleItemBackground = itemMiddleBackground;
            }
            Drawable itemBottomBackground = a
                    .getDrawable(R.styleable.ActionSheet_bottomItemBackground);
            if (itemBottomBackground != null) {
                attrs.bottomItemBackground = itemBottomBackground;
            }
            Drawable itemSingleBackground = a
                    .getDrawable(R.styleable.ActionSheet_singleItemBackground);
            if (itemSingleBackground != null) {
                attrs.singleItemBackground = itemSingleBackground;
            }
            attrs.titleColor = a.getColor(
                    R.styleable.ActionSheet_titleColor,
                    attrs.titleColor);
            attrs.cancelButtonTextColor = a.getColor(
                    R.styleable.ActionSheet_cancelButtonTextColor,
                    attrs.cancelButtonTextColor);
            attrs.itemTextColor = a.getColor(
                    R.styleable.ActionSheet_itemTextColor,
                    attrs.itemTextColor);
            attrs.padding = (int) a.getDimension(
                    R.styleable.ActionSheet_actionSheetPadding, attrs.padding);
            attrs.itemSpacing = (int) a.getDimension(
                    R.styleable.ActionSheet_itemSpacing,
                    attrs.itemSpacing);
            attrs.cancelButtonMarginTop = (int) a.getDimension(
                    R.styleable.ActionSheet_cancelButtonMarginTop,
                    attrs.cancelButtonMarginTop);
            attrs.textSize = a.getDimensionPixelSize(R.styleable.ActionSheet_actionSheetTextSize, (int) attrs.textSize);
    
            a.recycle();
            return attrs;
        }

    2. 实现Fragment界面和数据绑定

      Fragment全局变量和參数:

    public class ActionSheet extends Fragment implements OnClickListener{
        protected WindowManager windowManager;
        protected View mBackgroundView;
        protected ViewGroup decorView;
        protected View parent; 
        protected LinearLayout container; //各个item选项容器
        protected Attributes mAttrs; //读取的样式參数配置
    
        private ItemClikListener mItemClikListener; //item点击监听事件
        private CancelListener mCancelListener; //取消按键监听事件
    
        protected boolean isDismissed = true;
        protected boolean isCancel = true;
    
        /**
        * 几个item的Id
        */
        public static final int BG_VIEW_ID = 100;
        public static final int CANCEL_BUTTON_ID = 101;
        public static final int ITEM_ID = 102;
    
        //两种样式相应key。便于外部设置
        public static final int ActionSheetThemeIOS6 = R.style.ActionSheetStyleIOS6;
        public static final int ActionSheetThemeIOS7 = R.style.ActionSheetStyleIOS7;
    
        protected final static long durationMillis = 200; //显示消失动画时长
    
        //。

    。。 }

      Fragment构造和初始化做了三件事:

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                 Bundle savedInstanceState) {
            initalize();
            startSlideInAnim();
            return super.onCreateView(inflater, container, savedInstanceState);
        }
    
        protected void initalize() {
            tryHideSoftInput(); //尝试隐藏输入法
            mAttrs = initAttrs(); //读取样式
            initViews(); //初始化绑定view
        }
    
    
        /**
         * 假设输入法键盘没有隐藏,则隐藏软键盘
         */
        protected void tryHideSoftInput(){
            InputMethodManager imm = (InputMethodManager) getActivity()
                    .getSystemService(Context.INPUT_METHOD_SERVICE);
            if (imm.isActive()) {
                View focusView = getActivity().getCurrentFocus();
                if (focusView != null) {
                    imm.hideSoftInputFromWindow(focusView.getWindowToken(), 0);
                }
            }
        }
    
        //读取样式前边已贴。。。

    /** * 初始化背景view 和 底部items */ protected void initViews(){ windowManager = (WindowManager) getActivity().getSystemService(Context.WINDOW_SERVICE); FrameLayout parent = new FrameLayout(getActivity()); /** *初始化背景view */ mBackgroundView = new View(getActivity()); mBackgroundView.setBackgroundColor(0x88000000); mBackgroundView.setId(BG_VIEW_ID); mBackgroundView.setOnClickListener(this); FrameLayout.LayoutParams bgLayoutParams = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT); parent.addView(mBackgroundView); /** *初始化包括item选项的view */ container = new LinearLayout(getActivity()); FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.WRAP_CONTENT); params.gravity = Gravity.BOTTOM; container.setLayoutParams(params); container.setOrientation(LinearLayout.VERTICAL); createItems(); parent.addView(container); this.parent = parent; //获取跟试图并加入view decorView = (ViewGroup) getActivity().getWindow().getDecorView(); decorView.addView(parent); }

      创建选项并绑定数据和事件

        /**
         * 初始化底部items
         */
        protected void createItems(){ 
            //创建title
            String mTitle = getArguments().getString(Builder.ARG_TITLE);
            if(mTitle !=null){
                TextView title = new TextView(getActivity());
                title.getPaint().setFakeBoldText(true);
                title.setTextSize(TypedValue.COMPLEX_UNIT_PX, mAttrs.textSize);
                title.setText(mTitle);
                title.setTextColor(mAttrs.titleColor);
                title.setGravity(Gravity.CENTER);
                LinearLayout.LayoutParams params = createItemLayoutParams();
                int margin = mAttrs.itemSpacing > 0 ?

    mAttrs.itemSpacing:mAttrs.margin; params.setMargins(margin, 0, margin, margin); container.addView(title, params); } //创建items String[] titles = getArguments().getStringArray(Builder.ARG_ITEM_TITLES); if (titles != null) { for (int i = 0; i < titles.length; i++) { Button bt = new Button(getActivity()); bt.setId(ITEM_ID + i); bt.setOnClickListener(this); bt.setBackgroundDrawable(getItemBg(titles, i)); bt.setText(titles[i]); bt.setTextColor(mAttrs.itemTextColor); bt.setTextSize(TypedValue.COMPLEX_UNIT_PX, mAttrs.textSize); if (i > 0) { LinearLayout.LayoutParams params = createItemLayoutParams(); params.topMargin = mAttrs.itemSpacing; container.addView(bt, params); } else { container.addView(bt); } } } //创建cancelbutton String cancelText = getArguments().getString(Builder.ARG_CANCEL_TEXT); if(cancelText != null){ Button bt = new Button(getActivity()); bt.getPaint().setFakeBoldText(true); bt.setTextSize(TypedValue.COMPLEX_UNIT_PX, mAttrs.textSize); bt.setId(CANCEL_BUTTON_ID); bt.setBackgroundDrawable(mAttrs.cancelButtonBackground); bt.setText(cancelText); bt.setTextColor(mAttrs.cancelButtonTextColor); bt.setOnClickListener(this); LinearLayout.LayoutParams params = createItemLayoutParams(); params.topMargin = mAttrs.cancelButtonMarginTop; container.addView(bt, params); } container.setBackgroundDrawable(mAttrs.background); container.setPadding(mAttrs.padding, mAttrs.padding, mAttrs.padding, mAttrs.padding); }

      Fragment展示和消失方法接口

        public void show(FragmentManager manager, String tag) {
            if (!isDismissed) {
                return;
            }
            isDismissed = false;
            FragmentTransaction ft = manager.beginTransaction();
            ft.add(this, tag);
            ft.addToBackStack(null); //同意回退栈
            ft.commit();
        }
    
        public void dismiss(){
            if (isDismissed) {
                return;
            }
            isDismissed = true;
            getFragmentManager().popBackStack();
            FragmentTransaction ft = getFragmentManager().beginTransaction();
            ft.remove(this);
            ft.commit();
        }

      显示消失相应的动画:

    private void startSlideInAnim() {
            container.clearAnimation();
            mBackgroundView.clearAnimation();
    
            TranslateAnimation translateAnimation = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 1, Animation.RELATIVE_TO_SELF, 0);
            translateAnimation.setDuration(durationMillis);
            AlphaAnimation alphaAnimation = new AlphaAnimation(0, 1);
            alphaAnimation.setDuration(durationMillis);
    
    
            container.startAnimation(translateAnimation);
            mBackgroundView.startAnimation(alphaAnimation);
        }
    
        private void startSlideOutAnim() {
            container.clearAnimation();
            TranslateAnimation translateAnimation = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 1);
            translateAnimation.setDuration(durationMillis);
    
            mBackgroundView.clearAnimation();
            AlphaAnimation alphaAnimation = new AlphaAnimation(1, 0);
            alphaAnimation.setDuration(durationMillis);
            container.startAnimation(translateAnimation);
            mBackgroundView.startAnimation(alphaAnimation);
        }

      点击事件的处理:

        @Override
        public void onClick(View v) {
            if (v.getId() == BG_VIEW_ID && !getArguments().getBoolean(Builder.ARG_CANCELABLE_ONTOUCHOUTSIDE)) {
                return;
            }
            if (v.getId() != CANCEL_BUTTON_ID && v.getId() != BG_VIEW_ID) {
                if (mItemClikListener != null) {
                    mItemClikListener.onitemClick(this, v.getId() - CANCEL_BUTTON_ID
                            - 1);
                }
                isCancel = false;
            }
            dismiss();
        }

      Fragment销毁时处理逻辑:

        @Override
        public void onDestroyView() {
            startSlideOutAnim();
            container.postDelayed(new Runnable() {
                @Override
                public void run() {
                    decorView.removeView(parent);
                }
            }, durationMillis);
            if (mCancelListener != null) {
                mCancelListener.onCancelClick(this);
            }
            super.onDestroyView();
        }
    

      Fragment事件接口定义:
    (注: show()差别于display()方法,show仅仅是负责展示。display负责显示/消除的切换)

        protected void setActionSheetListener(ItemClikListener itemClikListener,CancelListener cancelListener){
            this.mItemClikListener = itemClikListener;
            this.mCancelListener = cancelListener;
        }
    
        public static interface ItemClikListener{
            void onitemClick(ActionSheet actionSheet, int index);
        }
    
        public static interface CancelListener{
            void onCancelClick(ActionSheet actionSheet);
        }

    3. Builder模式封装和高速构建

      通过Builder静态高速构建Fragment对象

        public static Builder createBuilder(Context context,
                                            FragmentManager fragmentManager) {
            return init(context, fragmentManager);
        }

      Builder类以及对外封装接口:

    
        public static class Builder {
            private String mTag = "ActionSheet";
            private static final String ARG_TITLE = "title";
            private static final String ARG_CANCEL_TEXT = "cancel_text";
            private static final String ARG_ITEM_TITLES = "items_text";
            private static final String ARG_CANCELABLE_ONTOUCHOUTSIDE = "cancelable_ontouchoutside";
    
            private FragmentManager mFragmentManager;
            private Context mContext;
            private String mTitle;
            private String mCancelButtonText;
            private String[] mItemsText;
            private boolean mCancelableOnTouchOutside = true;
            private ItemClikListener itemClikListener;
            private CancelListener cancelListener;
            private boolean isThemed = false;
    
            public Builder(Context context, FragmentManager fragmentManager) {
                mContext = context;
                mFragmentManager = fragmentManager;
            }
    
            public Builder setTitle(String title) {
                mTitle = title;
                return this;
            }
    
            public Builder setCancelText(String title) {
                mCancelButtonText = title;
                return this;
            }
    
            public Builder setCancelText(String title,CancelListener cancelListener) {
                mCancelButtonText = title;
                this.cancelListener = cancelListener;
                return this;
            }
    
            public Builder setItemTexts(String... titles) {
                mItemsText = titles;
                return this;
            }
    
            public Builder setTheme(int resid){
                isThemed = true;
                mContext.setTheme(resid);
                return this;
            }
    
            public Builder setTag(String tag) {
                mTag = tag;
                return this;
            }
    
            public Builder setItemClickListener(ItemClikListener listener) {
                this.itemClikListener = listener;
                return this;
            }
    
            public Builder setCancelableOnTouchOutside(boolean cancelable) {
                mCancelableOnTouchOutside = cancelable;
                return this;
            }
    
            public Bundle initArguments() {
                Bundle bundle = new Bundle();
                bundle.putString(ARG_TITLE, mTitle);
                bundle.putString(ARG_CANCEL_TEXT, mCancelButtonText);
                bundle.putStringArray(ARG_ITEM_TITLES, mItemsText);
                bundle.putBoolean(ARG_CANCELABLE_ONTOUCHOUTSIDE,
                        mCancelableOnTouchOutside);
                return bundle;
            }
    
            public static ActionSheet actionSheet;
            public ActionSheet show() {
                if(!isThemed)
                    setTheme(R.style.ActionSheetStyleIOS7);
                if(actionSheet == null) {
                    actionSheet = (ActionSheet) Fragment.instantiate(
                            mContext, ActionSheet.class.getName(), initArguments());
                    actionSheet.setActionSheetListener(itemClikListener, cancelListener);
                }
                actionSheet.show(mFragmentManager, mTag);
                return actionSheet;
            }
    
            public ActionSheet display(){
                if(actionSheet == null){
                    if(!isThemed)
                        setTheme(R.style.ActionSheetStyleIOS7);
                    actionSheet = (ActionSheet) Fragment.instantiate(
                            mContext, ActionSheet.class.getName(), initArguments());
                    actionSheet.setActionSheetListener(itemClikListener, cancelListener);
                }
                if(actionSheet.isDismissed){
                    actionSheet.show(mFragmentManager, mTag);
                }else{
                    actionSheet.dismiss();
                }
                return actionSheet;
            }
        }

      以上便是Fragment实现ActionSheet的方法,我们使用的时候,也是各种灵活:(不调用方法即不配置该项)

    ActionSheet.init(this)
                    .setTitle("This is test title ,do you want do something?")
                    .setTheme(resid)
                    .setItemTexts("Item1", "Item2", "Item3", "Item4")
                    .setItemClickListener(new ItemClikListener() {
                        @Override
                        public void onitemClick(
                                baoyz.qiao.actionsheet.ActionSheet actionSheet,
                                int index) {
                            Toast.makeText(getApplicationContext(), "click item index = " + index,0).show();
                        }
                    })
                    .setCancelText("Cancel")
    //              .setCancelText("Cancel",new CancelListener() {
    //                  @Override
    //                  public void onCancelClick(ActionSheet actionSheet) {
    //                      Toast.makeText(getApplicationContext(), "dismissed ", 0).show();
    //                  }
    //              })
                    .show();

    当中,
    + setTheme() 參数可选 R.style.ActionSheetStyleIOS6 或 R.style.ActionSheetStyleIOS6 。不设置则默觉得 R.style.ActionSheetStyleIOS7。注:
    + setTitle不设置(不调用该方法) 则为无title样式
    + setCanTouchOutside(false) 点击选项外无响应,默觉得true,点击隐藏
    + setCancelText() 不设置 则不显示取消按钮。

    其參数有两个,第一个string为显示内容,第二个listener为相应事件
    + show() 最后。别忘记调用show让其显示。


    3.综述总结

      至此,我们的ActionSheet两种样式即实现已经ok了。

    日常使用也能够通过前面两种封装接口加以调用。方案github挺火。效果不好用我会乱说?!

    当然感谢baoyongzhang大士的贡献及灵感。


      最后额外赠送了一个线性式ActionSheetLayout布局,即演示界面的头部布局。

    事实上就是一个普通的linearLayout使用。只是同意相似ActionSheet的显示隐藏效果。作为补充呢。能够自己私下尝试使用很多其它的展示样式,比方从左側或右側现实和消失。

    有兴趣最好还是做一下。
      关门,放代码:
      演示样例demo源代码下载地址 (资源上传较慢,假设不可用。试试这里


  • 相关阅读:
    ssm复习资料
    嵌入式开发实践的第二种柱状图代码
    嵌入式开发实践的简单登录代码
    嵌入式开发实践的柱状图代码
    学习ps的坑
    js的执行上下文
    js的渲染
    vue 使用Ueditor富文本的配置
    使用iview Upload进行多文件上传,编辑页初始已上传的图片失败的问题
    beforeEach钩子,next('/login') 跳转问题,无线循环导致Maximum call stack size exceeded问题
  • 原文地址:https://www.cnblogs.com/claireyuancy/p/7357619.html
Copyright © 2020-2023  润新知