废话不说,直接上代码
第一步: 自定义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 );
}
}