From 7386d2d76a7964b8c8e779651d58f2ed6c4c522f Mon Sep 17 00:00:00 2001 From: NekoInverter <42698724+NekoInverter@users.noreply.github.com> Date: Sat, 21 Nov 2020 17:48:59 +0800 Subject: [PATCH] Update CheckTextView --- app/build.gradle | 1 - .../hippo/ehviewer/widget/CategoryTable.java | 22 +- .../java/com/hippo/widget/CheckTextView.java | 323 ++++++++++-------- .../drawable/check_text_view_foreground.xml | 13 + app/src/main/res/values-night/colors.xml | 2 +- app/src/main/res/values/attrs.xml | 4 +- app/src/main/res/values/colors.xml | 2 +- app/src/main/res/values/styles.xml | 2 +- 8 files changed, 205 insertions(+), 164 deletions(-) create mode 100644 app/src/main/res/drawable/check_text_view_foreground.xml diff --git a/app/build.gradle b/app/build.gradle index e28121832..d2c8c1673 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -93,7 +93,6 @@ dependencies { implementation 'com.github.seven332:glgallery:0.1.2' implementation 'com.github.seven332:glview:0.1.0' implementation 'com.github.seven332:glview-image:0.1.0' - implementation 'com.github.seven332:hotspot:0.1.0' implementation 'com.github.seven332:streampipe:0.1.0' implementation 'com.github.seven332:tuxiang:0.1.6' implementation 'com.github.seven332:unifile:9ec57bcd8f' diff --git a/app/src/main/java/com/hippo/ehviewer/widget/CategoryTable.java b/app/src/main/java/com/hippo/ehviewer/widget/CategoryTable.java index b4a1cfbc9..8fa21ad55 100644 --- a/app/src/main/java/com/hippo/ehviewer/widget/CategoryTable.java +++ b/app/src/main/java/com/hippo/ehviewer/widget/CategoryTable.java @@ -98,7 +98,7 @@ public class CategoryTable extends TableLayout implements View.OnLongClickListen boolean checked = ((CheckTextView) v).isChecked(); for (CheckTextView option : mOptions) { if (option != v) { - option.setChecked(!checked, false); + option.setChecked(!checked); } } } @@ -132,16 +132,16 @@ public class CategoryTable extends TableLayout implements View.OnLongClickListen * @param category target category */ public void setCategory(int category) { - mDoujinshi.setChecked(!NumberUtils.int2boolean(category & EhConfig.DOUJINSHI), false); - mManga.setChecked(!NumberUtils.int2boolean(category & EhConfig.MANGA), false); - mArtistCG.setChecked(!NumberUtils.int2boolean(category & EhConfig.ARTIST_CG), false); - mGameCG.setChecked(!NumberUtils.int2boolean(category & EhConfig.GAME_CG), false); - mWestern.setChecked(!NumberUtils.int2boolean(category & EhConfig.WESTERN), false); - mNonH.setChecked(!NumberUtils.int2boolean(category & EhConfig.NON_H), false); - mImageSets.setChecked(!NumberUtils.int2boolean(category & EhConfig.IMAGE_SET), false); - mCosplay.setChecked(!NumberUtils.int2boolean(category & EhConfig.COSPLAY), false); - mAsianPorn.setChecked(!NumberUtils.int2boolean(category & EhConfig.ASIAN_PORN), false); - mMisc.setChecked(!NumberUtils.int2boolean(category & EhConfig.MISC), false); + mDoujinshi.setChecked(!NumberUtils.int2boolean(category & EhConfig.DOUJINSHI)); + mManga.setChecked(!NumberUtils.int2boolean(category & EhConfig.MANGA)); + mArtistCG.setChecked(!NumberUtils.int2boolean(category & EhConfig.ARTIST_CG)); + mGameCG.setChecked(!NumberUtils.int2boolean(category & EhConfig.GAME_CG)); + mWestern.setChecked(!NumberUtils.int2boolean(category & EhConfig.WESTERN)); + mNonH.setChecked(!NumberUtils.int2boolean(category & EhConfig.NON_H)); + mImageSets.setChecked(!NumberUtils.int2boolean(category & EhConfig.IMAGE_SET)); + mCosplay.setChecked(!NumberUtils.int2boolean(category & EhConfig.COSPLAY)); + mAsianPorn.setChecked(!NumberUtils.int2boolean(category & EhConfig.ASIAN_PORN)); + mMisc.setChecked(!NumberUtils.int2boolean(category & EhConfig.MISC)); } @Override diff --git a/app/src/main/java/com/hippo/widget/CheckTextView.java b/app/src/main/java/com/hippo/widget/CheckTextView.java index 72a807efb..3cfa258df 100644 --- a/app/src/main/java/com/hippo/widget/CheckTextView.java +++ b/app/src/main/java/com/hippo/widget/CheckTextView.java @@ -16,199 +16,226 @@ package com.hippo.widget; -import android.animation.Animator; -import android.animation.ObjectAnimator; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.os.Bundle; -import android.os.Parcelable; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.os.Build; import android.util.AttributeSet; +import android.view.Gravity; import android.view.View; -import android.view.View.OnClickListener; -import android.view.animation.Interpolator; import androidx.annotation.NonNull; -import androidx.appcompat.widget.AppCompatTextView; +import androidx.appcompat.widget.AppCompatCheckedTextView; import com.hippo.ehviewer.R; -import com.hippo.hotspot.Hotspotable; -import com.hippo.yorozuya.AnimationUtils; -import com.hippo.yorozuya.MathUtils; -import com.hippo.yorozuya.SimpleAnimatorListener; -public class CheckTextView extends AppCompatTextView implements OnClickListener, Hotspotable { +public class CheckTextView extends AppCompatCheckedTextView implements View.OnClickListener { - private static final String STATE_KEY_SUPER = "super"; - private static final String STATE_KEY_CHECKED = "checked"; + private Drawable mForeground; - private static final long ANIMATION_DURATION = 200; - Animator mAnimator; - private final Animator.AnimatorListener mAnimatorListener = new SimpleAnimatorListener() { - @Override - public void onAnimationEnd(Animator animation) { - mAnimator = null; - } - }; - private int mMaskColor; - private boolean mChecked = false; - private boolean mPrepareAnimator = false; - private Paint mPaint; - private float mRadius = 0f; - private float mX; - private float mY; - private float mMaxRadius; + private final Rect mSelfBounds = new Rect(); + + private final Rect mOverlayBounds = new Rect(); + + private int mForegroundGravity = Gravity.FILL; + + protected boolean mForegroundInPadding = true; + + boolean mForegroundBoundsChanged = false; + + public CheckTextView(Context context) { + this(context, null); + } public CheckTextView(Context context, AttributeSet attrs) { - super(context, attrs); - init(context, attrs); + this(context, attrs, 0); } - public CheckTextView(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - init(context, attrs); + public CheckTextView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + + init(context, attrs, defStyleAttr); } - private void init(Context context, AttributeSet attrs) { - TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CheckTextView); - mMaskColor = a.getColor(R.styleable.CheckTextView_maskColor, Color.WHITE); + private void init(Context context, AttributeSet attrs, int defStyleAttr) { + TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CheckTextView, + defStyleAttr, 0); + + mForegroundGravity = a.getInt( + R.styleable.CheckTextView_android_foregroundGravity, mForegroundGravity); + + Drawable d = a.getDrawable(R.styleable.CheckTextView_android_foreground); + if (d != null) { + setForeground(d); + } + + mForegroundInPadding = a.getBoolean( + R.styleable.CheckTextView_foregroundInsidePadding, true); + a.recycle(); - - mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG); - mPaint.setColor(mMaskColor); - setOnClickListener(this); } - @Override - public void setHotspot(float x, float y) { - mX = x; - mY = y; - mMaxRadius = MathUtils.coverageRadius(getWidth(), getHeight(), x, y); + /** + * Describes how the foreground is positioned. + * + * @return foreground gravity. + * @see #setForegroundGravity(int) + */ + public int getForegroundGravity() { + return mForegroundGravity; } - @Override - public void drawableHotspotChanged(float x, float y) { - super.drawableHotspotChanged(x, y); - - mX = x; - mY = y; - mMaxRadius = MathUtils.coverageRadius(getWidth(), getHeight(), x, y); - } - - public float getRadius() { - return mRadius; - } - - public void setRadius(float radius) { - float bigger = Math.max(mRadius, radius); - mRadius = radius; - invalidate((int) (mX - bigger), (int) (mY - bigger), (int) (mX + bigger), (int) (mY + bigger)); - } - - public void prepareAnimations() { - mPrepareAnimator = true; - } - - private void createAnimations() { - float startRadius; - float endRadius; - Interpolator interpolator; - if (mChecked) { - startRadius = 0; - endRadius = mMaxRadius; - interpolator = AnimationUtils.FAST_SLOW_INTERPOLATOR; - } else { - startRadius = mMaxRadius; - endRadius = 0; - interpolator = AnimationUtils.SLOW_FAST_INTERPOLATOR; - } - mRadius = startRadius; - - final ObjectAnimator radiusAnim = ObjectAnimator.ofFloat(this, "radius", startRadius, endRadius); - radiusAnim.setDuration(ANIMATION_DURATION); - radiusAnim.setInterpolator(interpolator); - radiusAnim.addListener(mAnimatorListener); - radiusAnim.start(); - mAnimator = radiusAnim; - } - - private void cancelAnimations() { - if (mAnimator != null) { - mAnimator.cancel(); - } - } - - @Override - public void onClick(View v) { - setChecked(!mChecked); - } - - @Override - protected void onDraw(@NonNull Canvas canvas) { - super.onDraw(canvas); - - if (mPrepareAnimator) { - mPrepareAnimator = false; - cancelAnimations(); - createAnimations(); - } - - if (mAnimator != null) { - canvas.drawCircle(mX, mY, mRadius, mPaint); - } else { - if (mChecked) { - canvas.drawColor(mMaskColor); + /** + * Describes how the foreground is positioned. Defaults to START and TOP. + * + * @param foregroundGravity See {@link android.view.Gravity} + * @see #getForegroundGravity() + */ + public void setForegroundGravity(int foregroundGravity) { + if (mForegroundGravity != foregroundGravity) { + if ((foregroundGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) { + foregroundGravity |= Gravity.START; } + + if ((foregroundGravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) { + foregroundGravity |= Gravity.TOP; + } + + mForegroundGravity = foregroundGravity; + + if (mForegroundGravity == Gravity.FILL && mForeground != null) { + Rect padding = new Rect(); + mForeground.getPadding(padding); + } + + requestLayout(); + } + } + + @Override + protected boolean verifyDrawable(@NonNull Drawable who) { + return super.verifyDrawable(who) || (who == mForeground); + } + + @Override + public void jumpDrawablesToCurrentState() { + super.jumpDrawablesToCurrentState(); + if (mForeground != null) { + mForeground.jumpToCurrentState(); + } + } + + @Override + protected void drawableStateChanged() { + super.drawableStateChanged(); + if (mForeground != null && mForeground.isStateful()) { + mForeground.setState(getDrawableState()); } } /** - * Changes the checked state of this CheckTextView. + * Supply a Drawable that is to be rendered on top of all of the child + * views in the frame layout. Any padding in the Drawable will be taken + * into account by ensuring that the children are inset to be placed + * inside of the padding area. * - * @param checked checked true to check the CheckTextView, false to uncheck it - * @param animation true for show animation + * @param drawable The Drawable to be drawn on top of the children. */ - public void setChecked(boolean checked, boolean animation) { - if (mChecked != checked) { - mChecked = checked; - - if (animation) { - prepareAnimations(); + public void setForeground(Drawable drawable) { + if (mForeground != drawable) { + if (mForeground != null) { + mForeground.setCallback(null); + unscheduleDrawable(mForeground); } + + mForeground = drawable; + + if (drawable != null) { + setWillNotDraw(false); + drawable.setCallback(this); + if (drawable.isStateful()) { + drawable.setState(getDrawableState()); + } + if (mForegroundGravity == Gravity.FILL) { + Rect padding = new Rect(); + drawable.getPadding(padding); + } + } else { + setWillNotDraw(true); + } + requestLayout(); invalidate(); } } /** - * Get the checked state of it. + * Returns the drawable used as the foreground of this FrameLayout. The + * foreground drawable, if non-null, is always drawn on top of the children. * - * @return true is it is checked + * @return A Drawable or null if no foreground was set. */ - public boolean isChecked() { - return mChecked; - } - - public void setChecked(boolean checked) { - setChecked(checked, true); + public Drawable getForeground() { + return mForeground; } @Override - public Parcelable onSaveInstanceState() { - final Bundle state = new Bundle(); - state.putParcelable(STATE_KEY_SUPER, super.onSaveInstanceState()); - state.putBoolean(STATE_KEY_CHECKED, mChecked); - return state; + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + mForegroundBoundsChanged |= changed; } @Override - public void onRestoreInstanceState(Parcelable state) { - if (state instanceof Bundle) { - final Bundle savedState = (Bundle) state; - super.onRestoreInstanceState(savedState.getParcelable(STATE_KEY_SUPER)); - setChecked(savedState.getBoolean(STATE_KEY_CHECKED), false); + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + super.onSizeChanged(w, h, oldw, oldh); + mForegroundBoundsChanged = true; + } + + @Override + public void draw(@NonNull Canvas canvas) { + super.draw(canvas); + + if (mForeground != null) { + final Drawable foreground = mForeground; + + if (mForegroundBoundsChanged) { + mForegroundBoundsChanged = false; + final Rect selfBounds = mSelfBounds; + final Rect overlayBounds = mOverlayBounds; + + final int w = getRight() - getLeft(); + final int h = getBottom() - getTop(); + + if (mForegroundInPadding) { + selfBounds.set(0, 0, w, h); + } else { + selfBounds.set(getPaddingLeft(), getPaddingTop(), + w - getPaddingRight(), h - getPaddingBottom()); + } + + Gravity.apply(mForegroundGravity, foreground.getIntrinsicWidth(), + foreground.getIntrinsicHeight(), selfBounds, overlayBounds); + foreground.setBounds(overlayBounds); + } + + foreground.draw(canvas); } } + + @Override + public void drawableHotspotChanged(float x, float y) { + super.drawableHotspotChanged(x, y); + if (mForeground != null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + mForeground.setHotspot(x, y); + } + } + } + + @Override + public void onClick(View v) { + setChecked(!isChecked()); + } } diff --git a/app/src/main/res/drawable/check_text_view_foreground.xml b/app/src/main/res/drawable/check_text_view_foreground.xml new file mode 100644 index 000000000..73df9dfc7 --- /dev/null +++ b/app/src/main/res/drawable/check_text_view_foreground.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values-night/colors.xml b/app/src/main/res/values-night/colors.xml index fb24d9e5a..48c699aa9 100644 --- a/app/src/main/res/values-night/colors.xml +++ b/app/src/main/res/values-night/colors.xml @@ -90,7 +90,7 @@ @color/yellow_800 @color/deep_orange_400 - #8a000000 + #4a000000 #fff44336 diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml index a071a7f71..dd780f2c3 100644 --- a/app/src/main/res/values/attrs.xml +++ b/app/src/main/res/values/attrs.xml @@ -97,7 +97,9 @@ - + + + diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 22c8ce11d..7a6021ed6 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -120,7 +120,7 @@ @color/yellow_800 @color/deep_orange_400 - #8a000000 + #4a000000 #fff44336 diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 28b069355..23879700e 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -71,7 +71,7 @@ bold center @dimen/category_table_item_height - @color/check_text_view_mask + @drawable/check_text_view_foreground