比QQ更炫酷的'选项弹窗'动画

HintPopupwindow
实现:

首先我做出来的自定义view是希望全局只要调一个方法就可以用的, 就像popupwindow那样, 所以我的思路是初始化整个布局, 然后以Toast的方式添加到屏幕最前端.

所以第一步: 初始化整个弹窗布局

 @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button button = (Button) findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //弹出选项弹窗
                hintPopupWindow.showPopupWindow(v);
            }
        });
        ImageView imageView = (ImageView) findViewById(R.id.imageView);
        imageView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //弹出选项弹窗
                hintPopupWindow.showPopupWindow(v);
            }
        });

        //下面的操作是初始化弹出数据
        ArrayList<String> strList = new ArrayList<>();
        strList.add("选项item1选项item1选项item1选项item1");
        strList.add("选项item2");
        strList.add("选项item3");

        ArrayList<View.OnClickListener> clickList = new ArrayList<>();
        View.OnClickListener clickListener = new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(MainActivity.this, "点击事件触发", Toast.LENGTH_SHORT).show();
            }
        };
        clickList.add(clickListener);
        clickList.add(clickListener);
        clickList.add(clickListener);
        clickList.add(clickListener);

        //具体初始化逻辑看下面的图
        hintPopupWindow = new HintPopupWindow(this, strList, clickList);
    }
     /**
     * @param contentList 点击item内容的文字
     * @param clickList 点击item的事件
     */
    public void initLayout(List<String> contentList, List<View.OnClickListener> clickList){

        //这是根布局
        rootView = (ViewGroup) View.inflate(activity, R.layout.item_root_hintpopupwindow, null);
        linearLayout = (ViewGroup) rootView.findViewById(R.id.linearLayout);

        //格式化点击item, 将文字和click事件一一绑定上去
        List<View> list = new ArrayList<>();
        for(int x=0; x<contentList.size(); x++){
            View view = View.inflate(activity, R.layout.item_hint_popupwindow, null);
            TextView textView = (TextView) view.findViewById(R.id.tv_content);
            View v_line = view.findViewById(R.id.v_line);
            textView.setText(contentList.get(x));
            linearLayout.addView(view);
            list.add(view);
            if(x == 0){
                v_line.setVisibility(View.INVISIBLE);
            }else{
                v_line.setVisibility(View.VISIBLE);
            }
        }
        for (int x=0; x<list.size(); x++){
            list.get(x).setOnClickListener(clickList.get(x));
        }

        //这里给你根布局设置背景透明, 为的是让他看起来和activity的布局一样
        params = new WindowManager.LayoutParams();
        params.width = WindowManager.LayoutParams.MATCH_PARENT;
        params.height = WindowManager.LayoutParams.MATCH_PARENT;
        params.format = PixelFormat.RGBA_8888;//背景透明
        params.gravity = Gravity.LEFT | Gravity.TOP;

        //当点击根布局时, 隐藏
        rootView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                gonePopupWindow();
            }
        });

        rootView.setOnKeyListener(new View.OnKeyListener() {
            @Override
            public boolean onKey(View v, int keyCode, KeyEvent event) {
                //如果是显示状态那么隐藏视图
                if(keyCode == KeyEvent.KEYCODE_BACK && isShow) gonePopupWindow();
                return isShow;
            }
        });
    }

第二步: 这里比较重要了, 当点击button, 弹出选项框的时候, 具体做哪些事情

/**
     * 弹出选项弹窗
     * @param locationView 默认在该view的下方弹出, 和popupWindow类似
     */
    public void showPopupWindow(View locationView){
        try {
            //这个步骤是得到该view相对于屏幕的坐标, 注意不是相对于父布局哦!
            int[] arr = new int[2];
            locationView.getLocationOnScreen(arr);
            linearLayout.measure(0, 0);
            Rect frame = new Rect();
            activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(frame);//得到状态栏高度
            float x = arr[0] + locationView.getWidth() - linearLayout.getMeasuredWidth();
            float y = arr[1] - frame.top + locationView.getHeight();
            linearLayout.setX(x);
            linearLayout.setY(y);

            /*捕获当前activity的布局视图, 因为我们要动态模糊, 所以这个布局一定要是最新的,
            *这样我们把模糊后的布局盖到屏幕上时, 才能让用户感觉不出来变化*/
            View decorView = activity.getWindow().getDecorView().findViewById(android.R.id.content);
            Bitmap bitmap = getBitmapByView(decorView);//这里是将view转成bitmap
            setBlurBackground(bitmap);//这里是模糊图片, 这个是重点我会单独讲的, 因为效率很重要啊!!!

            //这里就是使用WindowManager直接将我们处理好的view添加到屏幕最前端
            windowManager = (WindowManager) activity.getSystemService(Context.WINDOW_SERVICE);
            windowManager.addView(rootView, params);

            //这一步就是有回弹效果的弹出动画, 我用属性动画写的, 很简单
            showAnim(linearLayout, 0, 1, animDuration, true);

            //视图被弹出来时得到焦点, 否则就捕获不到Touch事件
            rootView.setFocusable(true);
            rootView.setFocusableInTouchMode(true);
            rootView.requestFocus();
            rootView.requestFocusFromTouch();

        }catch (Exception e){
            e.printStackTrace();
        }
    }

