From 233ab6d229c418bc35b6220fdd2cc17fb140f87e Mon Sep 17 00:00:00 2001 From: fengyuecanzhu <1021300691@qq.com> Date: Fri, 4 Jun 2021 12:50:23 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B9=A6=E7=B1=8D=E9=95=BF=E6=8C=89=E8=8F=9C?= =?UTF-8?q?=E5=8D=95=E9=87=8D=E5=81=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../myreader/ui/adapter/BookcaseAdapter.java | 9 +- .../ui/presenter/BookcasePresenter.java | 2 +- .../fycz/myreader/util/help/JsExtensions.java | 1 - .../fycz/myreader/widget/SwitchButton.java | 1163 +++++++++++++++++ app/src/main/res/drawable/ic_top.xml | 10 +- app/src/main/res/layout/menu_book.xml | 15 +- app/src/main/res/layout/menu_book_local.xml | 6 +- app/src/main/res/values/styles.xml | 28 + 8 files changed, 1210 insertions(+), 24 deletions(-) create mode 100644 app/src/main/java/xyz/fycz/myreader/widget/SwitchButton.java diff --git a/app/src/main/java/xyz/fycz/myreader/ui/adapter/BookcaseAdapter.java b/app/src/main/java/xyz/fycz/myreader/ui/adapter/BookcaseAdapter.java index 9bd38ce..37e09ad 100644 --- a/app/src/main/java/xyz/fycz/myreader/ui/adapter/BookcaseAdapter.java +++ b/app/src/main/java/xyz/fycz/myreader/ui/adapter/BookcaseAdapter.java @@ -52,6 +52,7 @@ import xyz.fycz.myreader.util.utils.ShareBookUtil; import xyz.fycz.myreader.webapi.crawler.ReadCrawlerUtil; import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler; import xyz.fycz.myreader.widget.CoverImageView; +import xyz.fycz.myreader.widget.SwitchButton; import xyz.fycz.myreader.widget.custom.DragAdapter; import xyz.fycz.myreader.greendao.entity.Book; import xyz.fycz.myreader.greendao.entity.Chapter; @@ -397,7 +398,7 @@ public abstract class BookcaseAdapter extends RecyclerView.Adapter { + sbIsUpdate.setChecked(!mBook.getIsCloseUpdate()); + sbIsUpdate.setOnCheckedChangeListener((buttonView, isChecked) -> { mBook.setIsCloseUpdate(!mBook.getIsCloseUpdate()); mBookService.updateEntity(mBook); }); diff --git a/app/src/main/java/xyz/fycz/myreader/ui/presenter/BookcasePresenter.java b/app/src/main/java/xyz/fycz/myreader/ui/presenter/BookcasePresenter.java index 4b7eedb..d01db4c 100644 --- a/app/src/main/java/xyz/fycz/myreader/ui/presenter/BookcasePresenter.java +++ b/app/src/main/java/xyz/fycz/myreader/ui/presenter/BookcasePresenter.java @@ -696,7 +696,7 @@ public class BookcasePresenter implements BasePresenter { @Override public void addGroup() { - mBookcaseFragment.getmBtnAddGroup().performClick(); + BookcasePresenter.this.addGroup(book); } }); } diff --git a/app/src/main/java/xyz/fycz/myreader/util/help/JsExtensions.java b/app/src/main/java/xyz/fycz/myreader/util/help/JsExtensions.java index e1c2fdb..f7dea3e 100644 --- a/app/src/main/java/xyz/fycz/myreader/util/help/JsExtensions.java +++ b/app/src/main/java/xyz/fycz/myreader/util/help/JsExtensions.java @@ -33,7 +33,6 @@ import xyz.fycz.myreader.util.utils.OkHttpUtils; import xyz.fycz.myreader.util.utils.StringUtils; @Keep - @SuppressWarnings({"unused"}) public interface JsExtensions { String TAG = JsExtensions.class.getSimpleName(); diff --git a/app/src/main/java/xyz/fycz/myreader/widget/SwitchButton.java b/app/src/main/java/xyz/fycz/myreader/widget/SwitchButton.java new file mode 100644 index 0000000..61ff776 --- /dev/null +++ b/app/src/main/java/xyz/fycz/myreader/widget/SwitchButton.java @@ -0,0 +1,1163 @@ +package xyz.fycz.myreader.widget; + +import android.animation.Animator; +import android.animation.ValueAnimator; +import android.annotation.TargetApi; +import android.content.Context; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.RectF; +import android.os.Build; +import android.util.AttributeSet; +import android.util.TypedValue; +import android.view.MotionEvent; +import android.view.View; +import android.widget.Checkable; + +import xyz.fycz.myreader.R; + + +/** + * @author fengyue + * @date 2021/6/4 12:31 + * SwitchButton. + */ +public class SwitchButton extends View implements Checkable { + private static final int DEFAULT_WIDTH = dp2pxInt(58); + private static final int DEFAULT_HEIGHT = dp2pxInt(36); + + /** + * 动画状态: + * 1.静止 + * 2.进入拖动 + * 3.处于拖动 + * 4.拖动-复位 + * 5.拖动-切换 + * 6.点击切换 + * **/ + private final int ANIMATE_STATE_NONE = 0; + private final int ANIMATE_STATE_PENDING_DRAG = 1; + private final int ANIMATE_STATE_DRAGING = 2; + private final int ANIMATE_STATE_PENDING_RESET = 3; + private final int ANIMATE_STATE_PENDING_SETTLE = 4; + private final int ANIMATE_STATE_SWITCH = 5; + + public SwitchButton(Context context) { + super(context); + init(context, null); + } + + public SwitchButton(Context context, AttributeSet attrs) { + super(context, attrs); + init(context, attrs); + } + + public SwitchButton(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(context, attrs); + } + + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + public SwitchButton(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + init(context, attrs); + } + + @Override + public final void setPadding(int left, int top, int right, int bottom) { + super.setPadding(0, 0, 0, 0); + } + + /** + * 初始化参数 + */ + private void init(Context context, AttributeSet attrs) { + + TypedArray typedArray = null; + if(attrs != null){ + typedArray = context.obtainStyledAttributes(attrs, R.styleable.SwitchButton); + } + + shadowEffect = optBoolean(typedArray, + R.styleable.SwitchButton_sb_shadow_effect, + true); + + uncheckCircleColor = optColor(typedArray, + R.styleable.SwitchButton_sb_uncheckcircle_color, + 0XffAAAAAA);//0XffAAAAAA; + + uncheckCircleWidth = optPixelSize(typedArray, + R.styleable.SwitchButton_sb_uncheckcircle_width, + dp2pxInt(1.5f));//dp2pxInt(1.5f); + + uncheckCircleOffsetX = dp2px(10); + + uncheckCircleRadius = optPixelSize(typedArray, + R.styleable.SwitchButton_sb_uncheckcircle_radius, + dp2px(4));//dp2px(4); + + checkedLineOffsetX = dp2px(4); + checkedLineOffsetY = dp2px(4); + + shadowRadius = optPixelSize(typedArray, + R.styleable.SwitchButton_sb_shadow_radius, + dp2pxInt(2.5f));//dp2pxInt(2.5f); + + shadowOffset = optPixelSize(typedArray, + R.styleable.SwitchButton_sb_shadow_offset, + dp2pxInt(1.5f));//dp2pxInt(1.5f); + + shadowColor = optColor(typedArray, + R.styleable.SwitchButton_sb_shadow_color, + 0X33000000);//0X33000000; + + uncheckColor = optColor(typedArray, + R.styleable.SwitchButton_sb_uncheck_color, + 0XffDDDDDD);//0XffDDDDDD; + + checkedColor = optColor(typedArray, + R.styleable.SwitchButton_sb_checked_color, + 0Xff51d367);//0Xff51d367; + + borderWidth = optPixelSize(typedArray, + R.styleable.SwitchButton_sb_border_width, + dp2pxInt(1));//dp2pxInt(1); + + checkLineColor = optColor(typedArray, + R.styleable.SwitchButton_sb_checkline_color, + Color.WHITE);//Color.WHITE; + + checkLineWidth = optPixelSize(typedArray, + R.styleable.SwitchButton_sb_checkline_width, + dp2pxInt(1f));//dp2pxInt(1.0f); + + checkLineLength = dp2px(6); + + int buttonColor = optColor(typedArray, + R.styleable.SwitchButton_sb_button_color, + Color.WHITE);//Color.WHITE; + + uncheckButtonColor = optColor(typedArray, + R.styleable.SwitchButton_sb_uncheckbutton_color, + buttonColor); + + checkedButtonColor = optColor(typedArray, + R.styleable.SwitchButton_sb_checkedbutton_color, + buttonColor); + + int effectDuration = optInt(typedArray, + R.styleable.SwitchButton_sb_effect_duration, + 300);//300; + + isChecked = optBoolean(typedArray, + R.styleable.SwitchButton_sb_checked, + false); + + showIndicator = optBoolean(typedArray, + R.styleable.SwitchButton_sb_show_indicator, + true); + + background = optColor(typedArray, + R.styleable.SwitchButton_sb_background, + Color.WHITE);//Color.WHITE; + + enableEffect = optBoolean(typedArray, + R.styleable.SwitchButton_sb_enable_effect, + true); + + if(typedArray != null){ + typedArray.recycle(); + } + + + paint = new Paint(Paint.ANTI_ALIAS_FLAG); + buttonPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + buttonPaint.setColor(buttonColor); + + if(shadowEffect){ + buttonPaint.setShadowLayer( + shadowRadius, + 0, shadowOffset, + shadowColor); + } + + + viewState = new ViewState(); + beforeState = new ViewState(); + afterState = new ViewState(); + + valueAnimator = ValueAnimator.ofFloat(0f, 1f); + valueAnimator.setDuration(effectDuration); + valueAnimator.setRepeatCount(0); + + valueAnimator.addUpdateListener(animatorUpdateListener); + valueAnimator.addListener(animatorListener); + + super.setClickable(true); + this.setPadding(0, 0, 0, 0); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { + setLayerType(LAYER_TYPE_SOFTWARE, null); + } + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + final int widthMode = MeasureSpec.getMode(widthMeasureSpec); + final int heightMode = MeasureSpec.getMode(heightMeasureSpec); + + if(widthMode == MeasureSpec.UNSPECIFIED + || widthMode == MeasureSpec.AT_MOST){ + widthMeasureSpec = MeasureSpec.makeMeasureSpec(DEFAULT_WIDTH, MeasureSpec.EXACTLY); + } + if(heightMode == MeasureSpec.UNSPECIFIED + || heightMode == MeasureSpec.AT_MOST){ + heightMeasureSpec = MeasureSpec.makeMeasureSpec(DEFAULT_HEIGHT, MeasureSpec.EXACTLY); + } + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + super.onSizeChanged(w, h, oldw, oldh); + + + float viewPadding = Math.max(shadowRadius + shadowOffset, borderWidth); + + height = h - viewPadding - viewPadding; + width = w - viewPadding - viewPadding; + + viewRadius = height * .5f; + buttonRadius = viewRadius - borderWidth; + + left = viewPadding; + top = viewPadding; + right = w - viewPadding; + bottom = h - viewPadding; + + centerX = (left + right) * .5f; + centerY = (top + bottom) * .5f; + + buttonMinX = left + viewRadius; + buttonMaxX = right - viewRadius; + + if(isChecked()){ + setCheckedViewState(viewState); + }else{ + setUncheckViewState(viewState); + } + + isUiInited = true; + + postInvalidate(); + + } + + /** + * @param viewState + */ + private void setUncheckViewState(ViewState viewState){ + viewState.radius = 0; + viewState.checkStateColor = uncheckColor; + viewState.checkedLineColor = Color.TRANSPARENT; + viewState.buttonX = buttonMinX; + buttonPaint.setColor(uncheckButtonColor); + } + + /** + * @param viewState + */ + private void setCheckedViewState(ViewState viewState){ + viewState.radius = viewRadius; + viewState.checkStateColor = checkedColor; + viewState.checkedLineColor = checkLineColor; + viewState.buttonX = buttonMaxX; + buttonPaint.setColor(checkedButtonColor); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + paint.setStrokeWidth(borderWidth); + paint.setStyle(Paint.Style.FILL); + //绘制白色背景 + paint.setColor(background); + drawRoundRect(canvas, + left, top, right, bottom, + viewRadius, paint); + //绘制关闭状态的边框 + paint.setStyle(Paint.Style.STROKE); + paint.setColor(uncheckColor); + drawRoundRect(canvas, + left, top, right, bottom, + viewRadius, paint); + + //绘制小圆圈 + if(showIndicator){ + drawUncheckIndicator(canvas); + } + + //绘制开启背景色 + float des = viewState.radius * .5f;//[0-backgroundRadius*0.5f] + paint.setStyle(Paint.Style.STROKE); + paint.setColor(viewState.checkStateColor); + paint.setStrokeWidth(borderWidth + des * 2f); + drawRoundRect(canvas, + left + des, top + des, right - des, bottom - des, + viewRadius, paint); + + //绘制按钮左边绿色长条遮挡 + paint.setStyle(Paint.Style.FILL); + paint.setStrokeWidth(1); + drawArc(canvas, + left, top, + left + 2 * viewRadius, top + 2 * viewRadius, + 90, 180, paint); + canvas.drawRect( + left + viewRadius, top, + viewState.buttonX, top + 2 * viewRadius, + paint); + + //绘制小线条 + if(showIndicator){ + drawCheckedIndicator(canvas); + } + + //绘制按钮 + drawButton(canvas, viewState.buttonX, centerY); + } + + + /** + * 绘制选中状态指示器 + * @param canvas + */ + protected void drawCheckedIndicator(Canvas canvas) { + drawCheckedIndicator(canvas, + viewState.checkedLineColor, + checkLineWidth, + left + viewRadius - checkedLineOffsetX, centerY - checkLineLength, + left + viewRadius - checkedLineOffsetY, centerY + checkLineLength, + paint); + } + + + /** + * 绘制选中状态指示器 + * @param canvas + * @param color + * @param lineWidth + * @param sx + * @param sy + * @param ex + * @param ey + * @param paint + */ + protected void drawCheckedIndicator(Canvas canvas, + int color, + float lineWidth, + float sx, float sy, float ex, float ey, + Paint paint) { + paint.setStyle(Paint.Style.STROKE); + paint.setColor(color); + paint.setStrokeWidth(lineWidth); + canvas.drawLine( + sx, sy, ex, ey, + paint); + } + + /** + * 绘制关闭状态指示器 + * @param canvas + */ + private void drawUncheckIndicator(Canvas canvas) { + drawUncheckIndicator(canvas, + uncheckCircleColor, + uncheckCircleWidth, + right - uncheckCircleOffsetX, centerY, + uncheckCircleRadius, + paint); + } + + + /** + * 绘制关闭状态指示器 + * @param canvas + * @param color + * @param lineWidth + * @param centerX + * @param centerY + * @param radius + * @param paint + */ + protected void drawUncheckIndicator(Canvas canvas, + int color, + float lineWidth, + float centerX, float centerY, + float radius, + Paint paint) { + paint.setStyle(Paint.Style.STROKE); + paint.setColor(color); + paint.setStrokeWidth(lineWidth); + canvas.drawCircle(centerX, centerY, radius, paint); + } + + /** + * @param canvas + * @param left + * @param top + * @param right + * @param bottom + * @param startAngle + * @param sweepAngle + * @param paint + */ + private void drawArc(Canvas canvas, + float left, float top, + float right, float bottom, + float startAngle, float sweepAngle, + Paint paint){ + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + canvas.drawArc(left, top, right, bottom, + startAngle, sweepAngle, true, paint); + }else{ + rect.set(left, top, right, bottom); + canvas.drawArc(rect, + startAngle, sweepAngle, true, paint); + } + } + + /** + * @param canvas + * @param left + * @param top + * @param right + * @param bottom + * @param backgroundRadius + * @param paint + */ + private void drawRoundRect(Canvas canvas, + float left, float top, + float right, float bottom, + float backgroundRadius, + Paint paint){ + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + canvas.drawRoundRect(left, top, right, bottom, + backgroundRadius, backgroundRadius, paint); + }else{ + rect.set(left, top, right, bottom); + canvas.drawRoundRect(rect, + backgroundRadius, backgroundRadius, paint); + } + } + + + /** + * @param canvas + * @param x px + * @param y px + */ + private void drawButton(Canvas canvas, float x, float y) { + canvas.drawCircle(x, y, buttonRadius, buttonPaint); + + paint.setStyle(Paint.Style.STROKE); + paint.setStrokeWidth(1); + paint.setColor(0XffDDDDDD); + canvas.drawCircle(x, y, buttonRadius, paint); + } + + @Override + public void setChecked(boolean checked) { + if(checked == isChecked()){ + postInvalidate(); + return; + } + toggle(enableEffect, false); + } + + @Override + public boolean isChecked() { + return isChecked; + } + + @Override + public void toggle() { + toggle(true); + } + + /** + * 切换状态 + * @param animate + */ + public void toggle(boolean animate) { + toggle(animate, true); + } + + private void toggle(boolean animate, boolean broadcast) { + if(!isEnabled()){return;} + + if(isEventBroadcast){ + throw new RuntimeException("should NOT switch the state in method: [onCheckedChanged]!"); + } + if(!isUiInited){ + isChecked = !isChecked; + if(broadcast){ + broadcastEvent(); + } + return; + } + + if(valueAnimator.isRunning()){ + valueAnimator.cancel(); + } + + if(!enableEffect || !animate){ + isChecked = !isChecked; + if(isChecked()){ + setCheckedViewState(viewState); + }else{ + setUncheckViewState(viewState); + } + postInvalidate(); + if(broadcast){ + broadcastEvent(); + } + return; + } + + animateState = ANIMATE_STATE_SWITCH; + beforeState.copy(viewState); + + if(isChecked()){ + //切换到unchecked + setUncheckViewState(afterState); + }else{ + setCheckedViewState(afterState); + } + valueAnimator.start(); + } + + /** + * + */ + private void broadcastEvent() { + if(onCheckedChangeListener != null){ + isEventBroadcast = true; + onCheckedChangeListener.onCheckedChanged(this, isChecked()); + } + isEventBroadcast = false; + } + + + @Override + public boolean onTouchEvent(MotionEvent event) { + if(!isEnabled()){return false;} + int actionMasked = event.getActionMasked(); + + switch (actionMasked){ + case MotionEvent.ACTION_DOWN:{ + isTouchingDown = true; + touchDownTime = System.currentTimeMillis(); + //取消准备进入拖动状态 + removeCallbacks(postPendingDrag); + //预设100ms进入拖动状态 + postDelayed(postPendingDrag, 100); + break; + } + case MotionEvent.ACTION_MOVE:{ + float eventX = event.getX(); + if(isPendingDragState()){ + //在准备进入拖动状态过程中,可以拖动按钮位置 + float fraction = eventX / getWidth(); + fraction = Math.max(0f, Math.min(1f, fraction)); + + viewState.buttonX = buttonMinX + + (buttonMaxX - buttonMinX) + * fraction; + + }else if(isDragState()){ + //拖动按钮位置,同时改变对应的背景颜色 + float fraction = eventX / getWidth(); + fraction = Math.max(0f, Math.min(1f, fraction)); + + viewState.buttonX = buttonMinX + + (buttonMaxX - buttonMinX) + * fraction; + + viewState.checkStateColor = (int) argbEvaluator.evaluate( + fraction, + uncheckColor, + checkedColor + ); + postInvalidate(); + + } + break; + } + case MotionEvent.ACTION_UP:{ + isTouchingDown = false; + //取消准备进入拖动状态 + removeCallbacks(postPendingDrag); + + if(System.currentTimeMillis() - touchDownTime <= 300){ + //点击时间小于300ms,认为是点击操作 + toggle(); + }else if(isDragState()){ + //在拖动状态,计算按钮位置,设置是否切换状态 + float eventX = event.getX(); + float fraction = eventX / getWidth(); + fraction = Math.max(0f, Math.min(1f, fraction)); + boolean newCheck = fraction > .5f; + if(newCheck == isChecked()){ + pendingCancelDragState(); + }else{ + isChecked = newCheck; + pendingSettleState(); + } + }else if(isPendingDragState()){ + //在准备进入拖动状态过程中,取消之,复位 + pendingCancelDragState(); + } + break; + } + case MotionEvent.ACTION_CANCEL:{ + isTouchingDown = false; + + removeCallbacks(postPendingDrag); + + if(isPendingDragState() + || isDragState()){ + //复位 + pendingCancelDragState(); + } + break; + } + } + return true; + } + + + /** + * 是否在动画状态 + * @return + */ + private boolean isInAnimating(){ + return animateState != ANIMATE_STATE_NONE; + } + + /** + * 是否在进入拖动或离开拖动状态 + * @return + */ + private boolean isPendingDragState(){ + return animateState == ANIMATE_STATE_PENDING_DRAG + || animateState == ANIMATE_STATE_PENDING_RESET; + } + + /** + * 是否在手指拖动状态 + * @return + */ + private boolean isDragState(){ + return animateState == ANIMATE_STATE_DRAGING; + } + + /** + * 设置是否启用阴影效果 + * @param shadowEffect true.启用 + */ + public void setShadowEffect(boolean shadowEffect) { + if(this.shadowEffect == shadowEffect){return;} + this.shadowEffect = shadowEffect; + + if(this.shadowEffect){ + buttonPaint.setShadowLayer( + shadowRadius, + 0, shadowOffset, + shadowColor); + }else{ + buttonPaint.setShadowLayer( + 0, + 0, 0, + 0); + } + } + + public void setEnableEffect(boolean enable){ + this.enableEffect = enable; + } + + /** + * 开始进入拖动状态 + */ + private void pendingDragState() { + if(isInAnimating()){return;} + if(!isTouchingDown){return;} + + if(valueAnimator.isRunning()){ + valueAnimator.cancel(); + } + + animateState = ANIMATE_STATE_PENDING_DRAG; + + beforeState.copy(viewState); + afterState.copy(viewState); + + if(isChecked()){ + afterState.checkStateColor = checkedColor; + afterState.buttonX = buttonMaxX; + afterState.checkedLineColor = checkedColor; + }else{ + afterState.checkStateColor = uncheckColor; + afterState.buttonX = buttonMinX; + afterState.radius = viewRadius; + } + + valueAnimator.start(); + } + + + /** + * 取消拖动状态 + */ + private void pendingCancelDragState() { + if(isDragState() || isPendingDragState()){ + if(valueAnimator.isRunning()){ + valueAnimator.cancel(); + } + + animateState = ANIMATE_STATE_PENDING_RESET; + beforeState.copy(viewState); + + if(isChecked()){ + setCheckedViewState(afterState); + }else{ + setUncheckViewState(afterState); + } + valueAnimator.start(); + } + } + + + /** + * 动画-设置新的状态 + */ + private void pendingSettleState() { + if(valueAnimator.isRunning()){ + valueAnimator.cancel(); + } + + animateState = ANIMATE_STATE_PENDING_SETTLE; + beforeState.copy(viewState); + + if(isChecked()){ + setCheckedViewState(afterState); + }else{ + setUncheckViewState(afterState); + } + valueAnimator.start(); + } + + + @Override + public final void setOnClickListener(OnClickListener l) {} + + @Override + public final void setOnLongClickListener(OnLongClickListener l) {} + + public void setOnCheckedChangeListener(OnCheckedChangeListener l){ + onCheckedChangeListener = l; + } + + public interface OnCheckedChangeListener{ + void onCheckedChanged(SwitchButton view, boolean isChecked); + } + + /*******************************************************/ + private static float dp2px(float dp){ + Resources r = Resources.getSystem(); + return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, r.getDisplayMetrics()); + } + + private static int dp2pxInt(float dp){ + return (int) dp2px(dp); + } + + private static int optInt(TypedArray typedArray, + int index, + int def) { + if(typedArray == null){return def;} + return typedArray.getInt(index, def); + } + + + private static float optPixelSize(TypedArray typedArray, + int index, + float def) { + if(typedArray == null){return def;} + return typedArray.getDimension(index, def); + } + + private static int optPixelSize(TypedArray typedArray, + int index, + int def) { + if(typedArray == null){return def;} + return typedArray.getDimensionPixelOffset(index, def); + } + + private static int optColor(TypedArray typedArray, + int index, + int def) { + if(typedArray == null){return def;} + return typedArray.getColor(index, def); + } + + private static boolean optBoolean(TypedArray typedArray, + int index, + boolean def) { + if(typedArray == null){return def;} + return typedArray.getBoolean(index, def); + } + /*******************************************************/ + + + /** + * 阴影半径 + */ + private int shadowRadius; + /** + * 阴影Y偏移px + */ + private int shadowOffset; + /** + * 阴影颜色 + */ + private int shadowColor ; + + /** + * 背景半径 + */ + private float viewRadius; + /** + * 按钮半径 + */ + private float buttonRadius; + + /** + * 背景高 + */ + private float height ; + /** + * 背景宽 + */ + private float width; + /** + * 背景位置 + */ + private float left ; + private float top ; + private float right ; + private float bottom ; + private float centerX; + private float centerY; + + /** + * 背景底色 + */ + private int background; + /** + * 背景关闭颜色 + */ + private int uncheckColor; + /** + * 背景打开颜色 + */ + private int checkedColor; + /** + * 边框宽度px + */ + private int borderWidth; + + /** + * 打开指示线颜色 + */ + private int checkLineColor; + /** + * 打开指示线宽 + */ + private int checkLineWidth; + /** + * 打开指示线长 + */ + private float checkLineLength; + /** + * 关闭圆圈颜色 + */ + private int uncheckCircleColor; + /** + *关闭圆圈线宽 + */ + private int uncheckCircleWidth; + /** + *关闭圆圈位移X + */ + private float uncheckCircleOffsetX; + /** + *关闭圆圈半径 + */ + private float uncheckCircleRadius; + /** + *打开指示线位移X + */ + private float checkedLineOffsetX; + /** + *打开指示线位移Y + */ + private float checkedLineOffsetY; + /** + * Color for button when it's uncheck + */ + private int uncheckButtonColor; + /** + * Color for button when it's check + */ + private int checkedButtonColor; + + + /** + * 按钮最左边 + */ + private float buttonMinX; + /** + * 按钮最右边 + */ + private float buttonMaxX; + + /** + * 按钮画笔 + */ + private Paint buttonPaint; + /** + * 背景画笔 + */ + private Paint paint; + + /** + * 当前状态 + */ + private ViewState viewState; + private ViewState beforeState; + private ViewState afterState; + + private RectF rect = new RectF(); + /** + * 动画状态 + */ + private int animateState = ANIMATE_STATE_NONE; + + /** + * + */ + private ValueAnimator valueAnimator; + + private final android.animation.ArgbEvaluator argbEvaluator + = new android.animation.ArgbEvaluator(); + + /** + *是否选中 + */ + private boolean isChecked; + /** + * 是否启用动画 + */ + private boolean enableEffect; + /** + * 是否启用阴影效果 + */ + private boolean shadowEffect; + /** + * 是否显示指示器 + */ + private boolean showIndicator; + /** + * 收拾是否按下 + */ + private boolean isTouchingDown = false; + /** + * + */ + private boolean isUiInited = false; + /** + * + */ + private boolean isEventBroadcast = false; + + private OnCheckedChangeListener onCheckedChangeListener; + + /** + * 手势按下的时刻 + */ + private long touchDownTime; + + private Runnable postPendingDrag = new Runnable() { + @Override + public void run() { + if(!isInAnimating()){ + pendingDragState(); + } + } + }; + + private ValueAnimator.AnimatorUpdateListener animatorUpdateListener + = new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + float value = (Float) animation.getAnimatedValue(); + switch (animateState) { + case ANIMATE_STATE_PENDING_SETTLE: { + } + case ANIMATE_STATE_PENDING_RESET: { + } + case ANIMATE_STATE_PENDING_DRAG: { + viewState.checkedLineColor = (int) argbEvaluator.evaluate( + value, + beforeState.checkedLineColor, + afterState.checkedLineColor + ); + + viewState.radius = beforeState.radius + + (afterState.radius - beforeState.radius) * value; + + if(animateState != ANIMATE_STATE_PENDING_DRAG){ + viewState.buttonX = beforeState.buttonX + + (afterState.buttonX - beforeState.buttonX) * value; + } + + viewState.checkStateColor = (int) argbEvaluator.evaluate( + value, + beforeState.checkStateColor, + afterState.checkStateColor + ); + + break; + } + case ANIMATE_STATE_SWITCH: { + viewState.buttonX = beforeState.buttonX + + (afterState.buttonX - beforeState.buttonX) * value; + + float fraction = (viewState.buttonX - buttonMinX) / (buttonMaxX - buttonMinX); + + viewState.checkStateColor = (int) argbEvaluator.evaluate( + fraction, + uncheckColor, + checkedColor + ); + + viewState.radius = fraction * viewRadius; + viewState.checkedLineColor = (int) argbEvaluator.evaluate( + fraction, + Color.TRANSPARENT, + checkLineColor + ); + break; + } + default: + case ANIMATE_STATE_DRAGING: { + } + case ANIMATE_STATE_NONE: { + break; + } + } + postInvalidate(); + } + }; + + private Animator.AnimatorListener animatorListener + = new Animator.AnimatorListener() { + @Override + public void onAnimationStart(Animator animation) { + } + + @Override + public void onAnimationEnd(Animator animation) { + switch (animateState) { + case ANIMATE_STATE_DRAGING: { + break; + } + case ANIMATE_STATE_PENDING_DRAG: { + animateState = ANIMATE_STATE_DRAGING; + viewState.checkedLineColor = Color.TRANSPARENT; + viewState.radius = viewRadius; + + postInvalidate(); + break; + } + case ANIMATE_STATE_PENDING_RESET: { + animateState = ANIMATE_STATE_NONE; + postInvalidate(); + break; + } + case ANIMATE_STATE_PENDING_SETTLE: { + animateState = ANIMATE_STATE_NONE; + postInvalidate(); + broadcastEvent(); + break; + } + case ANIMATE_STATE_SWITCH: { + isChecked = !isChecked; + animateState = ANIMATE_STATE_NONE; + postInvalidate(); + broadcastEvent(); + break; + } + default: + case ANIMATE_STATE_NONE: { + break; + } + } + } + + @Override + public void onAnimationCancel(Animator animation) { + } + + @Override + public void onAnimationRepeat(Animator animation) { + } + }; + + + /*******************************************************/ + /** + * 保存动画状态 + * */ + private static class ViewState { + /** + * 按钮x位置[buttonMinX-buttonMaxX] + */ + float buttonX; + /** + * 状态背景颜色 + */ + int checkStateColor; + /** + * 选中线的颜色 + */ + int checkedLineColor; + /** + * 状态背景的半径 + */ + float radius; + ViewState(){} + private void copy(ViewState source){ + this.buttonX = source.buttonX; + this.checkStateColor = source.checkStateColor; + this.checkedLineColor = source.checkedLineColor; + this.radius = source.radius; + } + } + +} diff --git a/app/src/main/res/drawable/ic_top.xml b/app/src/main/res/drawable/ic_top.xml index c577827..aa0c2e6 100644 --- a/app/src/main/res/drawable/ic_top.xml +++ b/app/src/main/res/drawable/ic_top.xml @@ -4,12 +4,6 @@ android:viewportWidth="1024" android:viewportHeight="1024"> - - + android:pathData="M855.87,106.43a42.46,42.46 0,0 1,-42.46 42.46L203.44,148.9a42.46,42.46 0,0 1,0 -84.93h609.97a42.46,42.46 0,0 1,42.46 42.46zM511.82,264.35a42.46,42.46 0,0 0,-42.46 42.46v609.97a42.46,42.46 0,0 0,84.93 0L554.29,306.82a42.46,42.46 0,0 0,-42.46 -42.46zM541.97,233.02c-16.59,-16.58 -42.53,-17.54 -57.92,-2.13L171.23,543.68c-15.41,15.41 -14.45,41.34 2.13,57.92 16.59,16.59 42.51,17.54 57.92,2.13l312.8,-312.78c15.39,-15.41 14.45,-41.34 -2.13,-57.92zM481.7,233.02c-16.58,16.58 -17.54,42.51 -2.13,57.92l312.8,312.8c15.39,15.39 41.33,14.45 57.92,-2.14 16.58,-16.58 17.52,-42.51 2.11,-57.92L539.62,230.9c-15.41,-15.41 -41.34,-14.45 -57.92,2.13z" + android:fillColor="#000000"/> diff --git a/app/src/main/res/layout/menu_book.xml b/app/src/main/res/layout/menu_book.xml index 0f81aab..04960cc 100644 --- a/app/src/main/res/layout/menu_book.xml +++ b/app/src/main/res/layout/menu_book.xml @@ -4,15 +4,15 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" - android:layout_marginBottom="10dp" - android:paddingHorizontal="10dp"> + android:layout_marginBottom="15dp" + android:paddingHorizontal="15dp"> + android:paddingVertical="10dp"> - + android:layout_marginBottom="15dp" + android:paddingHorizontal="15dp"> + android:paddingVertical="10dp"> @color/textPrimary @dimen/text_small_size + + + + + + + + + + + + + + + + + + + + + + + + + + + +