package com.hjq.demo.widget.pro; import static com.hjq.demo.widget.pro.BubbleUtils.dp2px; import static com.hjq.demo.widget.pro.BubbleUtils.sp2px; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.app.Activity; 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.Path; import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.RectF; import android.os.Build; import android.os.Bundle; import android.os.Parcelable; import android.util.AttributeSet; import android.util.SparseArray; import android.view.Gravity; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.ViewParent; import android.view.Window; import android.view.WindowManager; import android.view.animation.LinearInterpolator; import androidx.annotation.ColorInt; import androidx.annotation.IntDef; import androidx.annotation.NonNull; import androidx.core.content.ContextCompat; import com.hjq.demo.R; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.math.BigDecimal; /** * A beautiful and powerful Android custom seek bar, which has a bubble view with progress * appearing upon when seeking. Highly customizable, mostly demands has been considered. * <p> * Created by woxingxiao on 2016-10-27. */ public class BubbleSeekBar extends View { static final int NONE = -1; @IntDef({NONE, TextPosition.SIDES, TextPosition.BOTTOM_SIDES, TextPosition.BELOW_SECTION_MARK}) @Retention(RetentionPolicy.SOURCE) public @interface TextPosition { int SIDES = 0, BOTTOM_SIDES = 1, BELOW_SECTION_MARK = 2; } private float mMin; // min private float mMax; // max private float mProgress; // 实时值 private boolean isFloatType; // 支持浮点型输出 private int mTrackSize; // height of right-track(on the right of thumb) private int mSecondTrackSize; // 右轨道高度(拇指右侧) private int mThumbRadius; // 半径的 圆角 private int mThumbRadiusOnDragging; // 拖拽时拇指半径 private int mTrackColor; // 右声道的颜色 private int mSecondTrackColor; // 左声道的颜色 private int mThumbColor; // 拇指颜色 private int mSectionCount; // 整个进度的份额(最大-最小) private boolean isShowSectionMark; // 是否显示分界点 private boolean isAutoAdjustSectionMark; // 是否自动滚动到最近的区段标记 private boolean isShowSectionText; // 是否显示节文本 private int mSectionTextSize; // 节文本的文本大小 private int mSectionTextColor; // 节文本的文本颜色 @TextPosition private int mSectionTextPosition = NONE; // 段文字相对于轨迹的文字位置 private int mSectionTextInterval; // 两段文字的间隔 private boolean isShowThumbText; // 是否在thumb下显示实时进度文本 private int mThumbTextSize; // 进度文本的文本大小 private int mThumbTextColor; // 进度文本的文本颜色 private boolean isShowProgressInFloat; // 是否在浮动中显示气泡进度 private boolean isTouchToSeek; // 触按赛道上的任何位置可快速搜索 private boolean isSeekStepSection; // seek one step by one section, the progress is discrete private boolean isSeekBySection; // 分段搜索,进度可能不是线性的 private long mAnimDuration; // 动画的持续时间 private boolean isAlwaysShowBubble; // 泡泡显示的时间 private long mAlwaysShowBubbleDelay; // t气泡出现之前的延迟持续时间始终显示 private boolean isHideBubble; // 是否隐藏气泡 private boolean isRtl; // 从右向左 private int mBubbleColor;// color of bubble private int mBubbleTextSize; // text size of bubble-progress private int mBubbleTextColor; // text color of bubble-progress private float mDelta; // max - min private float mSectionValue; // (mDelta / mSectionCount) private float mThumbCenterX; // X coordinate of thumb's center private float mTrackLength; // pixel length of whole track private float mSectionOffset; // pixel length of one section private boolean isThumbOnDragging; // is thumb on dragging or not private int mTextSpace; // space between text and track private boolean triggerBubbleShowing; private SparseArray<String> mSectionTextArray = new SparseArray<>(); private float mPreThumbCenterX; private boolean triggerSeekBySection; private OnProgressChangedListener mProgressListener; // progress changing listener private float mLeft; // space between left of track and left of the view private float mRight; // space between right of track and left of the view private Paint mPaint; private Rect mRectText; private WindowManager mWindowManager; private BubbleView mBubbleView; private int mBubbleRadius; private float mBubbleCenterRawSolidX; private float mBubbleCenterRawSolidY; private float mBubbleCenterRawX; private WindowManager.LayoutParams mLayoutParams; private int[] mPoint = new int[2]; private boolean isTouchToSeekAnimEnd = true; private float mPreSecValue; // previous SectionValue private BubbleConfigBuilder mConfigBuilder; // config attributes public BubbleSeekBar(Context context) { this(context, null); } public BubbleSeekBar(Context context, AttributeSet attrs) { this(context, attrs, 0); } public BubbleSeekBar(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.BubbleSeekBar, defStyleAttr, 0); mMin = a.getFloat(R.styleable.BubbleSeekBar_bsb_min, 0.0f); mMax = a.getFloat(R.styleable.BubbleSeekBar_bsb_max, 100.0f); mProgress = a.getFloat(R.styleable.BubbleSeekBar_bsb_progress, mMin); isFloatType = a.getBoolean(R.styleable.BubbleSeekBar_bsb_is_float_type, false); mTrackSize = a.getDimensionPixelSize(R.styleable.BubbleSeekBar_bsb_track_size, dp2px(2)); mSecondTrackSize = a.getDimensionPixelSize(R.styleable.BubbleSeekBar_bsb_second_track_size, mTrackSize + dp2px(2)); mThumbRadius = a.getDimensionPixelSize(R.styleable.BubbleSeekBar_bsb_thumb_radius, mSecondTrackSize + dp2px(2)); mThumbRadiusOnDragging = a.getDimensionPixelSize(R.styleable.BubbleSeekBar_bsb_thumb_radius_on_dragging, mSecondTrackSize * 2); mSectionCount = a.getInteger(R.styleable.BubbleSeekBar_bsb_section_count, 10); mTrackColor = a.getColor(R.styleable.BubbleSeekBar_bsb_track_color, ContextCompat.getColor(context, R.color.colorPrimary)); mSecondTrackColor = a.getColor(R.styleable.BubbleSeekBar_bsb_second_track_color, ContextCompat.getColor(context, R.color.colorAccent)); mThumbColor = a.getColor(R.styleable.BubbleSeekBar_bsb_thumb_color, mSecondTrackColor); isShowSectionText = a.getBoolean(R.styleable.BubbleSeekBar_bsb_show_section_text, false); mSectionTextSize = a.getDimensionPixelSize(R.styleable.BubbleSeekBar_bsb_section_text_size, sp2px(14)); mSectionTextColor = a.getColor(R.styleable.BubbleSeekBar_bsb_section_text_color, mTrackColor); isSeekStepSection = a.getBoolean(R.styleable.BubbleSeekBar_bsb_seek_step_section, false); isSeekBySection = a.getBoolean(R.styleable.BubbleSeekBar_bsb_seek_by_section, false); int pos = a.getInteger(R.styleable.BubbleSeekBar_bsb_section_text_position, NONE); if (pos == 0) { mSectionTextPosition = TextPosition.SIDES; } else if (pos == 1) { mSectionTextPosition = TextPosition.BOTTOM_SIDES; } else if (pos == 2) { mSectionTextPosition = TextPosition.BELOW_SECTION_MARK; } else { mSectionTextPosition = NONE; } mSectionTextInterval = a.getInteger(R.styleable.BubbleSeekBar_bsb_section_text_interval, 1); isShowThumbText = a.getBoolean(R.styleable.BubbleSeekBar_bsb_show_thumb_text, false); mThumbTextSize = a.getDimensionPixelSize(R.styleable.BubbleSeekBar_bsb_thumb_text_size, sp2px(14)); mThumbTextColor = a.getColor(R.styleable.BubbleSeekBar_bsb_thumb_text_color, mSecondTrackColor); mBubbleColor = a.getColor(R.styleable.BubbleSeekBar_bsb_bubble_color, mSecondTrackColor); mBubbleTextSize = a.getDimensionPixelSize(R.styleable.BubbleSeekBar_bsb_bubble_text_size, sp2px(14)); mBubbleTextColor = a.getColor(R.styleable.BubbleSeekBar_bsb_bubble_text_color, Color.WHITE); isShowSectionMark = a.getBoolean(R.styleable.BubbleSeekBar_bsb_show_section_mark, false); isAutoAdjustSectionMark = a.getBoolean(R.styleable.BubbleSeekBar_bsb_auto_adjust_section_mark, false); isShowProgressInFloat = a.getBoolean(R.styleable.BubbleSeekBar_bsb_show_progress_in_float, false); int duration = a.getInteger(R.styleable.BubbleSeekBar_bsb_anim_duration, -1); mAnimDuration = duration < 0 ? 200 : duration; isTouchToSeek = a.getBoolean(R.styleable.BubbleSeekBar_bsb_touch_to_seek, false); isAlwaysShowBubble = a.getBoolean(R.styleable.BubbleSeekBar_bsb_always_show_bubble, false); duration = a.getInteger(R.styleable.BubbleSeekBar_bsb_always_show_bubble_delay, 0); mAlwaysShowBubbleDelay = duration < 0 ? 0 : duration; isHideBubble = a.getBoolean(R.styleable.BubbleSeekBar_bsb_hide_bubble, false); isRtl = a.getBoolean(R.styleable.BubbleSeekBar_bsb_rtl, false); setEnabled(a.getBoolean(R.styleable.BubbleSeekBar_android_enabled, isEnabled())); a.recycle(); mPaint = new Paint(); mPaint.setAntiAlias(true); mPaint.setStrokeCap(Paint.Cap.ROUND); mPaint.setTextAlign(Paint.Align.CENTER); mRectText = new Rect(); mTextSpace = dp2px(2); initConfigByPriority(); if (isHideBubble) return; mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); // init BubbleView mBubbleView = new BubbleView(context); mBubbleView.setProgressText(isShowProgressInFloat ? String.valueOf(getProgressFloat()) : String.valueOf(getProgress())); mLayoutParams = new WindowManager.LayoutParams(); mLayoutParams.gravity = Gravity.START | Gravity.TOP; mLayoutParams.width = ViewGroup.LayoutParams.WRAP_CONTENT; mLayoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT; mLayoutParams.format = PixelFormat.TRANSLUCENT; mLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED; // MIUI禁止了开发者使用TYPE_TOAST,Android 7.1.1 对TYPE_TOAST的使用更严格 if (BubbleUtils.isMIUI() || Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) { mLayoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION; } else { mLayoutParams.type = WindowManager.LayoutParams.TYPE_TOAST; } calculateRadiusOfBubble(); } private void initConfigByPriority() { if (mMin == mMax) { mMin = 0.0f; mMax = 100.0f; } if (mMin > mMax) { float tmp = mMax; mMax = mMin; mMin = tmp; } if (mProgress < mMin) { mProgress = mMin; } if (mProgress > mMax) { mProgress = mMax; } if (mSecondTrackSize < mTrackSize) { mSecondTrackSize = mTrackSize + dp2px(2); } if (mThumbRadius <= mSecondTrackSize) { mThumbRadius = mSecondTrackSize + dp2px(2); } if (mThumbRadiusOnDragging <= mSecondTrackSize) { mThumbRadiusOnDragging = mSecondTrackSize * 2; } if (mSectionCount <= 0) { mSectionCount = 10; } mDelta = mMax - mMin; mSectionValue = mDelta / mSectionCount;
seekbar 泡泡
最新推荐文章于 2024-07-18 11:07:23 发布