到这里我们所有操作就都完成了 大家是不是感觉很简单, 嘿嘿嘿, 最难的坑其实是模糊图片那里, 因为我们是当用户点击弹出按钮的时候动态模糊的, 所以效率就很重要, 下面是我对activity视图bitmap的处理:

 private void setBlurBackground(Bitmap bitmap) {

        //将activity的bitmap缩小的9分之1,因为本身我就要对图片进行模糊处理,s所以不需要太高规格的图片
        //将图片体积缩小,模糊效率也会等比例加快
        Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmap, bitmap.getWidth() / 3, bitmap.getHeight() / 3, false);
        //将缩小后的图片模糊化
        Bitmap blurBitmap = getBlurBitmap(activity, scaledBitmap, 5);
        rootView.setAlpha(0);
        rootView.setBackgroundDrawable(new BitmapDrawable(blurBitmap));
        alphaAnim(rootView, 0, 1, animDuration);
    }

当用户点下按钮时,我们需要立刻就将模糊后的图片显示出来, 下面是我的模糊图片代码:

/**
     * android系统的模糊方法
     * @param bitmap 要模糊的图片
     * @param radius 模糊等级 >=0 && <=25
     */
    public static Bitmap blurBitmap(Context context, Bitmap bitmap, int radius) {

        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1){
            //Let's create an empty bitmap with the same size of the bitmap we want to blur
            Bitmap outBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888);
            //Instantiate a new Renderscript
            RenderScript rs = RenderScript.create(context);
            //Create an Intrinsic Blur Script using the Renderscript
            ScriptIntrinsicBlur blurScript = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));
            //Create the Allocations (in/out) with the Renderscript and the in/out bitmaps
            Allocation allIn = Allocation.createFromBitmap(rs, bitmap);
            Allocation allOut = Allocation.createFromBitmap(rs, outBitmap);
            //Set the radius of the blur
            blurScript.setRadius(radius);
            //Perform the Renderscript
            blurScript.setInput(allIn);
            blurScript.forEach(allOut);
            //Copy the final bitmap created by the out Allocation to the outBitmap
            allOut.copyTo(outBitmap);
            //recycle the original bitmap
            bitmap.recycle();
            //After finishing everything, we destroy the Renderscript.
            rs.destroy();
            return outBitmap;
        }else{
            return bitmap;
        }
    }

android里面的高斯模糊我大概总结了一下 基本有三种, 优缺点都有, 我用的是系统推荐的, 速度比较快,而且也简单, 但只能支持android版本17以上, 但现在手机用android4.2以下的估计也很少了.

第二种就是利用glide自定义类继承BitmapTransformation来实现在加载图片时模糊图片,但和第一种差不多,也要android版本17以上才能用

第三种就是用java层的代码, 手动算出像素值, 因为图片处理的代码逻辑都是用java实现的, 所以效率极差, 不推荐.

最后在说一下那个弹出蠕动的动画, 很简单20行代码就ok了, 我是用属性动画写的, 让弹窗view的宽和高的规模从0到1, 然后在从1到0.95, 这样就造成了一个弹出的动态效果, 很easy吧

 private void showAnim(final View view, float start, final float end, int duration, final boolean isWhile) {

        ValueAnimator va = ValueAnimator.ofFloat(start, end).setDuration(duration);
        va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float value = (float) animation.getAnimatedValue();
                view.setPivotX(view.getWidth());
                view.setPivotY(0);
                view.setScaleX(value);
                view.setScaleY(value);
            }
        });
        va.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                if (isWhile) showAnim(view, end, 0.95f, animDuration / 3, false);
            }
        });
        va.start();
    }

demo下载http://download.csdn.net/detail/qq_35549248/9847375

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值