diff --git a/app/src/main/java/io/legado/app/ui/widget/SearchView.java b/app/src/main/java/io/legado/app/ui/widget/SearchView.java deleted file mode 100644 index 3ee0bd69b..000000000 --- a/app/src/main/java/io/legado/app/ui/widget/SearchView.java +++ /dev/null @@ -1,119 +0,0 @@ -package io.legado.app.ui.widget; - -import android.app.SearchableInfo; -import android.content.Context; -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.drawable.Drawable; -import android.text.Spannable; -import android.text.SpannableStringBuilder; -import android.text.style.ImageSpan; -import android.util.AttributeSet; -import android.util.TypedValue; -import android.view.Gravity; -import android.widget.TextView; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import io.legado.app.R; - -public class SearchView extends androidx.appcompat.widget.SearchView { - private Drawable mSearchHintIcon = null; - private TextView textView = null; - - public SearchView(Context context) { - this(context, null); - } - - public SearchView(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public SearchView(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - } - - @Override - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - super.onLayout(changed, left, top, right, bottom); - try { - if (textView == null) { - textView = (TextView) this.findViewById(androidx.appcompat.R.id.search_src_text); - mSearchHintIcon = this.getContext().getDrawable(R.drawable.ic_search_hint); - updateQueryHint(); - } - // 改变字体 - textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14); - textView.setGravity(Gravity.CENTER_VERTICAL); - } catch (Exception e) { - e.printStackTrace(); - } - } - - - private CharSequence getDecoratedHint(CharSequence hintText) { - // If the field is always expanded or we don't have a search hint icon, - // then don't add the search icon to the hint. - if (mSearchHintIcon == null) { - return hintText; - } - - final int textSize = (int) (textView.getTextSize() * 0.8); - mSearchHintIcon.setBounds(0, 0, textSize, textSize); - - final SpannableStringBuilder ssb = new SpannableStringBuilder(" "); - ssb.setSpan(new CenteredImageSpan(mSearchHintIcon), 1, 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); - ssb.append(hintText); - return ssb; - } - - private void updateQueryHint() { - if (textView != null) { - final CharSequence hint = getQueryHint(); - textView.setHint(getDecoratedHint(hint == null ? "" : hint)); - } - } - - @Override - public void setIconifiedByDefault(boolean iconified) { - super.setIconifiedByDefault(iconified); - updateQueryHint(); - } - - @Override - public void setSearchableInfo(SearchableInfo searchable) { - super.setSearchableInfo(searchable); - if (searchable != null) - updateQueryHint(); - } - - @Override - public void setQueryHint(@Nullable CharSequence hint) { - super.setQueryHint(hint); - updateQueryHint(); - } -} - -class CenteredImageSpan extends ImageSpan { - - public CenteredImageSpan(final Drawable drawable) { - super(drawable); - } - - @Override - public void draw(@NonNull Canvas canvas, CharSequence text, - int start, int end, float x, - int top, int y, int bottom, @NonNull Paint paint) { - // image to draw - Drawable b = getDrawable(); - // font metrics of text to be replaced - Paint.FontMetricsInt fm = paint.getFontMetricsInt(); - int transY = (y + fm.descent + y + fm.ascent) / 2 - - b.getBounds().bottom / 2; - - canvas.save(); - canvas.translate(x, transY); - b.draw(canvas); - canvas.restore(); - } -} \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/widget/SearchView.kt b/app/src/main/java/io/legado/app/ui/widget/SearchView.kt new file mode 100644 index 000000000..a1d8bd5f6 --- /dev/null +++ b/app/src/main/java/io/legado/app/ui/widget/SearchView.kt @@ -0,0 +1,113 @@ +package io.legado.app.ui.widget + +import android.app.SearchableInfo +import android.content.Context +import android.graphics.Canvas +import android.graphics.Paint +import android.graphics.drawable.Drawable +import android.text.Spannable +import android.text.SpannableStringBuilder +import android.text.style.ImageSpan +import android.util.AttributeSet +import android.util.TypedValue +import android.view.Gravity +import android.view.View +import android.widget.TextView +import androidx.appcompat.widget.SearchView +import io.legado.app.R + +class SearchView : SearchView { + private var mSearchHintIcon: Drawable? = null + private var textView: TextView? = null + + constructor( + context: Context?, + attrs: AttributeSet? = null + ) : 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) + try { + if (textView == null) { + textView = + findViewById(androidx.appcompat.R.id.search_src_text) as TextView + mSearchHintIcon = this.context.getDrawable(R.drawable.ic_search_hint) + updateQueryHint() + } + // 改变字体 + textView!!.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14f) + textView!!.gravity = Gravity.CENTER_VERTICAL + } catch (e: Exception) { + e.printStackTrace() + } + } + + private fun getDecoratedHint(hintText: CharSequence): CharSequence { + // If the field is always expanded or we don't have a search hint icon, + // then don't add the search icon to the hint. + if (mSearchHintIcon == null) { + return hintText + } + val textSize = (textView!!.textSize * 0.8).toInt() + mSearchHintIcon!!.setBounds(0, 0, textSize, textSize) + val ssb = SpannableStringBuilder(" ") + ssb.setSpan(CenteredImageSpan(mSearchHintIcon), 1, 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) + ssb.append(hintText) + return ssb + } + + private fun updateQueryHint() { + if (textView != null) { + val hint = queryHint + textView!!.hint = getDecoratedHint(hint ?: "") + } + } + + override fun setIconifiedByDefault(iconified: Boolean) { + super.setIconifiedByDefault(iconified) + updateQueryHint() + } + + override fun setSearchableInfo(searchable: SearchableInfo?) { + super.setSearchableInfo(searchable) + searchable?.let { + updateQueryHint() + } + } + + override fun setQueryHint(hint: CharSequence?) { + super.setQueryHint(hint) + updateQueryHint() + } +} + +internal class CenteredImageSpan(drawable: Drawable?) : ImageSpan(drawable!!) { + override fun draw( + canvas: Canvas, text: CharSequence, + start: Int, end: Int, x: Float, + top: Int, y: Int, bottom: Int, paint: Paint + ) { + // image to draw + val b = drawable + // font metrics of text to be replaced + val fm = paint.fontMetricsInt + val transY = ((y + fm.descent + y + fm.ascent) / 2 + - b.bounds.bottom / 2) + canvas.save() + canvas.translate(x, transY.toFloat()) + b.draw(canvas) + canvas.restore() + } +} \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/widget/ShadowLayout.java b/app/src/main/java/io/legado/app/ui/widget/ShadowLayout.java deleted file mode 100644 index 4ef6ffd2e..000000000 --- a/app/src/main/java/io/legado/app/ui/widget/ShadowLayout.java +++ /dev/null @@ -1,195 +0,0 @@ -package io.legado.app.ui.widget; - -import android.content.Context; -import android.content.res.TypedArray; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.RectF; -import android.util.AttributeSet; -import android.util.DisplayMetrics; -import android.view.View; -import android.widget.RelativeLayout; -import io.legado.app.R; - -/** - * ShadowLayout.java - *

- * Created by lijiankun on 17/8/11. - */ - -public class ShadowLayout extends RelativeLayout { - - public static final int ALL = 0x1111; - - public static final int LEFT = 0x0001; - - public static final int TOP = 0x0010; - - public static final int RIGHT = 0x0100; - - public static final int BOTTOM = 0x1000; - - public static final int SHAPE_RECTANGLE = 0x0001; - - public static final int SHAPE_OVAL = 0x0010; - - private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - - private RectF mRectF = new RectF(); - - /** - * 阴影的颜色 - */ - private int mShadowColor = Color.TRANSPARENT; - - /** - * 阴影的大小范围 - */ - private float mShadowRadius = 0; - - /** - * 阴影 x 轴的偏移量 - */ - private float mShadowDx = 0; - - /** - * 阴影 y 轴的偏移量 - */ - private float mShadowDy = 0; - - /** - * 阴影显示的边界 - */ - private int mShadowSide = ALL; - - /** - * 阴影的形状,圆形/矩形 - */ - private int mShadowShape = SHAPE_RECTANGLE; - - public ShadowLayout(Context context) { - this(context, null); - } - - public ShadowLayout(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public ShadowLayout(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - init(attrs); - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - float effect = mShadowRadius + dip2px(5); - float rectLeft = 0; - float rectTop = 0; - float rectRight = this.getMeasuredWidth(); - float rectBottom = this.getMeasuredHeight(); - int paddingLeft = 0; - int paddingTop = 0; - int paddingRight = 0; - int paddingBottom = 0; - this.getWidth(); - if ((mShadowSide & LEFT) == LEFT) { - rectLeft = effect; - paddingLeft = (int) effect; - } - if ((mShadowSide & TOP) == TOP) { - rectTop = effect; - paddingTop = (int) effect; - } - if ((mShadowSide & RIGHT) == RIGHT) { - rectRight = this.getMeasuredWidth() - effect; - paddingRight = (int) effect; - } - if ((mShadowSide & BOTTOM) == BOTTOM) { - rectBottom = this.getMeasuredHeight() - effect; - paddingBottom = (int) effect; - } - if (mShadowDy != 0.0f) { - rectBottom = rectBottom - mShadowDy; - paddingBottom = paddingBottom + (int) mShadowDy; - } - if (mShadowDx != 0.0f) { - rectRight = rectRight - mShadowDx; - paddingRight = paddingRight + (int) mShadowDx; - } - mRectF.left = rectLeft; - mRectF.top = rectTop; - mRectF.right = rectRight; - mRectF.bottom = rectBottom; - this.setPadding(paddingLeft, paddingTop, paddingRight, paddingBottom); - } - - /** - * 真正绘制阴影的方法 - */ - @Override - protected void onDraw(Canvas canvas) { - super.onDraw(canvas); - setUpShadowPaint(); - if (mShadowShape == SHAPE_RECTANGLE) { - canvas.drawRect(mRectF, mPaint); - } else if (mShadowShape == SHAPE_OVAL) { - canvas.drawCircle(mRectF.centerX(), mRectF.centerY(), Math.min(mRectF.width(), mRectF.height()) / 2, mPaint); - } - } - - public void setShadowColor(int shadowColor) { - mShadowColor = shadowColor; - requestLayout(); - postInvalidate(); - } - - public void setShadowRadius(float shadowRadius) { - mShadowRadius = shadowRadius; - requestLayout(); - postInvalidate(); - } - - /** - * 读取设置的阴影的属性 - * - * @param attrs 从其中获取设置的值 - */ - private void init(AttributeSet attrs) { - setLayerType(View.LAYER_TYPE_SOFTWARE, null); // 关闭硬件加速 - this.setWillNotDraw(false); // 调用此方法后,才会执行 onDraw(Canvas) 方法 - - TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.ShadowLayout); - if (typedArray != null) { - mShadowColor = typedArray.getColor(R.styleable.ShadowLayout_shadowColor, - getContext().getResources().getColor(android.R.color.black)); - mShadowRadius = typedArray.getDimension(R.styleable.ShadowLayout_shadowRadius, dip2px(0)); - mShadowDx = typedArray.getDimension(R.styleable.ShadowLayout_shadowDx, dip2px(0)); - mShadowDy = typedArray.getDimension(R.styleable.ShadowLayout_shadowDy, dip2px(0)); - mShadowSide = typedArray.getInt(R.styleable.ShadowLayout_shadowSide, ALL); - mShadowShape = typedArray.getInt(R.styleable.ShadowLayout_shadowShape, SHAPE_RECTANGLE); - typedArray.recycle(); - } - setUpShadowPaint(); - } - - private void setUpShadowPaint() { - mPaint.reset(); - mPaint.setAntiAlias(true); - mPaint.setColor(Color.TRANSPARENT); - mPaint.setShadowLayer(mShadowRadius, mShadowDx, mShadowDy, mShadowColor); - } - - /** - * dip2px dp 值转 px 值 - * - * @param dpValue dp 值 - * @return px 值 - */ - private float dip2px(float dpValue) { - DisplayMetrics dm = getContext().getResources().getDisplayMetrics(); - float scale = dm.density; - return (dpValue * scale + 0.5F); - } -} diff --git a/app/src/main/java/io/legado/app/ui/widget/ShadowLayout.kt b/app/src/main/java/io/legado/app/ui/widget/ShadowLayout.kt new file mode 100644 index 000000000..0f6ad3200 --- /dev/null +++ b/app/src/main/java/io/legado/app/ui/widget/ShadowLayout.kt @@ -0,0 +1,185 @@ +package io.legado.app.ui.widget + +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.view.View +import android.widget.RelativeLayout +import io.legado.app.R +import io.legado.app.utils.getCompatColor + +/** + * ShadowLayout.java + * + * + * Created by lijiankun on 17/8/11. + */ +class ShadowLayout( + context: Context, + attrs: AttributeSet? = null +) : RelativeLayout(context, attrs) { + private val mPaint = + Paint(Paint.ANTI_ALIAS_FLAG) + private val mRectF = RectF() + + /** + * 阴影的颜色 + */ + private var mShadowColor = Color.TRANSPARENT + + /** + * 阴影的大小范围 + */ + private var mShadowRadius = 0f + + /** + * 阴影 x 轴的偏移量 + */ + private var mShadowDx = 0f + + /** + * 阴影 y 轴的偏移量 + */ + private var mShadowDy = 0f + + /** + * 阴影显示的边界 + */ + private var mShadowSide = ALL + + /** + * 阴影的形状,圆形/矩形 + */ + private var mShadowShape = SHAPE_RECTANGLE + + + init { + setLayerType(View.LAYER_TYPE_SOFTWARE, null) // 关闭硬件加速 + setWillNotDraw(false) // 调用此方法后,才会执行 onDraw(Canvas) 方法 + val typedArray = + context.obtainStyledAttributes(attrs, R.styleable.ShadowLayout) + mShadowColor = typedArray.getColor( + R.styleable.ShadowLayout_shadowColor, + context.getCompatColor(android.R.color.black) + ) + mShadowRadius = + typedArray.getDimension(R.styleable.ShadowLayout_shadowRadius, dip2px(0f)) + mShadowDx = typedArray.getDimension(R.styleable.ShadowLayout_shadowDx, dip2px(0f)) + mShadowDy = typedArray.getDimension(R.styleable.ShadowLayout_shadowDy, dip2px(0f)) + mShadowSide = + typedArray.getInt(R.styleable.ShadowLayout_shadowSide, ALL) + mShadowShape = typedArray.getInt( + R.styleable.ShadowLayout_shadowShape, + SHAPE_RECTANGLE + ) + typedArray.recycle() + + setUpShadowPaint() + } + + override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec) + val effect = mShadowRadius + dip2px(5f) + var rectLeft = 0f + var rectTop = 0f + var rectRight = this.measuredWidth.toFloat() + var rectBottom = this.measuredHeight.toFloat() + var paddingLeft = 0 + var paddingTop = 0 + var paddingRight = 0 + var paddingBottom = 0 + this.width + if (mShadowSide and LEFT == LEFT) { + rectLeft = effect + paddingLeft = effect.toInt() + } + if (mShadowSide and TOP == TOP) { + rectTop = effect + paddingTop = effect.toInt() + } + if (mShadowSide and RIGHT == RIGHT) { + rectRight = this.measuredWidth - effect + paddingRight = effect.toInt() + } + if (mShadowSide and BOTTOM == BOTTOM) { + rectBottom = this.measuredHeight - effect + paddingBottom = effect.toInt() + } + if (mShadowDy != 0.0f) { + rectBottom -= mShadowDy + paddingBottom += mShadowDy.toInt() + } + if (mShadowDx != 0.0f) { + rectRight -= mShadowDx + paddingRight += mShadowDx.toInt() + } + mRectF.left = rectLeft + mRectF.top = rectTop + mRectF.right = rectRight + mRectF.bottom = rectBottom + setPadding(paddingLeft, paddingTop, paddingRight, paddingBottom) + } + + /** + * 真正绘制阴影的方法 + */ + override fun onDraw(canvas: Canvas) { + super.onDraw(canvas) + setUpShadowPaint() + if (mShadowShape == SHAPE_RECTANGLE) { + canvas.drawRect(mRectF, mPaint) + } else if (mShadowShape == SHAPE_OVAL) { + canvas.drawCircle( + mRectF.centerX(), + mRectF.centerY(), + mRectF.width().coerceAtMost(mRectF.height()) / 2, + mPaint + ) + } + } + + fun setShadowColor(shadowColor: Int) { + mShadowColor = shadowColor + requestLayout() + postInvalidate() + } + + fun setShadowRadius(shadowRadius: Float) { + mShadowRadius = shadowRadius + requestLayout() + postInvalidate() + } + + private fun setUpShadowPaint() { + mPaint.reset() + mPaint.isAntiAlias = true + mPaint.color = Color.TRANSPARENT + mPaint.setShadowLayer(mShadowRadius, mShadowDx, mShadowDy, mShadowColor) + } + + /** + * dip2px dp 值转 px 值 + * + * @param dpValue dp 值 + * @return px 值 + */ + private fun dip2px(dpValue: Float): Float { + val dm = context.resources.displayMetrics + val scale = dm.density + return dpValue * scale + 0.5f + } + + companion object { + const val ALL = 0x1111 + const val LEFT = 0x0001 + const val TOP = 0x0010 + const val RIGHT = 0x0100 + const val BOTTOM = 0x1000 + const val SHAPE_RECTANGLE = 0x0001 + const val SHAPE_OVAL = 0x0010 + } + +} \ No newline at end of file