美文网首页高级UI
面试官: 为了信息安全,来给聊天界面加上水印

面试官: 为了信息安全,来给聊天界面加上水印

作者: Android扫地僧 | 来源:发表于2020-02-20 10:46 被阅读0次

    点击上方 **Android扫地僧 **,选择 **星标 **公众号

    重磅资源、干货,快上车!

    推荐阅读

    1.Android进阶资源分享(2月15日更新)

    2.Android面试题附答案,会了起码30K

    3.吊打面试官———Java内存模型(详解)

    “为了信息安全,来给聊天界面加上水印,怎么实现?****”

    这个是我曾经面试遇到过的一个问题,回答完就让我回去等消息了

    image 先来看看效果:情景纯属虚构,如有雷同,概不负责
    image

    你乍得一听,简单!上手就写,直接给ReyclerView加个背景,你会发现聊天界面滚动,水印不动,甚至水印不显示;或者我直接给Item加个背景,也不行,被item背景色覆盖了,并且受item布局控制


    首先拆解一下这个需求:

    1、聊天界面一般都是基于RecyclerView / ListView实现,所以我们的水印是要加在RecyclerView / ListView上

    2、水印不会覆盖聊天消息也就是遮挡Item

    3、水印会随着聊天界面滚动

    懵逼了吗?其实这个场景在很多企业软件中都有用到,企业微信,钉钉,一般都是将员工姓名和工号,添加在一些保密级别比较高的界面上,防止截图泄露内容。


    添加水印在android开发中比较常见,对于那些固定水印直接可以用图片平铺就可以实现了。如果需要根据文字生成动态的水印,可以使用自定义Drawable重写onDraw()方法,来绘制相应的文字。

    <article style="margin: 0px; padding: 0px; max-width: 100%; overflow-wrap: break-word !important; box-sizing: border-box !important;">

    但如果需要在RecyclerView 上绘制动态水印。你使用上面的方法就会发现,当RecyclerView滑动的时候,水印并不会随着滑动,而是一直固定在哪里。如果需求是需要水印也跟着RecyclerView滑动,那我们应该怎么实现呢?

    实现步骤:

    1、自定义一个Drawable用于动态显示水印。

    2、用ItemDecoration来绘制水印,并且跟踪滚动位置。

    3、由于是重写ItemDecoration的onDraw(),所以该水印的位置在ItemView之下,如果ItemView有背景色将会遮挡住水印

    效果图:

    单个水印

    image

    多个水印

    image

    我知道你会说:

    Talk is cheap, show me the code.

    核心代码WatermarkDecoration.java

    package com.duoshou7.app.watermark;
    ​
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.graphics.ColorFilter;
    import android.graphics.Paint;
    import android.graphics.PixelFormat;
    import android.graphics.Rect;
    import android.graphics.drawable.Drawable;
    import android.support.annotation.ColorInt;
    import android.support.annotation.NonNull;
    import android.support.annotation.Nullable;
    import android.support.v7.widget.RecyclerView;
    ​
    import java.util.List;
    ​
    /**
     * recyclerView 添加水印
     */
    ​
    public class WatermarkDecoration extends RecyclerView.ItemDecoration {
    ​
        WatermarkParams mWatermarkParams;
        WaterMarkDrawable mDrawable;
        int mScrollY = 0;
        int mListDrawTextSize = -1;
    ​
        private WatermarkDecoration(WatermarkParams mWatermarkParams) {
            this.mWatermarkParams = mWatermarkParams;
            mDrawable = new WaterMarkDrawable();
            mListDrawTextSize = mWatermarkParams.mDrawTexts != null
                    ? mWatermarkParams.mDrawTexts.size() : -1;
        }
    ​
        @Override
        public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
            super.onDraw(c, parent, state);
            int top = -mScrollY;
            int right = parent.getWidth() / mWatermarkParams.mColumnNum;
            int start = Math.abs(mScrollY / mWatermarkParams.mRowHeight);
            int max = (parent.getHeight() + mScrollY) / mWatermarkParams.mRowHeight + 1;
            for (int i = start; i < max; i++) {
                int tempTop = top + (mWatermarkParams.mRowHeight * start);
                int tempLeft = right / 3;
                for (int j = 0; j < mWatermarkParams.mColumnNum; j++) {
                    mDrawable.setBounds(tempLeft, top, tempLeft + right, tempTop + mWatermarkParams.mRowHeight);
                    if (mListDrawTextSize == -1) {
                        mDrawable.setDrawTex(mWatermarkParams.mDrawText);
                    } else {
                        mDrawable.setDrawTex(mWatermarkParams.mDrawTexts.get((i * mWatermarkParams.mColumnNum + j) % mListDrawTextSize));
                    }
                    mDrawable.draw(c);
                    tempLeft += right;
                }
                top += mWatermarkParams.mRowHeight;
            }
    ​
        }
    ​
        //跟踪recyclerView 滚动值
        public void setScrollY(int dy) {
            this.mScrollY += dy;
        }
    ​
        class WaterMarkDrawable extends Drawable {
    ​
            Paint mPaint;
            String mTempText;
    ​
            public WaterMarkDrawable() {
                mPaint = new Paint();
                mPaint.setColor(mWatermarkParams.mTextColor);
                mPaint.setTextSize(mWatermarkParams.mTextSize);
                mPaint.setAlpha((int) (255 * mWatermarkParams.mAlpha));
                mPaint.setAntiAlias(true);
                mPaint.setTextAlign(Paint.Align.LEFT);
            }
    ​
            @Override
            public void draw(@NonNull Canvas canvas) {
                Rect rect = getBounds();
                canvas.save();
                canvas.rotate(mWatermarkParams.mDegrees, rect.left, rect.bottom);
                canvas.drawText(mTempText, rect.left, rect.bottom, mPaint);
                canvas.restore();
            }
    ​
            @Override
            public void setAlpha(int alpha) {
    ​
            }
    ​
            @Override
            public void setColorFilter(@Nullable ColorFilter colorFilter) {
    ​
            }
    ​
            @Override
            public int getOpacity() {
                return PixelFormat.UNKNOWN;
            }
    ​
            public void setDrawTex(String mDrawTex) {
                this.mTempText = mDrawTex;
            }
        }
    ​
        public static class Builder {
            private WatermarkParams mWatermarkParams;
    ​
            //水印背景字符
            public Builder(String drawText) {
                mWatermarkParams = new WatermarkParams(drawText);
            }
    ​
            //多个水印背景字符
            public Builder(List<String> drawTexts) {
                mWatermarkParams = new WatermarkParams(drawTexts);
            }
    ​
            //文字大小
            public Builder setTextSize(int textSize) {
                mWatermarkParams.mTextSize = textSize;
                return this;
            }
    ​
            //文字颜色
            public Builder setTextColor(@ColorInt int textColor) {
                mWatermarkParams.mTextColor = textColor;
                return this;
            }
    ​
            //展示多少列
            public Builder setColumnNum(int columnNum) {
                mWatermarkParams.mColumnNum = columnNum;
                return this;
            }
    ​
            //行高
            public Builder setRowHeight(int rowHeight) {
                mWatermarkParams.mRowHeight = rowHeight;
                return this;
            }
    ​
            //倾斜角度
            public Builder setDegrees(int degrees) {
                mWatermarkParams.mDegrees = degrees;
                return this;
            }
    ​
            //透明度 0-1
            public Builder setAlpha(float alpha) {
                mWatermarkParams.mAlpha = alpha;
                return this;
            }
    ​
            public WatermarkDecoration builder() {
                return new WatermarkDecoration(mWatermarkParams);
            }
        }
    ​
        static class WatermarkParams {
            String mDrawText;
            List<String> mDrawTexts;
            int mTextColor = Color.parseColor("#ebebeb");
            int mTextSize = 40;
            int mColumnNum = 3;
            int mRowHeight = 240;
            int mDegrees = -30;
            float mAlpha = 0.5f;
    ​
    ​
            public WatermarkParams(String mDrawText) {
                this.mDrawText = mDrawText;
            }
    ​
            public WatermarkParams(List<String> mDrawTexts) {
                this.mDrawTexts = mDrawTexts;
            }
        }
    }
    ​
    

    如何使用:

     private void setWatermark() {
    //        WatermarkDecoration.Builder builder = new WatermarkDecoration.Builder("单个水印")
    //                .setColumnNum(3)
    //                .setTextColor(Color.GRAY)
    //                .setTextSize(35);
            //多个水印
            WatermarkDecoration.Builder builder = new WatermarkDecoration.Builder(getMultiple())
                    .setColumnNum(3)
                    .setTextColor(Color.GRAY)
                    .setTextSize(35);
    ​
            mWatermarkDecoration = builder.builder();
    ​
            mRecyclerView.addItemDecoration(mWatermarkDecoration);
            mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
                @Override
                public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                    super.onScrolled(recyclerView, dx, dy);
                    //设置水印滚动位置
                    mWatermarkDecoration.setScrollY(dy);
                }
            });
        }
    

    mark一下,你一定会用得上。

    对于自定义View这方面还不太熟悉的同学,建议这一块重点提升,回复【电子书】即可获取以下热门Android开发书籍,包括启舰的《Android自定义控件开发入门与实战》,直接一本书的内容来讲述自定义View。

    记得按时学习

    扫码领取资源.png

    相关文章

      网友评论

        本文标题:面试官: 为了信息安全,来给聊天界面加上水印

        本文链接:https://www.haomeiwen.com/subject/gzwtqhtx.html