Android APP 自定義水印(kotlin)

  • 自定義WaterMarkView
/**
 * Created by guc on 2020/6/12.
 * Description:水印
 */
class WaterMarkView(context: Context, attrs: AttributeSet?, defStyle: Int) :
    View(context, attrs, defStyle) {
    constructor(context: Context, attrs: AttributeSet) : this(context, attrs, 0)
    constructor(context: Context) : this(context, null, 0)

    companion object {
        const val TAG = "WaterMarkView"
        const val DEFAULT_RADIAN: Float = (PI / 6).toFloat()
    }

    var markText: CharSequence = "水印"
    private val path = Path()
    private var lineHeight = 0  //px
    private var markerTextSize = 0f
    private var singleMarkerWidth = 0
    private var singleMarkerHeight = 0
    private var markerSpace = 0//間距
    private var deltaFixSpace = 0//修正間距
    var radian = DEFAULT_RADIAN//弧度
    private var repeatCountX = 1
    private var repeatCountY = 1
    private var repeatSpace = 1

    private val paint = Paint().apply {
        isAntiAlias = true
        color = Color.DKGRAY
        style = Paint.Style.FILL
    }

    init {
        val a = context.obtainStyledAttributes(attrs, R.styleable.WaterMarkView)
        markText = a.getString(R.styleable.WaterMarkView_markerText) ?: "水印"
        LogG.loge(TAG, "獲取前:$lineHeight")
        lineHeight = a.getDimensionPixelOffset(
            R.styleable.WaterMarkView_lineHeight,
            dp2px(50f)
        )
        LogG.loge(TAG, "獲取後:$lineHeight")
        markerTextSize = a.getDimension(R.styleable.WaterMarkView_markerTextSize, 48f)
        markerSpace =
            a.getDimension(R.styleable.WaterMarkView_markerSpace, dp2px(30f).toFloat()).toInt()
        deltaFixSpace =
            a.getDimension(R.styleable.WaterMarkView_fixSpace, markerSpace.toFloat() / 2).toInt()
        radian = a.getFloat(R.styleable.WaterMarkView_radian, DEFAULT_RADIAN)
        a.recycle()
    }

    @SuppressLint("DrawAllocation")
    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        val rectText = Rect()
        paint.textSize = markerTextSize
        paint.getTextBounds(markText.toString(), 0, markText.toString().length, rectText)
        singleMarkerWidth = rectText.width()
        singleMarkerHeight = rectText.height()
        val space = "a"
        paint.getTextBounds(space, 0, space.length, rectText)
        repeatSpace = markerSpace / rectText.width()
        repeatCountX = ceil(width * 1.0 / cos(radian) / (singleMarkerWidth + markerSpace)).toInt()
        repeatCountY = floor(height * 1.0 / lineHeight).toInt()
        val itemStdHeight = getItemHeight()
        for (i in 1..repeatCountY) {
            path.reset()
            path.moveTo(0f + deltaFixSpace, i * lineHeight.toFloat())
            val x = getEndX(itemStdHeight, i) + deltaFixSpace
            val y = getEndY(itemStdHeight, i)
            path.lineTo(x, y)
            canvas.drawTextOnPath(getLineText(), path, 0f, 0f, paint)
        }

    }

    private fun isEnoughHeight(itemHeight: Float, times: Int) = itemHeight <= times * lineHeight

    private fun getEndX(itemHeight: Float, times: Int): Float =
        if (isEnoughHeight(
                itemHeight,
                times
            )
        ) width.toFloat() else (lineHeight * times * 1.0f / tan(
            radian
        ))

    private fun getEndY(itemHeight: Float, times: Int): Float =
        if (isEnoughHeight(itemHeight, times)) (lineHeight * times - itemHeight) else 0f

    private fun getItemHeight(): Float {
        return width * tan(radian)
    }

    private fun getLineText(): String {
        val sb = StringBuilder()
        repeat(repeatCountX) {
            sb.append(markText)
            repeat(repeatSpace) {
                sb.append(" ")
            }
        }
        return sb.toString()
    }

    private fun dp2px(dipValue: Float): Int {
        val scale = resources.displayMetrics.density
        return (dipValue * scale + 0.5f).toInt()
    }
}
  • 自定義屬性
<declare-styleable name="WaterMarkView">
   <attr name="markerText" format="reference|string" /> <!--水印文本-->
   <attr name="lineHeight" format="dimension" /> <!--水印行間距-->
   <attr name="markerTextSize" format="dimension" /> <!--水印字體大小-->
   <attr name="markerSpace" format="dimension" /> <!--水印間距-->
   <attr name="fixSpace" format="dimension" /> <!--界面左側邊距設置-->
   <attr name="radian" format="float" /> <!--傾斜角度-->
</declare-styleable>
  • 創建水印佈局layout_water_mark.xml
<?xml version="1.0" encoding="utf-8"?>
<com.kframe.widget.WaterMarkView             
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:alpha="0.2"
    app:lineHeight="80dp"
    app:markerSpace="50dp"
    app:markerText=""
    app:radian="0.524" />
  • 調用Api
object WaterMarkUtil{
    private const val VIEW_TAG = "view_tag"
    var enable = false
    var text = "水印"

    //在Activity的onActivityStart()方法調用
    fun onActivityStart(activity:Activity){
         val rootView = activity.window.decorView.findViewById<ViewGroup>(android.R.id.content)
        if (enable && text.isNotEmpty() && rootView.findViewWithTag<View>(VIEW_TAG) == null) {
            val waterMarkView =
                LayoutInflater.from(activity)
                    .inflate(R.layout.layout_water_mark, null) as WaterMarkView
            waterMarkView.tag = VIEW_TAG
            waterMarkView.markText = text
            rootView.addView(waterMarkView)
        }
        
    }

}
  • 水印設置及調用
//開啓水印,並設置水印文本
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    WaterMarkUtil.enable = true
    WaterMarkUtil.text = "自定義水印"
}
//添加水印
override fun onStart() {
    super.onStart()
    WaterMarkUtil.onActivityStart(this)
}
  • 效果

效果展示

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章