pull/32/head
parent
b400ab884b
commit
2539b1a67f
@ -0,0 +1,233 @@ |
||||
package io.legado.app.ui.widget |
||||
|
||||
import android.content.Context |
||||
import android.graphics.Color |
||||
import android.graphics.drawable.ShapeDrawable |
||||
import android.graphics.drawable.shapes.RoundRectShape |
||||
import android.text.TextUtils |
||||
import android.util.AttributeSet |
||||
import android.util.TypedValue |
||||
import android.view.Gravity |
||||
import android.view.View |
||||
import android.view.ViewGroup |
||||
import android.widget.FrameLayout |
||||
import android.widget.FrameLayout.LayoutParams |
||||
import android.widget.TabWidget |
||||
import androidx.appcompat.widget.AppCompatTextView |
||||
import io.legado.app.R |
||||
|
||||
|
||||
/** |
||||
* Created by milad heydari on 5/6/2016. |
||||
*/ |
||||
class BadgeView @JvmOverloads constructor( |
||||
context: Context, |
||||
attrs: AttributeSet? = null, |
||||
defStyle: Int = android.R.attr.textViewStyle |
||||
) : AppCompatTextView(context, attrs, defStyle) { |
||||
|
||||
/** |
||||
* @return Returns true if view is hidden on badge value 0 or null; |
||||
*/ |
||||
/** |
||||
* @param hideOnNull the hideOnNull to set |
||||
*/ |
||||
var isHideOnNull = true |
||||
set(hideOnNull) { |
||||
field = hideOnNull |
||||
text = text |
||||
} |
||||
private var radius: Float = 0.toFloat() |
||||
|
||||
val badgeCount: Int? |
||||
get() { |
||||
if (text == null) { |
||||
return null |
||||
} |
||||
val text = text.toString() |
||||
try { |
||||
return Integer.parseInt(text) |
||||
} catch (e: NumberFormatException) { |
||||
return null |
||||
} |
||||
|
||||
} |
||||
|
||||
var badgeGravity: Int |
||||
get() { |
||||
val params = layoutParams as LayoutParams |
||||
return params.gravity |
||||
} |
||||
set(gravity) { |
||||
val params = layoutParams as LayoutParams |
||||
params.gravity = gravity |
||||
layoutParams = params |
||||
} |
||||
|
||||
val badgeMargin: IntArray |
||||
get() { |
||||
val params = layoutParams as LayoutParams |
||||
return intArrayOf(params.leftMargin, params.topMargin, params.rightMargin, params.bottomMargin) |
||||
} |
||||
|
||||
init { |
||||
|
||||
init() |
||||
} |
||||
|
||||
private fun init() { |
||||
if (layoutParams !is LayoutParams) { |
||||
val layoutParams = LayoutParams( |
||||
ViewGroup.LayoutParams.WRAP_CONTENT, |
||||
ViewGroup.LayoutParams.WRAP_CONTENT, |
||||
Gravity.CENTER |
||||
) |
||||
setLayoutParams(layoutParams) |
||||
} |
||||
|
||||
// set default font |
||||
setTextColor(Color.WHITE) |
||||
//setTypeface(Typeface.DEFAULT_BOLD); |
||||
setTextSize(TypedValue.COMPLEX_UNIT_SP, 11f) |
||||
setPadding(dip2Px(5f), dip2Px(1f), dip2Px(5f), dip2Px(1f)) |
||||
radius = 8f |
||||
|
||||
// set default background |
||||
setBackground(radius, Color.parseColor("#d3321b")) |
||||
|
||||
gravity = Gravity.CENTER |
||||
|
||||
// default values |
||||
isHideOnNull = true |
||||
setBadgeCount(0) |
||||
minWidth = dip2Px(16f) |
||||
minHeight = dip2Px(16f) |
||||
} |
||||
|
||||
fun setBackground(dipRadius: Float, badgeColor: Int) { |
||||
val radius = dip2Px(dipRadius) |
||||
val radiusArray = floatArrayOf( |
||||
radius.toFloat(), |
||||
radius.toFloat(), |
||||
radius.toFloat(), |
||||
radius.toFloat(), |
||||
radius.toFloat(), |
||||
radius.toFloat(), |
||||
radius.toFloat(), |
||||
radius.toFloat() |
||||
) |
||||
|
||||
val roundRect = RoundRectShape(radiusArray, null, null) |
||||
val bgDrawable = ShapeDrawable(roundRect) |
||||
bgDrawable.paint.color = badgeColor |
||||
background = bgDrawable |
||||
} |
||||
|
||||
fun setBackground(badgeColor: Int) { |
||||
setBackground(radius, badgeColor) |
||||
} |
||||
|
||||
/** |
||||
* @see android.widget.TextView.setText |
||||
*/ |
||||
override fun setText(text: CharSequence, type: BufferType) { |
||||
if (isHideOnNull && TextUtils.isEmpty(text)) { |
||||
visibility = View.GONE |
||||
} else { |
||||
visibility = View.VISIBLE |
||||
} |
||||
super.setText(text, type) |
||||
} |
||||
|
||||
fun setBadgeCount(count: Int) { |
||||
text = count.toString() |
||||
if (count == 0) { |
||||
visibility = View.GONE |
||||
} |
||||
} |
||||
|
||||
fun setHighlight(highlight: Boolean) { |
||||
setBackground(resources.getColor(if (highlight) R.color.highlight else R.color.darker_gray)) |
||||
} |
||||
|
||||
fun setBadgeMargin(dipMargin: Int) { |
||||
setBadgeMargin(dipMargin, dipMargin, dipMargin, dipMargin) |
||||
} |
||||
|
||||
fun setBadgeMargin(leftDipMargin: Int, topDipMargin: Int, rightDipMargin: Int, bottomDipMargin: Int) { |
||||
val params = layoutParams as LayoutParams |
||||
params.leftMargin = dip2Px(leftDipMargin.toFloat()) |
||||
params.topMargin = dip2Px(topDipMargin.toFloat()) |
||||
params.rightMargin = dip2Px(rightDipMargin.toFloat()) |
||||
params.bottomMargin = dip2Px(bottomDipMargin.toFloat()) |
||||
layoutParams = params |
||||
} |
||||
|
||||
fun incrementBadgeCount(increment: Int) { |
||||
val count = badgeCount |
||||
if (count == null) { |
||||
setBadgeCount(increment) |
||||
} else { |
||||
setBadgeCount(increment + count) |
||||
} |
||||
} |
||||
|
||||
fun decrementBadgeCount(decrement: Int) { |
||||
incrementBadgeCount(-decrement) |
||||
} |
||||
|
||||
/** |
||||
* Attach the BadgeView to the TabWidget |
||||
* @param target the TabWidget to attach the BadgeView |
||||
* @param tabIndex index of the tab |
||||
*/ |
||||
fun setTargetView(target: TabWidget, tabIndex: Int) { |
||||
val tabView = target.getChildTabViewAt(tabIndex) |
||||
setTargetView(tabView) |
||||
} |
||||
|
||||
/** |
||||
* Attach the BadgeView to the target view |
||||
* @param target the view to attach the BadgeView |
||||
*/ |
||||
fun setTargetView(target: View?) { |
||||
if (parent != null) { |
||||
(parent as ViewGroup).removeView(this) |
||||
} |
||||
|
||||
if (target == null) { |
||||
return |
||||
} |
||||
|
||||
if (target.parent is FrameLayout) { |
||||
(target.parent as FrameLayout).addView(this) |
||||
|
||||
} else if (target.parent is ViewGroup) { |
||||
// use a new FrameLayout container for adding badge |
||||
val parentContainer = target.parent as ViewGroup |
||||
val groupIndex = parentContainer.indexOfChild(target) |
||||
parentContainer.removeView(target) |
||||
|
||||
val badgeContainer = FrameLayout(context) |
||||
val parentLayoutParams = target.layoutParams |
||||
|
||||
badgeContainer.layoutParams = parentLayoutParams |
||||
target.layoutParams = ViewGroup.LayoutParams( |
||||
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT |
||||
) |
||||
|
||||
parentContainer.addView(badgeContainer, groupIndex, parentLayoutParams) |
||||
badgeContainer.addView(target) |
||||
|
||||
badgeContainer.addView(this) |
||||
} |
||||
|
||||
} |
||||
|
||||
/** |
||||
* converts dip to px |
||||
*/ |
||||
private fun dip2Px(dip: Float): Int { |
||||
return (dip * context.resources.displayMetrics.density + 0.5f).toInt() |
||||
} |
||||
} |
@ -0,0 +1,207 @@ |
||||
package io.legado.app.ui.widget |
||||
|
||||
import android.animation.Animator |
||||
import android.animation.AnimatorSet |
||||
import android.animation.ObjectAnimator |
||||
import android.content.Context |
||||
import android.graphics.Canvas |
||||
import android.graphics.Color |
||||
import android.graphics.Paint |
||||
import android.graphics.RectF |
||||
import android.util.AttributeSet |
||||
import android.util.TypedValue |
||||
import android.view.View |
||||
import android.view.animation.LinearInterpolator |
||||
import io.legado.app.R |
||||
|
||||
/** |
||||
* RotateLoading |
||||
* Created by Victor on 2015/4/28. |
||||
*/ |
||||
class RotateLoading : View { |
||||
|
||||
private var mPaint: Paint? = null |
||||
|
||||
private var loadingRectF: RectF? = null |
||||
private var shadowRectF: RectF? = null |
||||
|
||||
private var topDegree = 10 |
||||
private var bottomDegree = 190 |
||||
|
||||
private var arc: Float = 0.toFloat() |
||||
|
||||
private var thisWidth: Int = 0 |
||||
|
||||
private var changeBigger = true |
||||
|
||||
private var shadowPosition: Int = 0 |
||||
|
||||
var isStart = false |
||||
private set |
||||
|
||||
var loadingColor: Int = 0 |
||||
|
||||
private var speedOfDegree: Int = 0 |
||||
|
||||
private var speedOfArc: Float = 0.toFloat() |
||||
|
||||
constructor(context: Context) : super(context) { |
||||
initView(context, null) |
||||
} |
||||
|
||||
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { |
||||
initView(context, attrs) |
||||
} |
||||
|
||||
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { |
||||
initView(context, attrs) |
||||
} |
||||
|
||||
private fun initView(context: Context, attrs: AttributeSet?) { |
||||
loadingColor = Color.WHITE |
||||
thisWidth = dpToPx(context, DEFAULT_WIDTH.toFloat()) |
||||
shadowPosition = dpToPx(getContext(), DEFAULT_SHADOW_POSITION.toFloat()) |
||||
speedOfDegree = DEFAULT_SPEED_OF_DEGREE |
||||
|
||||
if (null != attrs) { |
||||
val typedArray = context.obtainStyledAttributes(attrs, R.styleable.RotateLoading) |
||||
loadingColor = typedArray.getColor(R.styleable.RotateLoading_loading_color, Color.WHITE) |
||||
thisWidth = typedArray.getDimensionPixelSize( |
||||
R.styleable.RotateLoading_loading_width, |
||||
dpToPx(context, DEFAULT_WIDTH.toFloat()) |
||||
) |
||||
shadowPosition = typedArray.getInt(R.styleable.RotateLoading_shadow_position, DEFAULT_SHADOW_POSITION) |
||||
speedOfDegree = typedArray.getInt(R.styleable.RotateLoading_loading_speed, DEFAULT_SPEED_OF_DEGREE) |
||||
typedArray.recycle() |
||||
} |
||||
speedOfArc = (speedOfDegree / 4).toFloat() |
||||
mPaint = Paint() |
||||
mPaint!!.color = loadingColor |
||||
mPaint!!.isAntiAlias = true |
||||
mPaint!!.style = Paint.Style.STROKE |
||||
mPaint!!.strokeWidth = thisWidth.toFloat() |
||||
mPaint!!.strokeCap = Paint.Cap.ROUND |
||||
} |
||||
|
||||
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) { |
||||
super.onSizeChanged(w, h, oldw, oldh) |
||||
|
||||
arc = 10f |
||||
|
||||
loadingRectF = |
||||
RectF((2 * thisWidth).toFloat(), (2 * thisWidth).toFloat(), (w - 2 * thisWidth).toFloat(), (h - 2 * thisWidth).toFloat()) |
||||
shadowRectF = RectF( |
||||
(2 * thisWidth + shadowPosition).toFloat(), |
||||
(2 * thisWidth + shadowPosition).toFloat(), |
||||
(w - 2 * thisWidth + shadowPosition).toFloat(), |
||||
(h - 2 * thisWidth + shadowPosition).toFloat() |
||||
) |
||||
} |
||||
|
||||
|
||||
override fun onDraw(canvas: Canvas) { |
||||
super.onDraw(canvas) |
||||
|
||||
if (!isStart) { |
||||
return |
||||
} |
||||
|
||||
mPaint!!.color = Color.parseColor("#1a000000") |
||||
canvas.drawArc(shadowRectF!!, topDegree.toFloat(), arc, false, mPaint!!) |
||||
canvas.drawArc(shadowRectF!!, bottomDegree.toFloat(), arc, false, mPaint!!) |
||||
|
||||
mPaint!!.color = loadingColor |
||||
canvas.drawArc(loadingRectF!!, topDegree.toFloat(), arc, false, mPaint!!) |
||||
canvas.drawArc(loadingRectF!!, bottomDegree.toFloat(), arc, false, mPaint!!) |
||||
|
||||
topDegree += speedOfDegree |
||||
bottomDegree += speedOfDegree |
||||
if (topDegree > 360) { |
||||
topDegree = topDegree - 360 |
||||
} |
||||
if (bottomDegree > 360) { |
||||
bottomDegree = bottomDegree - 360 |
||||
} |
||||
|
||||
if (changeBigger) { |
||||
if (arc < 160) { |
||||
arc += speedOfArc |
||||
invalidate() |
||||
} |
||||
} else { |
||||
if (arc > speedOfDegree) { |
||||
arc -= 2 * speedOfArc |
||||
invalidate() |
||||
} |
||||
} |
||||
if (arc >= 160 || arc <= 10) { |
||||
changeBigger = !changeBigger |
||||
invalidate() |
||||
} |
||||
} |
||||
|
||||
fun start() { |
||||
startAnimator() |
||||
isStart = true |
||||
invalidate() |
||||
} |
||||
|
||||
fun stop() { |
||||
stopAnimator() |
||||
invalidate() |
||||
} |
||||
|
||||
private fun startAnimator() { |
||||
val scaleXAnimator = ObjectAnimator.ofFloat(this, "scaleX", 0.0f, 1f) |
||||
val scaleYAnimator = ObjectAnimator.ofFloat(this, "scaleY", 0.0f, 1f) |
||||
scaleXAnimator.setDuration(300) |
||||
scaleXAnimator.setInterpolator(LinearInterpolator()) |
||||
scaleYAnimator.setDuration(300) |
||||
scaleYAnimator.setInterpolator(LinearInterpolator()) |
||||
val animatorSet = AnimatorSet() |
||||
animatorSet.playTogether(scaleXAnimator, scaleYAnimator) |
||||
animatorSet.start() |
||||
} |
||||
|
||||
private fun stopAnimator() { |
||||
val scaleXAnimator = ObjectAnimator.ofFloat(this, "scaleX", 1f, 0f) |
||||
val scaleYAnimator = ObjectAnimator.ofFloat(this, "scaleY", 1f, 0f) |
||||
scaleXAnimator.setDuration(300) |
||||
scaleXAnimator.setInterpolator(LinearInterpolator()) |
||||
scaleYAnimator.setDuration(300) |
||||
scaleYAnimator.setInterpolator(LinearInterpolator()) |
||||
val animatorSet = AnimatorSet() |
||||
animatorSet.playTogether(scaleXAnimator, scaleYAnimator) |
||||
animatorSet.addListener(object : Animator.AnimatorListener { |
||||
override fun onAnimationStart(animation: Animator) { |
||||
|
||||
} |
||||
|
||||
override fun onAnimationEnd(animation: Animator) { |
||||
isStart = false |
||||
} |
||||
|
||||
override fun onAnimationCancel(animation: Animator) { |
||||
|
||||
} |
||||
|
||||
override fun onAnimationRepeat(animation: Animator) { |
||||
|
||||
} |
||||
}) |
||||
animatorSet.start() |
||||
} |
||||
|
||||
|
||||
fun dpToPx(context: Context, dpVal: Float): Int { |
||||
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpVal, context.resources.displayMetrics).toInt() |
||||
} |
||||
|
||||
companion object { |
||||
|
||||
private val DEFAULT_WIDTH = 6 |
||||
private val DEFAULT_SHADOW_POSITION = 2 |
||||
private val DEFAULT_SPEED_OF_DEGREE = 10 |
||||
} |
||||
|
||||
} |
@ -0,0 +1,56 @@ |
||||
package io.legado.app.ui.widget.image |
||||
|
||||
import android.annotation.SuppressLint |
||||
import android.content.Context |
||||
import android.graphics.Canvas |
||||
import android.graphics.Path |
||||
import android.util.AttributeSet |
||||
|
||||
|
||||
class CoverImageView : androidx.appcompat.widget.AppCompatImageView { |
||||
internal var width: Float = 0.toFloat() |
||||
internal var height: Float = 0.toFloat() |
||||
|
||||
constructor(context: Context) : super(context) |
||||
|
||||
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) |
||||
|
||||
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) |
||||
|
||||
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) { |
||||
super.onLayout(changed, left, top, right, bottom) |
||||
width = getWidth().toFloat() |
||||
height = getHeight().toFloat() |
||||
} |
||||
|
||||
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { |
||||
val measuredWidth = MeasureSpec.getSize(widthMeasureSpec) |
||||
val measuredHeight = measuredWidth * 7 / 5 |
||||
super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(measuredHeight, MeasureSpec.EXACTLY)) |
||||
} |
||||
|
||||
override fun onDraw(canvas: Canvas) { |
||||
if (width >= 10 && height > 10) { |
||||
@SuppressLint("DrawAllocation") |
||||
val path = Path() |
||||
//四个圆角 |
||||
path.moveTo(10f, 0f) |
||||
path.lineTo(width - 10, 0f) |
||||
path.quadTo(width, 0f, width, 10f) |
||||
path.lineTo(width, height - 10) |
||||
path.quadTo(width, height, width - 10, height) |
||||
path.lineTo(10f, height) |
||||
path.quadTo(0f, height, 0f, height - 10) |
||||
path.lineTo(0f, 10f) |
||||
path.quadTo(0f, 0f, 10f, 0f) |
||||
|
||||
canvas.clipPath(path) |
||||
} |
||||
super.onDraw(canvas) |
||||
} |
||||
|
||||
fun setHeight(height: Int) { |
||||
val width = height * 5 / 7 |
||||
minimumWidth = width |
||||
} |
||||
} |
@ -0,0 +1,92 @@ |
||||
package io.legado.app.ui.widget.image |
||||
|
||||
import android.annotation.SuppressLint |
||||
import android.content.Context |
||||
import android.graphics.Canvas |
||||
import android.graphics.Path |
||||
import android.util.AttributeSet |
||||
import androidx.appcompat.widget.AppCompatImageView |
||||
import io.legado.app.R |
||||
|
||||
class FilletImageView : AppCompatImageView { |
||||
internal var width: Float = 0.toFloat() |
||||
internal var height: Float = 0.toFloat() |
||||
private var leftTopRadius: Int = 0 |
||||
private var rightTopRadius: Int = 0 |
||||
private var rightBottomRadius: Int = 0 |
||||
private var leftBottomRadius: Int = 0 |
||||
|
||||
constructor(context: Context) : super(context) |
||||
|
||||
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { |
||||
init(context, attrs) |
||||
} |
||||
|
||||
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { |
||||
init(context, attrs) |
||||
} |
||||
|
||||
private fun init(context: Context, attrs: AttributeSet) { |
||||
// 读取配置 |
||||
val array = context.obtainStyledAttributes(attrs, R.styleable.FilletImageView) |
||||
val defaultRadius = 5 |
||||
val radius = array.getDimensionPixelOffset(R.styleable.FilletImageView_radius, defaultRadius) |
||||
leftTopRadius = array.getDimensionPixelOffset(R.styleable.FilletImageView_left_top_radius, defaultRadius) |
||||
rightTopRadius = array.getDimensionPixelOffset(R.styleable.FilletImageView_right_top_radius, defaultRadius) |
||||
rightBottomRadius = |
||||
array.getDimensionPixelOffset(R.styleable.FilletImageView_right_bottom_radius, defaultRadius) |
||||
leftBottomRadius = array.getDimensionPixelOffset(R.styleable.FilletImageView_left_bottom_radius, defaultRadius) |
||||
|
||||
//如果四个角的值没有设置,那么就使用通用的radius的值。 |
||||
if (defaultRadius == leftTopRadius) { |
||||
leftTopRadius = radius |
||||
} |
||||
if (defaultRadius == rightTopRadius) { |
||||
rightTopRadius = radius |
||||
} |
||||
if (defaultRadius == rightBottomRadius) { |
||||
rightBottomRadius = radius |
||||
} |
||||
if (defaultRadius == leftBottomRadius) { |
||||
leftBottomRadius = radius |
||||
} |
||||
array.recycle() |
||||
|
||||
} |
||||
|
||||
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) { |
||||
super.onLayout(changed, left, top, right, bottom) |
||||
width = getWidth().toFloat() |
||||
height = getHeight().toFloat() |
||||
} |
||||
|
||||
override fun onDraw(canvas: Canvas) { |
||||
//这里做下判断,只有图片的宽高大于设置的圆角距离的时候才进行裁剪 |
||||
val maxLeft = Math.max(leftTopRadius, leftBottomRadius) |
||||
val maxRight = Math.max(rightTopRadius, rightBottomRadius) |
||||
val minWidth = maxLeft + maxRight |
||||
val maxTop = Math.max(leftTopRadius, rightTopRadius) |
||||
val maxBottom = Math.max(leftBottomRadius, rightBottomRadius) |
||||
val minHeight = maxTop + maxBottom |
||||
if (width >= minWidth && height > minHeight) { |
||||
@SuppressLint("DrawAllocation") val path = Path() |
||||
//四个角:右上,右下,左下,左上 |
||||
path.moveTo(leftTopRadius.toFloat(), 0f) |
||||
path.lineTo(width - rightTopRadius, 0f) |
||||
path.quadTo(width, 0f, width, rightTopRadius.toFloat()) |
||||
|
||||
path.lineTo(width, height - rightBottomRadius) |
||||
path.quadTo(width, height, width - rightBottomRadius, height) |
||||
|
||||
path.lineTo(leftBottomRadius.toFloat(), height) |
||||
path.quadTo(0f, height, 0f, height - leftBottomRadius) |
||||
|
||||
path.lineTo(0f, leftTopRadius.toFloat()) |
||||
path.quadTo(0f, 0f, leftTopRadius.toFloat(), 0f) |
||||
|
||||
canvas.clipPath(path) |
||||
} |
||||
super.onDraw(canvas) |
||||
} |
||||
|
||||
} |
@ -0,0 +1,175 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<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:id="@+id/cv_content" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content" |
||||
android:clickable="true" |
||||
android:focusable="true" |
||||
android:foreground="?android:attr/selectableItemBackground" |
||||
tools:ignore="UnusedAttribute"> |
||||
|
||||
<io.legado.app.ui.widget.image.CoverImageView |
||||
android:id="@+id/iv_cover" |
||||
android:layout_width="60dp" |
||||
android:layout_height="80dp" |
||||
android:layout_margin="8dp" |
||||
android:contentDescription="@string/img_cover" |
||||
android:scaleType="centerCrop" |
||||
android:src="@drawable/img_cover_default" |
||||
android:transitionName="img_cover" |
||||
app:layout_constraintBottom_toBottomOf="parent" |
||||
app:layout_constraintLeft_toLeftOf="parent" |
||||
app:layout_constraintTop_toTopOf="parent" |
||||
tools:ignore="UnusedAttribute" /> |
||||
|
||||
<FrameLayout |
||||
android:id="@+id/fl_has_new" |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
app:layout_constraintRight_toRightOf="parent" |
||||
app:layout_constraintTop_toTopOf="@id/tv_name"> |
||||
|
||||
<io.legado.app.ui.widget.BadgeView |
||||
android:id="@+id/bv_unread" |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
android:layout_gravity="right" |
||||
android:layout_margin="5dp" |
||||
android:includeFontPadding="false" |
||||
tools:ignore="RtlHardcoded" /> |
||||
|
||||
<com.victor.loading.rotate.RotateLoading |
||||
android:id="@+id/rl_loading" |
||||
android:layout_width="26dp" |
||||
android:layout_height="26dp" |
||||
android:layout_gravity="right" |
||||
android:visibility="invisible" |
||||
app:loading_color="@color/colorAccent" |
||||
app:loading_width="2dp" |
||||
tools:ignore="RtlHardcoded" /> |
||||
</FrameLayout> |
||||
|
||||
<TextView |
||||
android:id="@+id/tv_name" |
||||
android:layout_width="0dp" |
||||
android:layout_height="wrap_content" |
||||
android:layout_marginLeft="8dp" |
||||
android:layout_marginTop="8dp" |
||||
android:includeFontPadding="false" |
||||
android:paddingLeft="4dp" |
||||
android:singleLine="true" |
||||
android:text="@string/book_name" |
||||
android:textColor="@color/tv_text_default" |
||||
android:textSize="16sp" |
||||
app:layout_constraintBottom_toTopOf="@+id/tv_author" |
||||
app:layout_constraintLeft_toRightOf="@+id/iv_cover" |
||||
app:layout_constraintRight_toLeftOf="@id/fl_has_new" |
||||
app:layout_constraintTop_toTopOf="parent" |
||||
tools:ignore="RtlHardcoded,RtlSymmetry" /> |
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView |
||||
android:id="@+id/iv_author" |
||||
android:layout_width="@dimen/desc_icon_size" |
||||
android:layout_height="@dimen/desc_icon_size" |
||||
android:layout_marginLeft="8dp" |
||||
android:contentDescription="@string/author" |
||||
android:paddingStart="2dp" |
||||
android:paddingEnd="2dp" |
||||
android:src="@drawable/ic_author" |
||||
app:layout_constraintBottom_toBottomOf="@+id/tv_author" |
||||
app:layout_constraintLeft_toRightOf="@+id/iv_cover" |
||||
app:layout_constraintTop_toTopOf="@+id/tv_author" |
||||
app:tint="@color/tv_text_secondary" |
||||
tools:ignore="RtlHardcoded,RtlSymmetry" /> |
||||
|
||||
<TextView |
||||
android:id="@+id/tv_author" |
||||
android:layout_width="0dp" |
||||
android:layout_height="wrap_content" |
||||
android:includeFontPadding="false" |
||||
android:maxLines="1" |
||||
android:paddingEnd="6dp" |
||||
android:text="@string/author" |
||||
android:textColor="@color/tv_text_secondary" |
||||
android:textSize="13sp" |
||||
app:layout_constraintBottom_toTopOf="@+id/tv_read" |
||||
app:layout_constraintLeft_toRightOf="@+id/iv_author" |
||||
app:layout_constraintRight_toLeftOf="@id/fl_has_new" |
||||
app:layout_constraintTop_toBottomOf="@+id/tv_name" |
||||
tools:ignore="RtlSymmetry" /> |
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView |
||||
android:id="@+id/iv_read" |
||||
android:layout_width="@dimen/desc_icon_size" |
||||
android:layout_height="@dimen/desc_icon_size" |
||||
android:layout_marginLeft="8dp" |
||||
android:contentDescription="@string/read_dur_progress" |
||||
android:paddingStart="2dp" |
||||
android:paddingEnd="2dp" |
||||
android:src="@drawable/ic_history" |
||||
app:layout_constraintBottom_toBottomOf="@+id/tv_read" |
||||
app:layout_constraintLeft_toRightOf="@+id/iv_cover" |
||||
app:layout_constraintTop_toTopOf="@+id/tv_read" |
||||
app:tint="@color/tv_text_secondary" |
||||
tools:ignore="RtlHardcoded,RtlSymmetry" /> |
||||
|
||||
<TextView |
||||
android:id="@+id/tv_read" |
||||
android:layout_width="0dp" |
||||
android:layout_height="wrap_content" |
||||
android:includeFontPadding="false" |
||||
android:singleLine="true" |
||||
android:text="@string/read_dur_progress" |
||||
android:textColor="@color/tv_text_secondary" |
||||
android:textSize="13sp" |
||||
app:layout_constraintBottom_toTopOf="@id/tv_last" |
||||
app:layout_constraintLeft_toRightOf="@+id/iv_read" |
||||
app:layout_constraintRight_toLeftOf="@id/fl_has_new" |
||||
app:layout_constraintTop_toBottomOf="@+id/tv_author" /> |
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView |
||||
android:id="@+id/iv_last" |
||||
android:layout_width="@dimen/desc_icon_size" |
||||
android:layout_height="@dimen/desc_icon_size" |
||||
android:layout_marginLeft="8dp" |
||||
android:contentDescription="@string/book_search_last" |
||||
android:paddingStart="2dp" |
||||
android:paddingEnd="2dp" |
||||
android:src="@drawable/ic_book_last" |
||||
app:layout_constraintBottom_toBottomOf="@+id/tv_last" |
||||
app:layout_constraintLeft_toRightOf="@+id/iv_cover" |
||||
app:layout_constraintTop_toTopOf="@+id/tv_last" |
||||
app:tint="@color/tv_text_secondary" |
||||
tools:ignore="RtlHardcoded,RtlSymmetry" /> |
||||
|
||||
<TextView |
||||
android:id="@+id/tv_last" |
||||
android:layout_width="0dp" |
||||
android:layout_height="wrap_content" |
||||
android:layout_marginBottom="8dp" |
||||
android:singleLine="true" |
||||
android:text="@string/book_search_last" |
||||
android:textColor="@color/tv_text_secondary" |
||||
android:textSize="13sp" |
||||
app:layout_constraintBottom_toBottomOf="parent" |
||||
app:layout_constraintLeft_toRightOf="@+id/iv_last" |
||||
app:layout_constraintRight_toLeftOf="@id/fl_has_new" |
||||
app:layout_constraintTop_toBottomOf="@+id/tv_read" /> |
||||
|
||||
<View |
||||
android:id="@+id/vw_select" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="0dp" |
||||
android:visibility="gone" |
||||
app:layout_constraintTop_toTopOf="parent" |
||||
app:layout_constraintBottom_toBottomOf="parent" /> |
||||
|
||||
<View |
||||
android:layout_width="match_parent" |
||||
android:layout_height="0.5dp" |
||||
android:background="@color/btn_bg_press" |
||||
app:layout_constraintBottom_toBottomOf="parent" /> |
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout> |
Loading…
Reference in new issue