pull/1615/head
parent
a02e0c746a
commit
a0b25dce29
@ -0,0 +1,323 @@ |
|||||||
|
package io.legado.app.ui.widget.text |
||||||
|
|
||||||
|
import android.content.Context |
||||||
|
import android.graphics.Canvas |
||||||
|
import android.graphics.Color |
||||||
|
import android.graphics.Paint |
||||||
|
import android.graphics.Path |
||||||
|
import android.util.AttributeSet |
||||||
|
import android.util.TypedValue |
||||||
|
import android.view.View |
||||||
|
import io.legado.app.R |
||||||
|
import io.legado.app.lib.theme.accentColor |
||||||
|
|
||||||
|
@Suppress("unused", "PrivatePropertyName") |
||||||
|
class BevelLabelView @JvmOverloads constructor( |
||||||
|
context: Context, |
||||||
|
attrs: AttributeSet? = null |
||||||
|
) : View(context, attrs) { |
||||||
|
|
||||||
|
private val MODE_LEFT_TOP = 0 |
||||||
|
private val MODE_RIGHT_TOP = 1 |
||||||
|
private val MODE_LEFT_BOTTOM = 2 |
||||||
|
private val MODE_RIGHT_BOTTOM = 3 |
||||||
|
private val MODE_LEFT_TOP_FILL = 4 |
||||||
|
private val MODE_RIGHT_TOP_FILL = 5 |
||||||
|
private val MODE_LEFT_BOTTOM_FILL = 6 |
||||||
|
private val MODE_RIGHT_BOTTOM_FILL = 7 |
||||||
|
|
||||||
|
private var mBgColor: Int |
||||||
|
private var mText: String |
||||||
|
private var mTextSize: Int |
||||||
|
private var mTextColor: Int |
||||||
|
private var mLength: Int |
||||||
|
private var mCorner: Int |
||||||
|
private var mMode: Int |
||||||
|
private var mPaint: Paint = Paint(Paint.ANTI_ALIAS_FLAG) |
||||||
|
private var path: Path = Path() |
||||||
|
private var mWidth = 0 |
||||||
|
private var mHeight: Int = 0 |
||||||
|
private var mRotate = 45 //因为默认模式是1,所以这时是45度 |
||||||
|
|
||||||
|
private var mX: Int = 0 |
||||||
|
private var mY: Int = 0 |
||||||
|
|
||||||
|
init { |
||||||
|
val typedArray = context.obtainStyledAttributes(attrs, R.styleable.BevelLabelView) |
||||||
|
mBgColor = typedArray.getColor( |
||||||
|
R.styleable.BevelLabelView_label_bg_color, |
||||||
|
context.accentColor |
||||||
|
) //默认红色 |
||||||
|
mText = typedArray.getString(R.styleable.BevelLabelView_label_text) ?: "" |
||||||
|
mTextSize = |
||||||
|
typedArray.getDimensionPixelOffset( |
||||||
|
R.styleable.BevelLabelView_label_text_size, |
||||||
|
sp2px(11) |
||||||
|
) |
||||||
|
mTextColor = typedArray.getColor(R.styleable.BevelLabelView_label_text_color, Color.WHITE) |
||||||
|
mLength = |
||||||
|
typedArray.getDimensionPixelOffset(R.styleable.BevelLabelView_label_length, dip2px(40)) |
||||||
|
mCorner = typedArray.getDimensionPixelOffset(R.styleable.BevelLabelView_label_corner, 0) |
||||||
|
mMode = typedArray.getInt(R.styleable.BevelLabelView_label_mode, 1) |
||||||
|
mPaint.isAntiAlias = true |
||||||
|
typedArray.recycle() |
||||||
|
} |
||||||
|
|
||||||
|
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { |
||||||
|
super.onMeasure(widthMeasureSpec, heightMeasureSpec) |
||||||
|
mWidth = MeasureSpec.getSize(widthMeasureSpec) |
||||||
|
mHeight = mWidth |
||||||
|
} |
||||||
|
|
||||||
|
override fun onDraw(canvas: Canvas) { |
||||||
|
mPaint.color = mBgColor |
||||||
|
drawBackgroundText(canvas) |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
private fun drawBackgroundText(canvas: Canvas) { |
||||||
|
check(mWidth == mHeight) { |
||||||
|
"width must equal to height" //标签view 是一个正方形, |
||||||
|
} |
||||||
|
when (mMode) { |
||||||
|
MODE_LEFT_TOP -> { |
||||||
|
mCorner = 0 //没有铺满的时候mCorner要归零; |
||||||
|
leftTopMeasure() |
||||||
|
getLeftTop() |
||||||
|
} |
||||||
|
MODE_RIGHT_TOP -> { |
||||||
|
mCorner = 0 |
||||||
|
rightTopMeasure() |
||||||
|
getRightTop() |
||||||
|
} |
||||||
|
MODE_LEFT_BOTTOM -> { |
||||||
|
mCorner = 0 |
||||||
|
leftBottomMeasure() |
||||||
|
getLeftBottom() |
||||||
|
} |
||||||
|
MODE_RIGHT_BOTTOM -> { |
||||||
|
mCorner = 0 |
||||||
|
rightBottomMeasure() |
||||||
|
getRightBottom() |
||||||
|
} |
||||||
|
MODE_LEFT_TOP_FILL -> { |
||||||
|
leftTopMeasure() |
||||||
|
getLeftTopFill() |
||||||
|
if (mCorner != 0) { |
||||||
|
canvas.drawPath(path, mPaint) |
||||||
|
getLeftTop() |
||||||
|
} |
||||||
|
} |
||||||
|
MODE_RIGHT_TOP_FILL -> { |
||||||
|
rightTopMeasure() |
||||||
|
getRightTopFill() |
||||||
|
if (mCorner != 0) { |
||||||
|
canvas.drawPath(path, mPaint) |
||||||
|
getRightTop() |
||||||
|
} |
||||||
|
} |
||||||
|
MODE_LEFT_BOTTOM_FILL -> { |
||||||
|
leftBottomMeasure() |
||||||
|
getLeftBottomFill() |
||||||
|
if (mCorner != 0) { |
||||||
|
canvas.drawPath(path, mPaint) |
||||||
|
getLeftBottom() |
||||||
|
} |
||||||
|
} |
||||||
|
MODE_RIGHT_BOTTOM_FILL -> { |
||||||
|
rightBottomMeasure() |
||||||
|
getRightBottomFill() |
||||||
|
if (mCorner != 0) { |
||||||
|
canvas.drawPath(path, mPaint) |
||||||
|
getRightBottom() |
||||||
|
} |
||||||
|
} |
||||||
|
else -> {} |
||||||
|
} |
||||||
|
canvas.drawPath(path, mPaint) |
||||||
|
mPaint.textSize = mTextSize.toFloat() |
||||||
|
mPaint.textAlign = Paint.Align.CENTER |
||||||
|
mPaint.color = mTextColor |
||||||
|
canvas.translate(mX.toFloat(), mY.toFloat()) |
||||||
|
canvas.rotate(mRotate.toFloat()) |
||||||
|
val baseLineY = (-(mPaint.descent() + mPaint.ascent())).toInt() / 2 //基线中间点的y轴计算公式 |
||||||
|
canvas.drawText(mText, 0f, baseLineY.toFloat(), mPaint) |
||||||
|
} |
||||||
|
|
||||||
|
private fun rightBottomMeasure() { |
||||||
|
mRotate = -45 |
||||||
|
mX = mWidth / 2 + mLength / 4 |
||||||
|
mY = mX |
||||||
|
} |
||||||
|
|
||||||
|
private fun leftBottomMeasure() { |
||||||
|
mRotate = 45 |
||||||
|
mX = mWidth / 2 - mLength / 4 |
||||||
|
mY = mHeight / 2 + mLength / 4 |
||||||
|
} |
||||||
|
|
||||||
|
private fun rightTopMeasure() { |
||||||
|
mRotate = 45 |
||||||
|
mX = mWidth / 2 + mLength / 4 |
||||||
|
mY = mHeight / 2 - mLength / 4 |
||||||
|
} |
||||||
|
|
||||||
|
private fun leftTopMeasure() { |
||||||
|
mRotate = -45 |
||||||
|
mX = mWidth / 2 - mLength / 4 |
||||||
|
mY = mX |
||||||
|
} |
||||||
|
|
||||||
|
//左上角铺满 |
||||||
|
private fun getLeftTopFill() { |
||||||
|
if (mCorner != 0) { |
||||||
|
path.addRoundRect( |
||||||
|
0f, |
||||||
|
0f, |
||||||
|
(mWidth / 2).toFloat(), |
||||||
|
(mHeight / 2).toFloat(), |
||||||
|
floatArrayOf(mCorner.toFloat(), mCorner.toFloat(), 0f, 0f, 0f, 0f, 0f, 0f), |
||||||
|
Path.Direction.CW |
||||||
|
) |
||||||
|
} else { |
||||||
|
path.moveTo(0f, 0f) |
||||||
|
path.lineTo(mWidth.toFloat(), 0f) |
||||||
|
path.lineTo(0f, mHeight.toFloat()) |
||||||
|
path.close() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
//左上角不铺满 |
||||||
|
private fun getLeftTop() { |
||||||
|
path.moveTo(if (mCorner != 0) mCorner.toFloat() else (mWidth - mLength).toFloat(), 0f) |
||||||
|
path.lineTo(mWidth.toFloat(), 0f) |
||||||
|
path.lineTo(0f, mHeight.toFloat()) |
||||||
|
path.lineTo(0f, if (mCorner != 0) mCorner.toFloat() else (mHeight - mLength).toFloat()) |
||||||
|
path.close() |
||||||
|
} |
||||||
|
|
||||||
|
//左下角铺满 |
||||||
|
private fun getLeftBottomFill() { |
||||||
|
if (mCorner != 0) { |
||||||
|
path.addRoundRect( |
||||||
|
0f, |
||||||
|
(mHeight / 2).toFloat(), |
||||||
|
(mWidth / 2).toFloat(), |
||||||
|
mHeight.toFloat(), |
||||||
|
floatArrayOf(0f, 0f, 0f, 0f, 0f, 0f, mCorner.toFloat(), mCorner.toFloat()), |
||||||
|
Path.Direction.CW |
||||||
|
) |
||||||
|
} else { |
||||||
|
path.moveTo(0f, 0f) |
||||||
|
path.lineTo(mWidth.toFloat(), mHeight.toFloat()) |
||||||
|
path.lineTo(0f, mHeight.toFloat()) |
||||||
|
path.close() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
//左下角不铺满 |
||||||
|
private fun getLeftBottom() { |
||||||
|
path.moveTo(0f, 0f) |
||||||
|
path.lineTo(mWidth.toFloat(), mHeight.toFloat()) |
||||||
|
path.lineTo( |
||||||
|
if (mCorner != 0) mCorner.toFloat() else (mWidth - mLength).toFloat(), |
||||||
|
mHeight.toFloat() |
||||||
|
) |
||||||
|
path.lineTo(0f, if (mCorner != 0) (mHeight - mCorner).toFloat() else mLength.toFloat()) |
||||||
|
path.close() |
||||||
|
} |
||||||
|
|
||||||
|
//右上角铺满 |
||||||
|
private fun getRightTopFill() { |
||||||
|
if (mCorner != 0) { |
||||||
|
path.addRoundRect( |
||||||
|
(mWidth / 2).toFloat(), |
||||||
|
0f, |
||||||
|
mWidth.toFloat(), |
||||||
|
(mHeight / 2).toFloat(), |
||||||
|
floatArrayOf(0f, 0f, mCorner.toFloat(), mCorner.toFloat(), 0f, 0f, 0f, 0f), |
||||||
|
Path.Direction.CW |
||||||
|
) |
||||||
|
} else { |
||||||
|
path.moveTo(0f, 0f) |
||||||
|
path.lineTo(mWidth.toFloat(), 0f) |
||||||
|
path.lineTo(mWidth.toFloat(), mHeight.toFloat()) |
||||||
|
path.close() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
//右上角不铺满 |
||||||
|
private fun getRightTop() { |
||||||
|
path.moveTo(0f, 0f) |
||||||
|
path.lineTo(if (mCorner != 0) (mWidth - mCorner).toFloat() else mLength.toFloat(), 0f) |
||||||
|
path.lineTo( |
||||||
|
mWidth.toFloat(), |
||||||
|
if (mCorner != 0) mCorner.toFloat() else (mHeight - mLength).toFloat() |
||||||
|
) |
||||||
|
path.lineTo(mWidth.toFloat(), mHeight.toFloat()) |
||||||
|
path.close() |
||||||
|
} |
||||||
|
|
||||||
|
//右下角铺满 |
||||||
|
private fun getRightBottomFill() { |
||||||
|
if (mCorner != 0) { |
||||||
|
path.addRoundRect( |
||||||
|
(mWidth / 2).toFloat(), |
||||||
|
(mHeight / 2).toFloat(), |
||||||
|
mWidth.toFloat(), |
||||||
|
mHeight.toFloat(), |
||||||
|
floatArrayOf(0f, 0f, 0f, 0f, mCorner.toFloat(), mCorner.toFloat(), 0f, 0f), |
||||||
|
Path.Direction.CW |
||||||
|
) |
||||||
|
} else { |
||||||
|
path.moveTo(mWidth.toFloat(), 0f) |
||||||
|
path.lineTo(mWidth.toFloat(), mHeight.toFloat()) |
||||||
|
path.lineTo(0f, mHeight.toFloat()) |
||||||
|
path.close() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
//右下角不铺满 |
||||||
|
private fun getRightBottom() { |
||||||
|
path.moveTo(mWidth.toFloat(), 0f) |
||||||
|
path.lineTo( |
||||||
|
mWidth.toFloat(), |
||||||
|
if (mCorner != 0) (mHeight - mCorner).toFloat() else mLength.toFloat() |
||||||
|
) |
||||||
|
path.lineTo( |
||||||
|
if (mCorner != 0) (mWidth - mCorner).toFloat() else mLength.toFloat(), |
||||||
|
mHeight.toFloat() |
||||||
|
) |
||||||
|
path.lineTo(0f, mHeight.toFloat()) |
||||||
|
path.close() |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* @param sp 转换大小 |
||||||
|
*/ |
||||||
|
@Suppress("SameParameterValue") |
||||||
|
private fun sp2px(sp: Int): Int { |
||||||
|
return TypedValue.applyDimension( |
||||||
|
TypedValue.COMPLEX_UNIT_SP, |
||||||
|
sp.toFloat(), |
||||||
|
resources.displayMetrics |
||||||
|
) |
||||||
|
.toInt() |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param dip 转换大小 |
||||||
|
*/ |
||||||
|
@Suppress("SameParameterValue") |
||||||
|
private fun dip2px(dip: Int): Int { |
||||||
|
return TypedValue.applyDimension( |
||||||
|
TypedValue.COMPLEX_UNIT_DIP, |
||||||
|
dip.toFloat(), |
||||||
|
resources.displayMetrics |
||||||
|
) |
||||||
|
.toInt() |
||||||
|
} |
||||||
|
|
||||||
|
} |
Loading…
Reference in new issue