kunfei 4 年之前
父節點
當前提交
535383878e

+ 2 - 1
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 =

+ 323 - 0
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()
+    }
+
+}

+ 26 - 10
app/src/main/res/layout/item_http_tts.xml

@@ -1,18 +1,20 @@
 <?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:gravity="center"
-    android:orientation="horizontal"
     android:padding="8dp">
 
     <io.legado.app.lib.theme.view.ThemeRadioButton
         android:id="@+id/cb_name"
         android:layout_width="0dp"
         android:layout_height="wrap_content"
-        android:layout_weight="1"
-        tools:ignore="RtlSymmetry" />
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintLeft_toLeftOf="parent"
+        app:layout_constraintRight_toLeftOf="@id/iv_edit"
+        app:layout_constraintTop_toTopOf="parent" />
 
     <androidx.appcompat.widget.AppCompatImageView
         android:id="@+id/iv_edit"
@@ -23,20 +25,34 @@
         android:contentDescription="@string/edit"
         android:padding="6dp"
         android:src="@drawable/ic_edit"
-        android:tint="@color/primaryText" />
+        android:tint="@color/primaryText"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintRight_toLeftOf="@+id/iv_menu_delete"
+        app:layout_constraintTop_toTopOf="parent" />
 
     <androidx.appcompat.widget.AppCompatImageView
         android:id="@+id/iv_menu_delete"
         android:layout_width="36dp"
         android:layout_height="36dp"
-        android:padding="6dp"
         android:background="?attr/selectableItemBackgroundBorderless"
+        android:padding="6dp"
         android:src="@drawable/ic_clear_all"
         android:tint="@color/primaryText"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintRight_toRightOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintVertical_bias="1.0"
         tools:ignore="RtlHardcoded" />
 
-    <View
-        android:layout_width="12dp"
-        android:layout_height="36dp" />
+    <io.legado.app.ui.widget.text.BevelLabelView
+        android:id="@+id/label_sys"
+        android:layout_width="36dp"
+        android:layout_height="36dp"
+        android:visibility="gone"
+        app:label_mode="right_top"
+        app:label_length="24dp"
+        app:label_text="SYS"
+        app:layout_constraintRight_toRightOf="parent"
+        app:layout_constraintTop_toTopOf="parent" />
 
-</LinearLayout>
+</androidx.constraintlayout.widget.ConstraintLayout>

+ 28 - 0
app/src/main/res/values/attrs.xml

@@ -213,4 +213,32 @@
     <declare-styleable name="Preference">
         <attr name="isBottomBackground" />
     </declare-styleable>
+
+    <!--斜角标签-->
+    <declare-styleable name="BevelLabelView">
+        <!--背景颜色-->
+        <attr name="label_bg_color" format="color|reference" />
+        <!--文字-->
+        <attr name="label_text" format="string" />
+        <!-- 文字颜色-->
+        <attr name="label_text_color" format="color|reference" />
+        <!--文字大小-->
+        <attr name="label_text_size" format="dimension" />
+
+        <attr name="label_length" format="dimension" />
+        <!--   圆角-->
+        <attr name="label_corner" format="dimension" />
+
+        <attr name="label_mode">
+            <!--   fill是沾满整个,-->
+            <enum name="left_top" value="0" />
+            <enum name="right_top" value="1" />
+            <enum name="left_bottom" value="2" />
+            <enum name="right_bottom" value="3" />
+            <enum name="left_top_fill" value="4" />
+            <enum name="right_top_fill" value="5" />
+            <enum name="left_bottom_fill" value="6" />
+            <enum name="right_bottom_fill" value="7" />
+        </attr>
+    </declare-styleable>
 </resources>