自定义控件(仿PopupWindow篇)

自定义控件实现方式

  • 自定义控件实现方式
    • 原生PopupWindow实现方式
    • 自定义仿PopupWindow实现方式
      • 代码块
    • 结束语

本文中初步讨论了关于原生PopupWindow实现方式,自定义仿PopupWindow样式实现以及注意细节
基于Android Studio API 23开发
- 原生PopupWindow样式实现方式
- 自定义仿PopupWindow实现方式
- 结束语


原生PopupWindow实现方式

在PopupWindow中的实现思路建树:

前提:在一个布局中心绘制PopupWindow,设置根布局背景为半透明–》实现类似Dialog的样式

1.弹出必须要有一个相应的样式View

  View view = LayoutInflater.from(context).inflate(R.layout.public_pop, null);

2.弹窗必须设置宽高,可选设置出现和隐藏动画

          PopupWindow window = new PopupWindow(view, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, isForce);

 window.setAnimationStyle(R.style.pop_style_fade);

3.弹窗的点击事件处理

//这里必须要注意将内容里的根布局的点击事件设为null,不然就会发生点击内容布局pop也会消失,在测试时必须用真机的返回键测试

//当需求为点击PopupWindow外部消失时
//要让点击PopupWindow之外的地方PopupWindow消失你需要调用
            window.setBackgroundDrawable(new ColorDrawable(0));
            window.setOutsideTouchable(false);
            window.setFocusable(true);
             contentLayout.setOnClickListener(null);
            //实现根布局的点击事件,点击外部消失
            parentLayout.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                   window.dismiss();
                }
            });
//当需求为点击PopupWindow外部不消失时,点击返回退出程序--》应用场景:强制更新
//相当于整个根布局一直获取着焦点不会释放
            window.setFocusable(true);
            parentLayout.setFocusable(true);
            parentLayout.setFocusableInTouchMode(true);
            parentLayout.setOnKeyListener(new View.OnKeyListener() {
                @Override
                public boolean onKey(View v, int keyCode, KeyEvent event) {
                    if (keyCode == KeyEvent.KEYCODE_BACK){
                         window.dismiss();
                        System.exit(0);
                        return true;
                    }

                    return false;
                }
            });

4.显示当前界面

//第一个参数是activity中的根布局
 window.showAtLocation(anchorLayout, Gravity.CENTER, 0, 0);

即可显示一个原生态的PopupWindow,设计效果如图:
自定义控件(仿PopupWindow篇)_第1张图片

自定义仿PopupWindow实现方式

需求前提:使用popupwindow不能简单实现从当前界面的哪个控件下方滑入滑出(需要计算高度并且需要单个在代码中实现动画效果,过程比较繁琐并且容易出错)

思想建树:
1 需要当前界面的View来进行自定义动画的开启和关闭

2 需要实现开启动画和关闭动画的对象–》ObjectAnimator

3 需要实现点击事件(用于内部item的点击后响应关闭pop的操作),并且暴露开启和关闭的方法由外部调用(用于处理一些外部点击事件)

同时需要暴露一个boolean对象用于外部判断当前的状态

实现前提,要求,关键:

前提:在当前的activity中确保这个对象的唯一性

要求:仿照dialog的设计样式使用使用build初始化当前数据–》AlertDialog.Builder builder=new AlertDialog.Builder(this); //先得到构造器

关键:1.
在自定义控件中使用public static class 传递初始化的对象—-》上下文以及当前动画的界面—》return new 当前的对象初始化对象中要实现的对象
2.
当前的界面需要有FrameLayout—》作用:将自定义的View对象填充进当前界面(注意:这里一定要是布局里面的frameLayout,不能是根布局,并且填充的FrameLayout需要设置大小)

代码块

//该类是借鉴GitHub上的自定义控件内容
public class EyepetizerMenuAnimation implements View.OnClickListener {

    private View mEyepetizerMenuView;
    private Context mContext;

    public static  ObjectAnimator mMenuOpenAnimation;
    public static  ObjectAnimator mMenuCloseAnimation;

    private DecelerateInterpolator mInterpolator;

