Android 三分钟搞定APP内水印背景(仿企微,钉钉)

791 阅读2分钟

废话不说,直接上代码

第一步: 自定义VIEW

/**
 * Created by Marcello
 * Time  Created at 2022/7/4.
 *  Description: 水印自定义view
 */
class WaterMarkView @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {

    private val mTextPaint: TextPaint = TextPaint()
    private val rect = Rect()


    private var mDegrees = -15
    private var mTextColor = 0
    private var mTextSize = 20
    private var mTextBold = false
    private var mDx = 50f //水平间距
    private var mDy = 20f //垂直间距
    private var mAlign: Paint.Align? = null
    private var mDrawWidth: Float = 0f
    private var mDrawHeight: Float = 0f
    private var mText = ""

    init {
        initAttrs(context, attrs)
    }

    private fun initAttrs(context: Context, attrs: AttributeSet?) {
        val typedArray: TypedArray =
            context.obtainStyledAttributes(attrs, R.styleable.WaterMarkView)
        mDegrees = typedArray.getInt(
            R.styleable.WaterMarkView_water_mark_degree,
            WaterMarkUtil.waterMarkInfo?.degrees
        )
        val text: String = typedArray.getString(R.styleable.WaterMarkView_water_mark_text) ?: ""
        mText = if (text.isEmpty()) WaterMarkUtil.waterMarkInfo.mStr else text
        mTextColor = typedArray.getColor(
            R.styleable.WaterMarkView_water_mark_textColor,
            WaterMarkUtil.waterMarkInfo?.mTextColor
        )
        mTextSize = typedArray.getDimensionPixelSize(
            R.styleable.WaterMarkView_water_mark_textSize,
            WaterMarkUtil.waterMarkInfo?.mTextSize
        )
        mTextBold = typedArray.getBoolean(
            R.styleable.WaterMarkView_water_mark_textBold,
            WaterMarkUtil.waterMarkInfo?.mTextBold
        )
        mDx = typedArray.getDimensionPixelSize(
            R.styleable.WaterMarkView_water_mark_dx,
            WaterMarkUtil.waterMarkInfo?.mDx
        ).toFloat()
        mDy = typedArray.getDimensionPixelSize(
            R.styleable.WaterMarkView_water_mark_dy,
            WaterMarkUtil.waterMarkInfo?.mDy
        ).toFloat()
        val align: Int = typedArray.getInt(
            R.styleable.WaterMarkView_water_mark_align,
            WaterMarkUtil.waterMarkInfo?.mAlign ?: 1
        )
        mAlign =
            if (align == 0) Paint.Align.LEFT else if (align == 2) Paint.Align.RIGHT else Paint.Align.CENTER
        typedArray.recycle()
        //默认设置
        setBackgroundColor(Color.TRANSPARENT)
        mTextPaint.setAntiAlias(true)
        mTextPaint.setFlags(Paint.ANTI_ALIAS_FLAG)
        mTextPaint.setColor(mTextColor)
        mTextPaint.setTextSize(mTextSize.toFloat())
        mTextPaint.setTypeface(if (mTextBold) Typeface.DEFAULT_BOLD else Typeface.DEFAULT)
        mTextPaint.setTextAlign(mAlign)


    }


    override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
        super.onLayout(changed, left, top, right, bottom)
        //获取实际宽高
        mDrawWidth = getWidth().toFloat();
        mDrawHeight = getHeight().toFloat();

    }

    @SuppressLint("DrawAllocation")
    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        //获取文字长度和宽度
//        mTextPaint.getTextBounds(mText, 0, mText.length, rect)
        val staticLayout = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            StaticLayout.Builder.obtain(mText, 0, mText.length, mTextPaint, 500)
                .build()
        } else {
            StaticLayout(
                mText,
                mTextPaint,
                 500,
                Layout.Alignment.ALIGN_NORMAL,
                1.0F,
                0.0F,
                false
            )
        }
        val textWidth: Int =
            if (staticLayout.lineCount > 0) Math.round(staticLayout.getLineWidth(0)) else 100
        val textHeight: Int = staticLayout.height
        //获取每个单独的item的宽高
        val itemWidth: Float = textWidth + mDx
        val itemHeight: Float = textHeight + mDy
        //获取水平、垂直方向需要绘制的个数
        val hSize = Math.round(mDrawWidth / itemWidth)

        val vSize = Math.round(mDrawHeight / itemHeight)

        //X轴开始坐标
        val xStart: Float = ScreenUtils.dip2px(context,20f).toFloat()
        //Y轴开始坐标
        val yStart: Float = mDy / 2 + textHeight

        //创建透明画布
        canvas?.drawColor(Color.TRANSPARENT)
        // 获取跟清晰的图像采样
        mTextPaint.setDither(true)
        mTextPaint.setFilterBitmap(true)
        //旋转对应角度
        canvas?.rotate(mDegrees.toFloat(), mDrawWidth / 2, mDrawHeight / 2)
        canvas?.save()
        val abs = Math.abs(mDegrees)
