Android水印相机和过渡动画的实现

    最近在做一个叫做小目标的项目,没错就是我大中华地区首富王某的名言:我们先定一个可以实现的小目标,赚他一个亿.

    好了社会我杰哥人狠话不多,接下来直接开始:小目标有个分享页,分享页面有很多图片供用户分享,用户也可以选择自定义拍照或者选择照片上传分享.公司的UI大(mei)神(zi)不想要原生的相机拍照,于是乎就有了以下的想法:


    可以看到我们要实现的功能是:点击拍照图片,图片跟随相机放大到屏幕大小,拍照之后图片跟随变小回到原来的位置.我们都知道Android原生的照相机页面虽然实用,但是完全不给我们定制,我想实现如图所示的效果简直是痴人说梦,既然不能定制那么这里就只有一条路了,那就是自定义相机页面,对于Android开发入门新手来说的我,一想到自定义view或者页面,就鸡冻的不要不要的,瞬间摩拳擦掌跃跃欲试了哈哈...(原谅我就是这么贱害羞)

    今天我要实现的小目标是:如何做出一个带水印的相机和丝丝顺滑的动画效果?首先我们得有一个相机的API,这个相机必须具有的功能是:可以固定拍照后相片的宽高比例.百度了一大堆之后效果都没有我想要的,于是乎上Git上又是一番搜寻,偶然间见到一个国外某大神写的一个相机的API,实现效果还比较理想,使用也简单,马上clone下来试了一下,恩~~效果还不错!就是你了:git地址在这里...

    相机的API有了,现在差的就是动画了,众所周知Android动画目前为止分为两个大类:传统动画和属性动画,传统动画分为...你想多了!我不会告诉你细节的,时间有限对动画不熟悉的道友们可以异步百度查查Android动画详解,我们这里用到的动画是补间动画和属性动画结合.

    东西的原材料准备完了,接下来分析我们要做的东西:这里我们把要实现的页面分为A和B两个页面,A是相机之前的照片展示页面,B是相机页面.这里大家需要清楚的是:所有的动画效果全是在B页面完成,在A页面点击进入B页面的过程中B页面背景是透明的.

    1.将A页面图片的全局位置传入B页面

    这里我们用到的是Rect对象,将rect对象使用intent传入B页面:

        // 创建一个 rect 对象来存储共享元素位置信息
        Rect rect = new Rect();
        ivShareImg.getGlobalVisibleRect(rect);
        // 将位置信息附加到 intent 上
        intent.setSourceBounds(rect);
        startActivity(intent);

    2.B页面接收到位置信息之后,设置View的位置与A页面图片重合:

        // 设置 水印和覆盖图 的位置,使其和上一个界面中图片的位置重合
        mRect = getIntent().getSourceBounds();
        scale = 1f * CommonFunction.getScreenWidth(this) / mRect.width();
        FrameLayout.LayoutParams params;
        params = new FrameLayout.LayoutParams(mRect.width(), mRect.height());
        params.setMargins(mRect.left, mRect.top, mRect.right, mRect.bottom);
        fl_cover_container.setLayoutParams(params);

    3.B页面view做动画平移和缩放

        // 如果不做伸缩,原本Y偏移位置计算方法:(status+title)-(status+marginTop) = title-marginTop = 48-15 = 33
        float transY = CommonFunction.dp2px(this,33);
        fl_cover_container.animate()
                .setInterpolator(DEFAULT_INTERPOLATOR)
                .setDuration(ANIMDURATION)
                .scaleX(scale)
                .scaleY(scale)
                .translationY(transY + (mRect.height() * scale - mRect.height()) / 2)
                .withEndAction(new Runnable() {
                    @Override
                    public void run() {
                        isAnim = false;
                        coverImage.startAnimation(
                                OptAnimationLoader.loadAnimation(CameraActivity.this, R.anim.camera_img_exit_anim));
                        fl_camera.setVisibility(View.VISIBLE);
                        if (!cameraView.isStarted()){
                            // 启动相机
                            cameraView.start();
                        }
                    }
                })
                .start();

    这里需要注意的是View位置的变化方向,如果A页面的图片距顶部的值小于B页面View距顶部的值,则用上述代码做平移,反之translationY()里面的代码取负.

    头部和底部View做平移动画:

        // 底部视图向上平移
        fl_controls_container.startAnimation(OptAnimationLoader.loadAnimation(this, R.anim.camera_slip_enter));
        // 头部视图向下平移
        topbar.startAnimation(OptAnimationLoader.loadAnimation(this, R.anim.camera_top_slip_in));

    将相机的位置设置到View动画结束之后的位置:

        // 设置camera的大小边界
        FrameLayout.LayoutParams paramsc = new FrameLayout.LayoutParams(CommonFunction.getScreenWidth(this),
                (int) (mRect.height()*scale));
        int marginTop = CommonFunction.dp2px(this,48)+ CommonFunction.getStatusBarHeight(this);
        paramsc.setMargins(0,marginTop,0,0);
        fl_camera.setLayoutParams(paramsc);

    4.拍照结束后退场动画:

EventBus.getDefault().post(new BusEvent(1,"拍照回调bitmap",previewBitmap));
        previewBitmap = null;
        cameraView.stop();
        fl_camera.setVisibility(View.GONE);
        blackCover.setVisibility(View.VISIBLE);
        fl_cover_container.animate()
                .setInterpolator(DEFAULT_INTERPOLATOR)
                .setDuration(ANIMDURATION)
                .scaleX(1)
                .scaleY(1)
                .translationY(0)
                .withEndAction(new Runnable() {
                    @Override
                    public void run() {
                        finish();
                        // 屏蔽activity默认跳转动画
                        overridePendingTransition(0,0);
                    }
                })
                .start();
        fl_controls_container.startAnimation(OptAnimationLoader.loadAnimation(this, R.anim.camera_slip_exit));
        topbar.startAnimation(OptAnimationLoader.loadAnimation(this, R.anim.camera_top_slip_out));

    5.注意事项

    在A页面跳转进入B页面的时候,如果A页面传入B页面的值为Bitmap,那么使用intent传递需要注意bitmap的大小不能超过1M,不然会直接抛出异常,所以传统做法是对图片进行质量压缩:

            ByteArrayOutputStream baos=new ByteArrayOutputStream();
            // 将图片进行质量压缩
            bitmap.compress(Bitmap.CompressFormat.JPEG, 10, baos);
            byte [] bitmapByte =baos.toByteArray();
            intent.putExtra("bitmap",bitmapByte);

    bitmap.compress():第一个参数为输出图片的格式,有JPEG格式,PNG格式和WEBP格式.第二个参数为压缩百分比,100为不压缩.第三个参数为字节输出流

    

    ok,写到这里大家已经对整个水印相机的编写了如指掌了,如果感兴趣可以自己尝试写一写,我们的口号是:不要只做一个代码的搬运工...

    来来来,小demo的Git地址(喜欢可以给个start哟羡慕):点击打开链接



    

    



猜你喜欢

转载自blog.csdn.net/lobo1992/article/details/80250022