    public static boolean mIsMenuClose = true;
    private static final String TRANSLATION = "translationY";


    public EyepetizerMenuAnimation(EyepetizerMenuBuilder builder) {
        this.mContext = builder.context;
        this.mEyepetizerMenuView = builder.eyepetizerMenuView;
        this.mInterpolator = new DecelerateInterpolator();
        this.mMenuOpenAnimation = buildMenuOpenAnimation();
        this.mMenuCloseAnimation = buildMenuCloseAnimation();
            mEyepetizerMenuView.setVisibility(View.GONE);
    }

    @Override
    public void onClick(View v) {
        if (mIsMenuClose) {
            open();
        }else {
            close();
        }
    }

    public static  void open() {
        mIsMenuClose = false;
        AnimatorSet set = new AnimatorSet();
        set.playTogether( mMenuOpenAnimation);
        set.start();
    }

    public static  void close() {
        mIsMenuClose = true;
        AnimatorSet set = new AnimatorSet();
        set.playTogether( mMenuCloseAnimation);//mActionMenuAnimation
        set.start();

    }

    private ObjectAnimator buildMenuOpenAnimation() {
        ObjectAnimator menuOpenAnimation = ObjectAnimator.ofFloat(
                mEyepetizerMenuView, TRANSLATION, -Utils.getScreenHeight(mContext), 0);
        menuOpenAnimation.setInterpolator(mInterpolator);
        menuOpenAnimation.setDuration(350);
        menuOpenAnimation.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationStart(Animator animation) {
                super.onAnimationStart(animation);
                mEyepetizerMenuView.setVisibility(View.VISIBLE);
            }
        });
        return menuOpenAnimation;
    }

    private ObjectAnimator buildMenuCloseAnimation() {
        ObjectAnimator menuCloseAnimation = ObjectAnimator.ofFloat(
                mEyepetizerMenuView, TRANSLATION, 0, -Utils.getScreenHeight(mContext));
        menuCloseAnimation.setInterpolator(mInterpolator);
        menuCloseAnimation.setDuration(350);
        menuCloseAnimation.addListener(new AnimatorListenerAdapter() {

            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                mEyepetizerMenuView.setVisibility(View.GONE);
            }
        });
        return menuCloseAnimation;
    }

    public static class EyepetizerMenuBuilder {

        private View eyepetizerMenuView;
        private Context context;
        public EyepetizerMenuBuilder(Context context, View eyepetizerMenuView) {
            this.eyepetizerMenuView = eyepetizerMenuView;
            this.context = context;
        }

        public EyepetizerMenuAnimation build() {
            return new EyepetizerMenuAnimation(this);
        }

    }

}

在activity中添加view

//frameLayout不能为根布局,是在布局中另外用于填充的frameLayout
  FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                Utils.getScreenHeight(getActivity()) * 3 / 5);
        root_fl.setLayoutParams(layoutParams);
        mEyepetizerMenuView = LayoutInflater.from(getActivity()).inflate(R.layout.layout_captail_pop_item, null);

        mEyepetizerMenuView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //截断透明背景点击事件
            }
        });
        root_fl.addView(mEyepetizerMenuView);
        new EyepetizerMenuAnimation.EyepetizerMenuBuilder(
                getActivity(), mEyepetizerMenuView)
                .build();

        //添加完成后就可以关闭菜单
          EyepetizerMenuAnimation.close();
        //添加完成后就可以开启菜单
          EyepetizerMenuAnimation.open();

结束语

对于原生PopupWindow满足所有基本要求,但是当滑入滑出的要求出现的时候很多新手都会被计算高度隐藏困惑并且也不能用Dialog实现,因为在界面中是PopupWindow弹出时是部分半透明。结合多方面因素考虑还是需要自定义一个为了适应界面不同,动画相同的情况—》使用联动的方式(即界面由外部传入,内部获取界面进行动画效果(内部只控制界面开启和关闭))。本人习惯使用Dialog代替一般需求的PopupWindow,个人感觉Dialog比较好用,自定义Dialog详见:
http://blog.csdn.net/wyh_healer/article/details/54891235

你可能感兴趣的:(自定义View)