//       val absItemHeight= itemHeight+(itemWidth*Math.sin(Math.PI*abs/180).toFloat())
//       val absItemWidth= itemWidth*Math.cos(Math.PI*abs/180).toFloat()
        //画X轴方向
        for (i in 0 until hSize) {

            //画Y轴方向
            for (j in 0 until vSize) {
                val xDraw = if (i == 0 && j==0) {
                    xStart
                } else if(j==0 && i!=0){
                    itemWidth
                }else{
                    0f
                }
                val yDraw= if (i == 0 && j==0) {
                    yStart
                } else if(i%2==0){
                    itemHeight
                }else if(j==0){
                    0f
                }else{
                    -itemHeight
                }
                //平移
                canvas?.translate(xDraw, yDraw)
                staticLayout.draw(canvas)
            }
        }
        canvas?.restore()

    }

    override fun dispatchTouchEvent(event: MotionEvent?): Boolean {
        return false
    }

    override fun onTouchEvent(event: MotionEvent?): Boolean {
        return false
    }
}

自定义VIEW 的自定义属性

<!--    水印view  自定义属性-->
<declare-styleable name="WaterMarkView">
    <attr name="water_mark_degree" format="integer|reference" />
    <attr name="water_mark_text" format="string|reference" />
    <attr name="water_mark_textColor" format="color|reference" />
    <attr name="water_mark_textSize" format="dimension|reference" />
    <attr name="water_mark_textBold" format="boolean" />
    <attr name="water_mark_dx" format="dimension|reference" />
    <attr name="water_mark_dy" format="dimension|reference" />
    <attr name="water_mark_align" format="dimension">
        <enum name="LEFT" value="0" />
        <enum name="CENTER" value="1" />
        <enum name="RIGHT" value="2" />
    </attr>

</declare-styleable>

第二步 水印工具类和默认值

/**
 * Created by Marcello
 * Time  Created at 2022/7/4.
 *  Description: 水印工具类
 */
class WaterMarkUtil {
    companion object{
        @JvmField
        var waterMarkInfo:WaterMarkInfo= buildWaterInfo {  }

        //添加水印到跟布局
        @JvmStatic
        fun showWatermarkView(activity: Activity) {
            val rootView: ViewGroup = activity.window.decorView.findViewById(android.R.id.content)
            val findViewWithTag = rootView.findViewWithTag<View>("water_view")
            if(findViewWithTag==null){
                val  waterMarkView=WaterMarkView(activity)
                waterMarkView.setTag("water_view")
                val  layoutParams=FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT,FrameLayout.LayoutParams.MATCH_PARENT)
                rootView.addView(waterMarkView,layoutParams)
            }
        }

        //初始化参数
        @JvmStatic
        fun setWaterMarkInfo(textContent: String,context:Context) {
            waterMarkInfo=buildWaterInfo {
                mStr=textContent
                degrees=-10
                mTextSize=ScreenUtils.dip2px(context,10f)
                mDx=ScreenUtils.dip2px(context,60f)
                mDy=ScreenUtils.dip2px(context,160f)
            }
        }


    }
/**
 * Created by Marcello
 * Time  Created at 2022/7/4.
 *  Description: 水印信息
 */
data class WaterMarkInfo internal constructor(
    var degrees: Int = -15,
    var mTextColor: Int = Color.parseColor("#08000000"),
    var mTextBold: Boolean = false,
    var mTextSize: Int = 24,
    var mDx: Int =88*2,
    var mDy: Int = 160 *2,
    var mAlign: Int =1 ,
    var mStr: String ="" ,
)
fun buildWaterInfo(buildAction: WaterMarkInfo.() -> Unit) = WaterMarkInfo().apply(buildAction)

第三步 APPLICATION 里注册ActivityLifecycleCallbacks

override fun onCreate() {
    super.onCreate()
ActivityLifecycleCallbacksImp.INSTANCE.init(this)
}

第四步 添加水印

@Override
public void onActivityCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState) {
        //排除掉 登录页和启动页
    if (!(activity.getClass().getCanonicalName().equals( SplashActivity.class.getCanonicalName() )
            || activity.getClass().getCanonicalName().equals( LoginActivity.class.getCanonicalName() ))) {
        //水印
        activity.getWindow().getDecorView().postDelayed( () -> WaterMarkUtil.showWatermarkView( activity ), 300 );
    }

}