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)
}
  • 效果

水印效果图

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
要自定义一个 Dialog,可以按照以下步骤进行操作: 1. 创建一个新的类来扩展 Dialog 类。在这个新类中,你可以定义你自己的布局和逻辑。 2. 在 onCreate() 方法中,加载布局并通过 findViewById() 方法获取视图元素的引用。 3. 在 show() 方法中,设置 Dialog 的大小和样式,然后将布局设置为 Dialog 的内容视图。 4. 在布局中添加必要的视图元素,如 TextView、EditText、Button 等。 5. 在适当的位置为这些视图元素添加响应事件的侦听器。 以下是一个简单的示例代码: ``` class MyDialog(context: Context) : Dialog(context) { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.my_dialog_layout) // 获取视图元素 val titleTextView = findViewById<TextView>(R.id.titleTextView) val cancelButton = findViewById<Button>(R.id.cancelButton) // 为按钮添加点击事件侦听器 cancelButton.setOnClickListener { dismiss() } } override fun show() { super.show() // 设置 Dialog 的大小和样式 window?.setLayout(WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.WRAP_CONTENT) window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) } } ``` 在这个示例中,我们创建了一个名为 MyDialog 的新类,它扩展了 Dialog 类。在 onCreate() 方法中,我们加载了自定义布局,并使用 findViewById() 方法获取了 TextView 和 Button 的引用。在适当的位置,我们为取消按钮添加了一个点击事件侦听器。在 show() 方法中,我们设置了 Dialog 的大小和样式,并将布局设置为 Dialog 的内容视图。 最后,我们可以在需要的地方创建并显示这个 Dialog: ``` val dialog = MyDialog(context) dialog.show() ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值