diff --git a/app/src/main/java/io/legado/app/ui/book/read/config/SpeakEngineDialog.kt b/app/src/main/java/io/legado/app/ui/book/read/config/SpeakEngineDialog.kt index ec2cdfadb..9d644e0dd 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/config/SpeakEngineDialog.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/config/SpeakEngineDialog.kt @@ -70,7 +70,7 @@ class SpeakEngineDialog(val callBack: CallBack) : BaseDialogFragment(R.layout.di override fun onStart() { super.onStart() - setLayout(0.2f, 0.9f) + setLayout(0.92f, 0.9f) } override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) { @@ -91,6 +91,7 @@ class SpeakEngineDialog(val callBack: CallBack) : BaseDialogFragment(R.layout.di sysTtsViews.add(cbName) ivEdit.gone() ivMenuDelete.gone() + labelSys.visible() cbName.text = engine.label cbName.tag = engine.name cbName.isChecked = diff --git a/app/src/main/java/io/legado/app/ui/widget/text/BevelLabelView.kt b/app/src/main/java/io/legado/app/ui/widget/text/BevelLabelView.kt new file mode 100644 index 000000000..098d013fb --- /dev/null +++ b/app/src/main/java/io/legado/app/ui/widget/text/BevelLabelView.kt @@ -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() + } + +} \ No newline at end of file diff --git a/app/src/main/res/layout/item_http_tts.xml b/app/src/main/res/layout/item_http_tts.xml index 47cb00990..a6530c541 100644 --- a/app/src/main/res/layout/item_http_tts.xml +++ b/app/src/main/res/layout/item_http_tts.xml @@ -1,18 +1,20 @@ - + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintLeft_toLeftOf="parent" + app:layout_constraintRight_toLeftOf="@id/iv_edit" + app:layout_constraintTop_toTopOf="parent" /> + android:tint="@color/primaryText" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintRight_toLeftOf="@+id/iv_menu_delete" + app:layout_constraintTop_toTopOf="parent" /> - + - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml index fd11b974c..51aed2498 100644 --- a/app/src/main/res/values/attrs.xml +++ b/app/src/main/res/values/attrs.xml @@ -213,4 +213,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file