一个用KT写的播放器上层UI框架(2)

261

横屏显示view HScreenController.kt

class HScreenController(context: Context, attrs: AttributeSet? = null) :BaseWidget(context,attrs) {
    var mSeekBar:MarkSeekBar?=null
    var iv_pause: ImageView?=null
    var mBack: ImageView?=null
    var mCurTime: TextView?=null
    var mTitle: TextView?=null
    var mEndTime: TextView?=null
    var ivHalfScreen:ImageView?=null
    var toHalfScreen:() -> Unit={}
    var pauseOrPlayClick:()->Unit={}
    var seekTo:(position:Int)->Unit={}
    var mTotalTime:Int=0
    var isDragStart = false
    override fun initView() {
        mBack=view?.findViewById(R.id.mBack)
        mTitle=view?.findViewById(R.id.mTitle)
        mSeekBar=view?.findViewById(R.id.sb_progress)
        iv_pause=view?.findViewById(R.id.iv_pause)
        mCurTime=view?.findViewById(R.id.tv_cur_time)
        mEndTime=view?.findViewById(R.id.tv_end_time)
        ivHalfScreen=view?.findViewById(R.id.ivHalfScreen)
    }

    override fun initInnerEvent() {
        mBack?.setOnClickListener {
            toHalfScreen()
        }
        iv_pause?.setOnClickListener {
            pauseOrPlayClick()
        }
        ivHalfScreen?.setOnClickListener{
            toHalfScreen()
        }
        mSeekBar?.setOnProgressChangedListener(SeekBarChangeEvent())
    }

    inner class SeekBarChangeEvent : MarkSeekBar.OnProgressChangedListener {
        override fun onProgressChanged(
            signSeekBar: MarkSeekBar,
            progress: Float,
            fromUser: Boolean
        ) {
            isDragStart = true
            mCurTime?.text = DKTimeFormatter.getInstance().stringForTime(progress.toInt())
        }

        override fun getProgressOnActionUp(signSeekBar: MarkSeekBar, progress: Float) {
            seekTo(progress.toInt())
            // 个别机型(vivo)单击进度条时按下状态没有置回,手动置回状态
            if (mSeekBar?.isPressed ==true) {
                mSeekBar?.isPressed = false
            }
            isDragStart = false
        }
    }

    fun setTitle(title:String?){
        mTitle?.text=title?:""
    }

    fun setTotalTime(totalTime:Int){
        mTotalTime = totalTime
        mSeekBar?.max=totalTime.toFloat()
        mEndTime?.text = DKTimeFormatter.getInstance().stringForTime(mTotalTime)
    }
    fun updateProgress(position:Int) {
        mCurTime?.text = DKTimeFormatter.getInstance().stringForTime(position)
        if (mSeekBar != null && mTotalTime > 0) {
            mSeekBar?.progress = position.toFloat()
        }
    }
    fun updateSecondCache(cacheTime: Int) {
        if (mSeekBar != null && mTotalTime > 0) {
            mSeekBar?.setCacheProgress((cacheTime * 1000).toFloat())
        }
    }

    fun showPlaying(){
        iv_pause?.setImageResource(R.drawable.player_play_start)
    }

    fun showPause(){
        iv_pause?.setImageResource(R.drawable.player_pause_16)
    }

    /**
     * 播放完成展示
     */
    fun showPlayEnd(){
        mSeekBar?.progress = mSeekBar?.max ?:100.toFloat()
        mCurTime?.text = DKTimeFormatter.getInstance().stringForTime(mTotalTime)
        mEndTime?.text = DKTimeFormatter.getInstance().stringForTime(mTotalTime)
    }

    override fun getLayoutId() = R.layout.player_h_controller
}

横屏显示view布局 player_h_controller.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="187.5dp"
        android:background="@drawable/bg_00000000_66000000">
        <LinearLayout
            android:orientation="horizontal"
            android:layout_marginHorizontal="20dp"
            android:gravity="center_vertical"
            android:layout_marginTop="20dp"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
            <ImageView
                android:id="@+id/mBack"
                android:layout_width="30dp"
                android:layout_height="30dp"
                android:layout_gravity="center"
                android:background="@drawable/icon_player_back" />

            <TextView
                android:id="@+id/mTitle"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:singleLine="true"
                android:layout_marginLeft="22dp"
                tools:text="雅思词汇分级及时间分配"
                android:maxLines="1"
                android:ellipsize="end"
                android:textColor="@android:color/white"
                android:textSize="14dp" />
        </LinearLayout>

    </RelativeLayout>
    <LinearLayout
        android:id="@+id/media_controller_h"
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:orientation="horizontal"
        android:paddingTop="12dp"
        android:gravity="center_vertical"
        android:layout_alignParentBottom="true"
        android:background="@drawable/bg_00000000_66000000_270">

        <ImageView
            android:id="@+id/iv_pause"
            android:layout_width="30dp"
            android:layout_height="30dp"
            android:padding="5dp"
            android:layout_marginLeft="22dp"
            android:src="@drawable/icon_player_pause_xxx" />

        <TextView
            android:id="@+id/tv_cur_time"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textStyle="bold"
            android:layout_marginLeft="7dp"
            android:text="00:00"
            android:textColor="@color/white"
            android:textSize="11dp" />

        <TextView
            android:id="@+id/tv_sep_time"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="/"
            android:textStyle="bold"
            android:textColor="@color/white"
            android:textSize="11dp" />

        <TextView
            android:id="@+id/tv_end_time"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="00:00"
            android:textStyle="bold"
            android:textColor="@color/white"
            android:textSize="11dp" />



        <RelativeLayout
            android:id="@+id/player_seek_rl"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:clipChildren="false"
            android:clipToPadding="false"
            android:orientation="horizontal">

            <com.koo.ieltspro2019.view.MarkSeekBar
                android:id="@+id/sb_progress"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_centerInParent="true"
                android:layout_centerVertical="true"
                app:msb_cache_track_color="#CCFFFFFF"
                app:msb_cache_track_size="2dp"
                app:msb_mark_radius="2.5dp"
                app:msb_min="0"
                app:msb_second_track_color="#FC5D64"
                app:msb_second_track_size="2dp"
                app:msb_section_mark_color="#FFFFFF"
                app:msb_show_section_mark="true"
                app:msb_show_thumb_shadow="true"
                app:msb_thumb_radius="10dp"
                app:msb_thumb_radius_on_dragging="13dp"
                app:msb_touch_to_seek="true"
                app:msb_track_color="#66FFFFFF"
                app:msb_track_size="2dp" />
        </RelativeLayout>

        <ImageView
            android:id="@+id/ivHalfScreen"
            android:layout_width="28dp"
            android:layout_height="28dp"
            android:layout_marginRight="19dp"
            android:padding="5dp"
            android:src="@drawable/player_icon_full"/>
    </LinearLayout>
</RelativeLayout>

加载中view PlayerLoadingView.kt

class PlayerLoadingView(context: Context, attrs: AttributeSet? = null) :BaseWidget(context, attrs){
    private var mLoadinLl: LinearLayout? = null
    private var mLvLoading: ImageView? = null
    private var mTvLoadingText: TextView? = null
    private var mTvLoadingSpeed: TextView? = null
    private var hyperspaceJumpAnimation: Animation? = null
    override fun initView() {
        mLoadinLl = view?.findViewById(R.id.ll_loading_common)
        mLvLoading = view?.findViewById(R.id.lv_loading)
        mTvLoadingSpeed = view?.findViewById(R.id.tv_loading_speed)
        mTvLoadingText = view?.findViewById(R.id.tv_loading_text)
        hyperspaceJumpAnimation = AnimationUtils.loadAnimation(context,R.anim.loading_animation) // 加载动画
    }

    override fun initInnerEvent() {
    }

    override fun getLayoutId()= R.layout.player_loading_view

    fun showBuffering() {
        visibility = VISIBLE
        mLvLoading?.visibility = VISIBLE
        mTvLoadingSpeed?.visibility = VISIBLE
        mTvLoadingText?.visibility = GONE
        mLoadinLl?.visibility = VISIBLE
        mLvLoading?.startAnimation(hyperspaceJumpAnimation)
    }

    /**
     * 服务器时间戳返回前Loading
     * 不显示速度
     */
    fun showPreLoading() {
        visibility = VISIBLE
        mLvLoading?.visibility = VISIBLE
        mTvLoadingSpeed?.visibility = GONE
        mTvLoadingText?.visibility = GONE
        mLoadinLl?.visibility = VISIBLE
        mLvLoading?.startAnimation(hyperspaceJumpAnimation)
    }

    fun updateLoadingSpeed(what: Int, extras: Int) {
        if (what == KoolMediaPlayer.MEDIA_INFO_NETWORK_BANDWIDTH && mTvLoadingSpeed != null && mTvLoadingSpeed?.visibility == VISIBLE) {
            mTvLoadingSpeed?.text = context.resources.getString(R.string.player_loading_speed, extras)
        }
    }

    override fun onDetachedFromWindow() {
        super.onDetachedFromWindow()
        mLvLoading?.clearAnimation()
    }

}

加载中view布局 player_loading_view.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:clickable="false"
    android:gravity="center"
    android:orientation="vertical">
    <LinearLayout
        android:id="@+id/ll_loading_common"
        android:layout_width="90dp"
        android:layout_height="73dp"
        android:gravity="center"
        android:orientation="vertical"
        android:background="@drawable/bg_cc212538_radius_10"
        android:visibility="gone">


        <ImageView
            android:id="@+id/lv_loading"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:src="@drawable/ic_loading"/>
        <TextView
            android:id="@+id/tv_loading_speed"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="6dp"
            android:paddingLeft="5dp"
            android:paddingRight="5dp"
            android:paddingBottom="1dp"
            android:text="0 KB/s"
            android:textColor="#ccffffff"
            android:textSize="12dp"
            android:visibility="gone" />

        <TextView
            android:id="@+id/tv_loading_text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="6dp"
            android:gravity="center"
            android:paddingLeft="5dp"
            android:paddingRight="5dp"
            android:text="加载中(0%)"
            android:textColor="#ccffffff"
            android:textSize="10dp"
            android:visibility="gone" />
    </LinearLayout>
</LinearLayout>

手指左右滑动屏幕时 显示的进度view SeekingMonitorView.kt

class SeekingMonitorView(context: Context, attrs: AttributeSet? = null) :BaseWidget(context,attrs) {
    private var mSeekBar: SeekBar? = null
    private var mCurrentTimeTv: TextView? = null
    private var startX = 0f

    private var mDuration:Int?=1
    private var mProgress: Int?=0

    var seekTo:(position:Int)->Unit={}

    //最后得到的进度
    var currentPosition:Int?=0
    override fun initView() {
        mSeekBar = view?.findViewById(R.id.monitorSeekBar)
        mCurrentTimeTv = view?.findViewById(R.id.dragTimeTv)
    }

    override fun initInnerEvent() {
    }

    override fun getLayoutId() = R.layout.player_seek_monitor_view

    fun initMonitorView(progress: Int,duration: Int) {
        mProgress=progress
        mDuration=duration
        if (mSeekBar != null) {
            mSeekBar?.max = duration
            mSeekBar?.progress = progress
        }
        if (mCurrentTimeTv != null) {
            mCurrentTimeTv!!.text = TimeUtils.parseShortTime(progress)
        }
    }

    fun seekStart(touchX:Float){
        //记录初始点位置
        if (startX==-1f){
            startX=touchX
        }else{
            var tempX=touchX-startX
            if(abs(tempX)>5){//移动超过5像素才开始更新
                //看一下改变的进度
                val percentProgress = getStepPosition(tempX)
                //换算出最后的进度
                currentPosition = getSeekToPosition(percentProgress, mProgress?:0, mDuration?:0)
                //更新文本文案
                updatePositionText(currentPosition?:0)
                //更新进度条
                if (mSeekBar != null) {
                    mSeekBar?.max = mDuration?:100
                    mSeekBar?.progress = currentPosition?:0 * 100 / if(mDuration!=null&&mDuration!!>0){mDuration!!}else{1}
                }
            }
        }

    }

    fun adjustSeekEnd() {
        startX=-1f
        seekTo(currentPosition?:0)
    }

    /**
     * 根据要改变的值和当前进度,换算出最后的进度
     */
    private fun getSeekToPosition(percentProgress: Int, currentPosition: Int, duration: Int): Int {
        val tPosition = currentPosition + percentProgress*duration/100
        if (tPosition < 0) {
            return 0
        } else if (tPosition > duration) {
            return duration
        }
        return tPosition
    }

    private fun updatePositionText(updatePosition: Int) {
        var updatePosition = updatePosition
        if (updatePosition < 0) {
            updatePosition = 0
        }
        val parseShortTime = TimeUtils.parseShortTime(updatePosition)
        if (mCurrentTimeTv != null) {
            mCurrentTimeTv?.text = parseShortTime
        }
    }


    /**
     * 根据手指位移了多少算出,百分比进度
     */
    fun getStepPosition(tempX: Float): Int {
        return (tempX.toInt()*100/context.resources.displayMetrics.widthPixels)
    }

    fun hideMonitorView() {
        visibility = GONE
    }
}

进度view的布局 player_seek_monitor_view.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical">
    <LinearLayout
        android:id="@+id/ll_monitor_common"
        android:layout_width="160dp"
        android:background="@drawable/player_progress_center_bg"
        android:orientation="vertical"
        android:visibility="visible"
        android:gravity="center"
        android:layout_height="73dp">

        <TextView
            android:id="@+id/dragTimeTv"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textColor="@color/C_FFFFFFFF"
            android:textSize="17dp"
            android:text=""
            android:layout_marginTop="10dp"
            android:layout_gravity="center_horizontal"/>
        <com.koo.ieltspro2019.player.ui.widget.PlayerSeekbar
            android:id="@+id/monitorSeekBar"
            android:layout_marginTop="9dp"
            android:layout_marginBottom="15dp"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginLeft="8dp"
            android:layout_marginRight="8dp"
            android:maxHeight="1dp"
            android:minHeight="1dp"
            android:padding="0dp"
            android:paddingEnd="0dp"
            android:paddingStart="0dp"
            android:progressDrawable="@drawable/player_seekbar"
            android:thumb="@null"/>
    </LinearLayout>

</LinearLayout>

调节音量和亮度的父类 GestureView.java

public abstract class GestureView extends FrameLayout {
    private Animator mAnimator = null;
    private Runnable mDissmissRunnable = new Runnable() {
        @Override
        public void run() {
            hide();
        }
    };
    protected ImageView mIcon;
    private boolean mIsShowing;
    protected TextView mText;

    protected abstract int getIcon();

    protected abstract int getTextMarginTop();

    public GestureView(Context context) {
        super(context);
        init();
    }

    private void init() {
        Context context = getContext();
        // 图标
        mIcon = new ImageView(context);
        LayoutParams layoutParams = new LayoutParams(Utils.dp2px(76), Utils.dp2px(93));
        layoutParams.gravity = Gravity.CENTER;
        mIcon.setLayoutParams(layoutParams);
        mIcon.setImageResource(getIcon());
        addView(mIcon);
        // 文字
        mText = new TextView(context);
        LayoutParams layoutParams2 = new LayoutParams(-2, -2);
        layoutParams2.gravity = 17;
        layoutParams2.topMargin = getTextMarginTop();
        mText.setLayoutParams(layoutParams2);
        mText.setTextColor(0xFFFFFFFF);
        mText.setTextSize(0, (float) Utils.dp2px(12));
        addView(mText);
        setVisibility(View.GONE);
    }

    public boolean isShowing() {
        return mIsShowing;
    }

    public void triggerAutoDismiss() {
        Scheduler.getUIHandler().removeCallbacks(mDissmissRunnable);
        Scheduler.getUIHandler().postDelayed(mDissmissRunnable, 1800);
    }

    public final void show() {
        if (!mIsShowing) {
            animateIn();
            mIsShowing = true;
        }
        triggerAutoDismiss();
    }

    public final void hide() {
        if (mIsShowing) {
            animateOut();
            mIsShowing = false;
        }
    }

    public final void gone() {
        if (mIsShowing) {
            setVisibility(View.GONE);
            mIsShowing = false;
        }
    }

    public void animateIn() {
        if (mAnimator != null) {
            mAnimator.end();
        }
        mAnimator = AnimatorFactory.animateAlphaIn(this);
    }

    public void animateOut() {
        if (mAnimator != null) {
            mAnimator.end();
        }
        mAnimator = AnimatorFactory.animateAlphaOut(this);
    }
}

动画框架 AnimatorFactory.java

public class AnimatorFactory {

    private static final int mDurationTime = 250;
    public static class AnimatorInvalidateUpdateListener implements ValueAnimator.AnimatorUpdateListener {
        private View mView;

        public AnimatorInvalidateUpdateListener(View view) {
            this.mView = view;
        }

        @Override
        public void onAnimationUpdate(ValueAnimator valueAnimator) {
            if (this.mView != null) {
                this.mView.invalidate();
            }
        }
    }

    private static int getHeight(View view) {
        int height = view.getHeight();
        if (height == 0) {
            return view.getContext().getResources().getDisplayMetrics().heightPixels;
        }
        return height;
    }

    private static int getWidth(View view) {
        int width = view.getWidth();
        if (width == 0) {
            return view.getContext().getResources().getDisplayMetrics().widthPixels;
        }
        return width;
    }

    public static ValueAnimator animateInBottomView(View view) {
        ValueAnimator ofFloat = ObjectAnimator.ofFloat(view, "translationY", new float[]{(float) getHeight(view), 0.0f});
        ofFloat.setInterpolator(new DecelerateInterpolator());
        ofFloat.setDuration(mDurationTime);
        ofFloat.addUpdateListener(new AnimatorInvalidateUpdateListener(view));
        ofFloat.start();
        view.setVisibility(View.VISIBLE);
        return ofFloat;
    }

    public static ValueAnimator animateInTopView(View view) {
        ValueAnimator ofFloat = ObjectAnimator.ofFloat(view, "translationY", new float[]{(float) (-getHeight(view)), 0.0f});
        ofFloat.setInterpolator(new DecelerateInterpolator());
        ofFloat.setDuration(mDurationTime);
        ofFloat.addUpdateListener(new AnimatorInvalidateUpdateListener(view));
        ofFloat.start();
        view.setVisibility(View.VISIBLE);
        return ofFloat;
    }

    public static ValueAnimator animateInLeftView(View view) {
        ValueAnimator ofFloat = ObjectAnimator.ofFloat(view, "translationX", new float[]{(float) (-getWidth(view)), 0.0f});
        ofFloat.setInterpolator(new DecelerateInterpolator());
        ofFloat.setDuration(mDurationTime);
        ofFloat.addUpdateListener(new AnimatorInvalidateUpdateListener(view));
        ofFloat.start();
        view.setVisibility(View.VISIBLE);
        return ofFloat;
    }

    public static ValueAnimator animateInRightView(View view) {
        ValueAnimator ofFloat = ObjectAnimator.ofFloat(view, "translationX", new float[]{(float) getWidth(view), 0.0f});
        ofFloat.setInterpolator(new DecelerateInterpolator());
        ofFloat.setDuration(mDurationTime);
        ofFloat.addUpdateListener(new AnimatorInvalidateUpdateListener(view));
        view.setVisibility(View.VISIBLE);
        ofFloat.start();
        return ofFloat;
    }

    public static Animator animateOutBottomView(View view) {
        Animator ofFloat = ObjectAnimator.ofFloat(view, "translationY", new float[]{0.0f, (float) view.getHeight()});
        ofFloat.setInterpolator(new DecelerateInterpolator());
        ofFloat.setDuration(mDurationTime);
        ofFloat.addListener(new ViewGoneAnimatorListener(view));
        ofFloat.start();
        return ofFloat;
    }

    public static Animator animateOutTopView(View view) {
        Animator ofFloat = ObjectAnimator.ofFloat(view, "translationY", new float[]{0.0f, (float) (-view.getHeight())});
        ofFloat.setInterpolator(new DecelerateInterpolator());
        ofFloat.setDuration(mDurationTime);
        ofFloat.addListener(new ViewGoneAnimatorListener(view));
        ofFloat.start();
        return ofFloat;
    }

    public static Animator animateOutLeftView(View view) {
        Animator ofFloat = ObjectAnimator.ofFloat(view, "translationX", new float[]{0.0f, (float) (-getWidth(view))});
        ofFloat.setInterpolator(new DecelerateInterpolator());
        ofFloat.setDuration(mDurationTime);
        ofFloat.addListener(new ViewGoneAnimatorListener(view));
        ofFloat.start();
        return ofFloat;
    }

    public static Animator animateOutRightView(View view) {
        Animator ofFloat = ObjectAnimator.ofFloat(view, "translationX", new float[]{0.0f, (float) getWidth(view)});
        ofFloat.setInterpolator(new DecelerateInterpolator());
        ofFloat.setDuration(mDurationTime);
        ofFloat.addListener(new ViewGoneAnimatorListener(view));
        ofFloat.start();
        return ofFloat;
    }

    public static Animator animateAlphaIn(View view) {
        Animator ofFloat = ObjectAnimator.ofFloat(view, "alpha", new float[]{0.0f,0.9f});
        ofFloat.setInterpolator(new DecelerateInterpolator());
        ofFloat.setDuration(mDurationTime);
        ofFloat.start();
        view.setVisibility(View.VISIBLE);
        return ofFloat;
    }

    public static Animator animateAlphaOut(View view) {
        Animator ofFloat = ObjectAnimator.ofFloat(view,"alpha", new float[]{0.9f, 0.0f});
        ofFloat.setInterpolator(new DecelerateInterpolator());
        ofFloat.setDuration(mDurationTime);
        ofFloat.addListener(new ViewGoneAnimatorListener(view));
        ofFloat.start();
        return ofFloat;
    }
}

音量调节 GestureVolumn.java

public class GestureVolumn extends GestureView {
    public GestureVolumn(Context context) {
        super(context);
    }

    public static GestureVolumn create(RelativeLayout relativeLayout) {
        GestureVolumn gestureVolumn = new GestureVolumn(relativeLayout.getContext());
        gestureVolumn.setLayoutParams(new LayoutParams(-1, -1));
        relativeLayout.addView(gestureVolumn);
        return gestureVolumn;
    }

    public void adjustVolume(Activity activity, float f) {
        int streamMaxVolume = 0;
        int newVolumeValue = 0;
        AudioManager audioManager = (AudioManager) activity.getSystemService(Context.AUDIO_SERVICE);
        streamMaxVolume = audioManager.getStreamMaxVolume(3);
        newVolumeValue = getNewVolumeValue(f, streamMaxVolume, audioManager.getStreamVolume(3));
        Log.d("VOLUME_audio","streamMaxVolume:"+streamMaxVolume+" newVolumeValue:"+newVolumeValue);
        audioManager.setStreamVolume(3, newVolumeValue, 8);

        if (streamMaxVolume != 0) {
        setPercent((newVolumeValue * 100) / streamMaxVolume);
        }
        show();
    }

    private void setPercent(int i) {
        this.mText.setText(i + "%");
    }

    private int getNewVolumeValue(float f, int i, int i2) {
        int r0 = f > 0.0f ? i2 - 1 : f < 0.0f ? i2 + 1 : i2;
        if (r0 > i) {
            r0 = i;
        }
        if (r0 < 0) {
            return 0;
        }
        return r0;
    }

    @Override
    protected int getIcon() {
        return R.drawable.player_icon_vol;
    }

    @Override
    protected int getTextMarginTop() {
        return Utils.dp2px(16);
    }
}

亮度调节 GestureBrightness.java

public class GestureBrightness extends GestureView {

    public GestureBrightness(Context context) {
        super(context);
    }

    public static GestureBrightness create(RelativeLayout relativeLayout) {
        GestureBrightness gestureBrightness = new GestureBrightness(relativeLayout.getContext());
        gestureBrightness.setLayoutParams(new LayoutParams(-1, -1));
        relativeLayout.addView(gestureBrightness);
        return gestureBrightness;
    }

    public void adjustBrightness(Activity activity, float f) {
        int activityBrightness = (int) (AndroidUtils.getActivityBrightness(activity) * 255.0f);
        if (activityBrightness < 0) {
            activityBrightness = AndroidUtils.getSystemBrightness(activity);
        }
        activityBrightness = getNewBrightnessValue(f, activityBrightness);
        AndroidUtils.setActivityBrightness(activity, activityBrightness);
        setPercent((activityBrightness * 100) / MotionEventCompat.ACTION_MASK);
        show();
    }

    private void setPercent(int i) {
        this.mText.setText(i + "%");
    }

    private int getNewBrightnessValue(float f, int i) {
        int mask = MotionEventCompat.ACTION_MASK;
        int r2 = f > 0.0f ? i - 17 : f < 0.0f ? i + 17 : i;
        if (r2 <= MotionEventCompat.ACTION_MASK) {
            mask = r2;
        }
        if (mask < 2) {
            return 2;
        }
        return mask;
    }

    @Override
    protected int getIcon() {
        return R.drawable.player_icon_light;
    }

    @Override
    protected int getTextMarginTop() {
        return Utils.dp2px(16);
    }
}

网络播放错误 PlayerNetErrorView.kt

class PlayerNetErrorView(context: Context, attrs: AttributeSet?):BaseWidget(context, attrs) {
    private var  mRetryBtn: TextView? = null
    override fun initView() {
        mRetryBtn = view?.findViewById(R.id.mRetryBtn)
    }

    override fun initInnerEvent() {
    }

    override fun getLayoutId()= R.layout.view_play_error

    fun initListener(listener:OnClickListener?){
        mRetryBtn?.setOnClickListener(listener)
    }
}

播放错误布局 view_play_error.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#99000000"
    android:gravity="center"
    android:orientation="vertical">
    <LinearLayout
        android:orientation="vertical"
        android:gravity="center_horizontal"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">
        <TextView
            android:id="@+id/mErrorText"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Oops! 视频加载失败"
            android:textColor="@android:color/white"
            android:textSize="14dp"
            android:textStyle="bold" />

        <TextView
            android:id="@+id/mRetryBtn"
            android:layout_width="75dp"
            android:layout_height="26dp"
            android:layout_marginTop="20dp"
            android:background="@drawable/bg_corner_13_66ffffff"
            android:gravity="center"
            android:text="点击重试"
            android:textColor="@android:color/white"
            android:textSize="12dp"
            android:textStyle="bold" />
    </LinearLayout>

</LinearLayout>

播放完成 PlayCompleteView.kt

class PlayCompleteView(context: Context, attrs: AttributeSet? = null) :BaseWidget(context, attrs) {
    private var mTipsTitleTv: TextView? = null
    private var mReplayTv: TextView? = null
    private var mPlayNextTv: TextView? = null
    var replay:()->Unit={}
    var playNext:()->Unit={}
    override fun initView() {
        mTipsTitleTv = view?.findViewById(R.id.tipsTitleTv)
        mPlayNextTv = view?.findViewById(R.id.playNextTv)
        mReplayTv = view?.findViewById(R.id.replayTv)
    }

    override fun initInnerEvent() {
        mReplayTv?.setOnClickListener {
            replay()
        }
        mPlayNextTv?.setOnClickListener {
            playNext()
        }
    }

    fun showPlayCompleteView() {
        visibility = VISIBLE
    }

    fun hideMonitorView() {
        visibility = GONE
    }

    override fun getLayoutId()= R.layout.player_play_complete_portrait_view
}

播放完成 player_play_complete_portrait_view.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#96212538"
    android:gravity="center"
    android:orientation="vertical">
    <RelativeLayout
        android:id="@+id/ll_monitor_common"
        android:layout_width="200dp"
        android:visibility="visible"
        android:gravity="center"
        android:layout_height="100dp">

        <TextView
            android:id="@+id/tipsTitleTv"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textColor="@color/C_FFFFFFFF"
            android:layout_centerHorizontal="true"
            android:textStyle="bold"
            android:textSize="14dp"
            android:text="本节学完啦~"/>

        <LinearLayout
            android:layout_width="match_parent"
            android:orientation="horizontal"
            android:layout_below="@id/tipsTitleTv"
            android:layout_centerHorizontal="true"
            android:gravity="center"
            android:layout_marginTop="20dp"
            android:layout_height="26dp">
            <TextView
                android:id="@+id/replayTv"
                android:layout_width="wrap_content"
                android:paddingHorizontal="14dp"
                android:layout_height="match_parent"
                android:textColor="@color/C_FFFFFFFF"
                android:gravity="center"
                android:background="@drawable/bg_40ffffff_14"
                android:textStyle="bold"
                android:text="重新播放"/>
            <TextView
                android:id="@+id/playNextTv"
                android:layout_width="wrap_content"
                android:paddingHorizontal="14dp"
                android:layout_marginLeft="25dp"
                android:gravity="center"
                android:layout_height="match_parent"
                android:textColor="@color/C_FFFFFFFF"
                android:background="@drawable/bg_40ffffff_14"
                android:textStyle="bold"
                android:text="继续下节"/>
        </LinearLayout>
    </RelativeLayout>

</LinearLayout>

播放器的PlayerSeekbar.java

public class MarkSeekBar extends View {

    //progress最小值
    private float mMin;
    // progress最大值
    private float mMax;
    // 当前值
    private float mProgress;
    //已缓存显示
    private float mCacheProgress;
    // 进度条高度
    private int mTrackSize;
    // 已选中进度条高度
    private int mSecondTrackSize;
    //缓存条的高度size
    private int mCacheTrackSize;


    // 滑动点,半径
    private int mThumbRadius;
    //是否显示滑动点
    private boolean isShowThumb;
    // 标记点,半径
    private int mMarkRadius;
    // 拖动时的 滑动点半径
    private int mThumbRadiusOnDragging;
    // 第一轨迹颜色
    private int mTrackColor;
    // 第二轨迹颜色
    private int mSecondTrackColor;
    //缓存轨迹颜色
    private int mCacheTrackColor;
    //标记点颜色
    private int mSectionMarkColor;

    // 滑动点的颜色
    private int mThumbColor;
    // 是否显示节点标记
    private boolean isShowSectionMark;

    private ArrayList<Float> mCustomArrayFloat;

    //动画持续时间
    private long mAnimDuration;

    private boolean isTouchToSeek; // touch anywhere on track to quickly seek

    private float mDelta; // max - min
    private float mThumbCenterX; // X coordinate of thumb's center
    private float mTrackLength; // pixel length of whole track
    //每一份的宽度  ,假如(progress max是 999的话 mPerProgressWidth的值是,总宽度/999)
    private float mPerProgressWidth;

    private boolean isThumbOnDragging; // is thumb on dragging or not

    private OnProgressChangedListener mProgressListener; // progress changing listener
    //轨道的左边距离本view的间距
    private float mLeft;
    //轨道的右边距离本view的间距
    private float mRight;
    private Paint mPaint;
    private Rect mRectText;

    private float mThumbBgAlpha; //  alpha of thumb shadow
    private float mThumbRatio; // ratio of thumb shadow
    private boolean isShowThumbShadow;

    public MarkSeekBar(Context context) {
        this(context, null);
    }

    public MarkSeekBar(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public MarkSeekBar(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MarkSeekBar, defStyleAttr, 0);
        mMin = a.getFloat(R.styleable.MarkSeekBar_msb_min, 0.0f);
        mMax = a.getFloat(R.styleable.MarkSeekBar_msb_max, 100.0f);
        mProgress = a.getFloat(R.styleable.MarkSeekBar_msb_progress, mMin);
        mTrackSize = a.getDimensionPixelSize(R.styleable.MarkSeekBar_msb_track_size, dp2px(2));
        mSecondTrackSize = a.getDimensionPixelSize(R.styleable.MarkSeekBar_msb_second_track_size, mTrackSize + dp2px(2));
        mCacheTrackSize = a.getDimensionPixelSize(R.styleable.MarkSeekBar_msb_cache_track_size, mTrackSize + dp2px(2));
        mThumbRadius = a.getDimensionPixelSize(R.styleable.MarkSeekBar_msb_thumb_radius, mSecondTrackSize + dp2px(2));
        mMarkRadius = a.getDimensionPixelSize(R.styleable.MarkSeekBar_msb_mark_radius, 0);
        mThumbRadiusOnDragging = a.getDimensionPixelSize(R.styleable.MarkSeekBar_msb_thumb_radius_on_dragging, mSecondTrackSize * 2);
        mTrackColor = a.getColor(R.styleable.MarkSeekBar_msb_track_color, ContextCompat.getColor(context, R.color.colorPrimary));
        mSecondTrackColor = a.getColor(R.styleable.MarkSeekBar_msb_second_track_color, ContextCompat.getColor(context, R.color.colorAccent));
        mCacheTrackColor = a.getColor(R.styleable.MarkSeekBar_msb_cache_track_color, ContextCompat.getColor(context, R.color.colorAccent));
        mThumbColor = a.getColor(R.styleable.MarkSeekBar_msb_thumb_color, mSecondTrackColor);
        isShowSectionMark = a.getBoolean(R.styleable.MarkSeekBar_msb_show_section_mark, false);
        isShowThumb = a.getBoolean(R.styleable.MarkSeekBar_msb_is_show_thumb, true);
        mSectionMarkColor = a.getColor(R.styleable.MarkSeekBar_msb_section_mark_color, Color.WHITE);
        int duration = a.getInteger(R.styleable.MarkSeekBar_msb_anim_duration, -1);
        mAnimDuration = duration < 0 ? 200 : duration;
        isTouchToSeek = a.getBoolean(R.styleable.MarkSeekBar_msb_touch_to_seek, false);

        mThumbBgAlpha = a.getFloat(R.styleable.MarkSeekBar_msb_thumb_bg_alpha, 0.2f);
        mThumbRatio = a.getFloat(R.styleable.MarkSeekBar_msb_thumb_ratio, 0.7f);
        isShowThumbShadow = a.getBoolean(R.styleable.MarkSeekBar_msb_show_thumb_shadow, false);
        a.recycle();

        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setStrokeCap(Paint.Cap.ROUND);
        mPaint.setTextAlign(Paint.Align.CENTER);

        mRectText = new Rect();
        initConfigByPriority();
    }


    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 (mCacheProgress < mMin) {
            mCacheProgress = mMin;
        }
        if (mCacheProgress > mMax) {
            mCacheProgress = mMax;
        }

        if (mSecondTrackSize < mTrackSize) {
            mSecondTrackSize = mTrackSize + dp2px(2);
        }
        if (mThumbRadius <= mSecondTrackSize) {
            mThumbRadius = mSecondTrackSize + dp2px(2);
        }
        if (mMarkRadius == 0) {
            mMarkRadius = mSecondTrackSize + dp2px(2);
        }
        if (mThumbRadiusOnDragging <= mSecondTrackSize) {
            mThumbRadiusOnDragging = mSecondTrackSize * 2;
        }
        mDelta = mMax - mMin;

        setProgress(mProgress);

    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int height = mThumbRadiusOnDragging * 2; // 默认高度为拖动时thumb圆的直径
        setMeasuredDimension(resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec), height);

        mLeft = getPaddingLeft() + mThumbRadiusOnDragging;
        mRight = getMeasuredWidth() - getPaddingRight() - mThumbRadiusOnDragging;

        //轨迹长度
        mTrackLength = mRight - mLeft;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        float xLeft = getPaddingLeft();
        float xRight = getMeasuredWidth() - getPaddingRight();
        float yTop = getPaddingTop() + mThumbRadiusOnDragging;

        //轨迹长度/总size    得到的结果
        mPerProgressWidth = mTrackLength * 1f / mMax;
        if (isShowThumb) {
            xLeft += mThumbRadiusOnDragging;
            xRight -= mThumbRadiusOnDragging;
        }

        if (!isThumbOnDragging) {
            mThumbCenterX = mTrackLength / mDelta * (mProgress - mMin) + xLeft;
        }

        // draw track
        mPaint.setColor(mSecondTrackColor);
        mPaint.setStrokeWidth(mSecondTrackSize);
        canvas.drawLine(xLeft, yTop, mThumbCenterX, yTop, mPaint);

        // draw second track
        mPaint.setColor(mTrackColor);
        mPaint.setStrokeWidth(mTrackSize);
        canvas.drawLine(mThumbCenterX, yTop, xRight, yTop, mPaint);

        //画缓存进度条
        if (mCacheProgress * mPerProgressWidth > mThumbCenterX) {
            mPaint.setColor(mCacheTrackColor);
            mPaint.setStrokeWidth(mCacheTrackSize);
            canvas.drawLine(mThumbCenterX, yTop, xLeft + mCacheProgress * mPerProgressWidth, yTop, mPaint);
        }

        //boolean conditionInterval = mSectionCount % 2 == 0;
        boolean conditionInterval = true;

        // draw sectionMark
        if (isShowSectionMark && mCustomArrayFloat != null) {
            drawCustomMark(canvas, xLeft, yTop, conditionInterval);
        }

        // draw thumb
        mPaint.setColor(mThumbColor);
        //draw thumb shadow
        if (isShowThumbShadow && isShowThumb) {
            canvas.drawCircle(mThumbCenterX, yTop, isThumbOnDragging ? mThumbRadiusOnDragging * mThumbRatio : mThumbRadius * mThumbRatio, mPaint);
            mPaint.setColor(getColorWithAlpha(mThumbColor, mThumbBgAlpha));
        }
        //Paint paint = new Paint();
        //Shader shader = new RadialGradient(mThumbCenterX, yTop, isThumbOnDragging ? mThumbRadiusOnDragging : mThumbRadius, mThumbColor, getColorWithAlpha(mThumbColor, mThumbBgAlpha), Shader.TileMode.CLAMP);
        //paint.setShader(shader);
        if (isShowThumb) {
            canvas.drawCircle(mThumbCenterX, yTop, isThumbOnDragging ? mThumbRadiusOnDragging : mThumbRadius, mPaint);
        }
    }

    /**
     * 画自定义节点标记
     *
     * @param canvas
     * @param xLeft
     * @param yTop
     * @param conditionInterval 条件区间,外界传过来的总是true
     */
    private void drawCustomMark(Canvas canvas, float xLeft, float yTop, boolean conditionInterval) {
        // 交汇点x轴坐标
        float junction = mTrackLength / mDelta * Math.abs(mProgress - mMin) + mLeft;
        //设置文本画笔的文字大小及边框
        mPaint.getTextBounds("0123456789", 0, "0123456789".length(), mRectText); // compute solid height

        float x_;
        for (int i = 0; i < mCustomArrayFloat.size(); i++) {
            x_ = xLeft + mCustomArrayFloat.get(i) * mPerProgressWidth;
            mPaint.setColor(x_ <= junction ? mSecondTrackColor : mSectionMarkColor);
            // sectionMark
            canvas.drawCircle(x_, yTop, mMarkRadius, mPaint);
        }
    }


    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        post(new Runnable() {
            @Override
            public void run() {
                requestLayout();
            }
        });
    }

    float dx;

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (!isEnabled()) {
            return false;
        }
        switch (event.getActionMasked()) {
            case MotionEvent.ACTION_DOWN:
                getParent().requestDisallowInterceptTouchEvent(true);
                isThumbOnDragging = isThumbTouched(event);
                //拖动中
                if (isThumbOnDragging) {
                    invalidate();
                } else if (isTouchToSeek && isTrackTouched(event)) {
                    //非拖动中允许点击到指定位置的话则
                    isThumbOnDragging = true;
                    mThumbCenterX = event.getX();
                    if (mThumbCenterX < mLeft) {
                        mThumbCenterX = mLeft;
                    }
                    if (mThumbCenterX > mRight) {
                        mThumbCenterX = mRight;
                    }
                    mProgress = (mThumbCenterX - mLeft) * mDelta / mTrackLength + mMin;
                    invalidate();
                }

                dx = mThumbCenterX - event.getX();
                break;
            case MotionEvent.ACTION_MOVE:
                if (isThumbOnDragging) {
                    mThumbCenterX = event.getX() + dx;
                    if (mThumbCenterX < mLeft) {
                        mThumbCenterX = mLeft;
                    }
                    if (mThumbCenterX > mRight) {
                        mThumbCenterX = mRight;
                    }
                    mProgress = (mThumbCenterX - mLeft) * mDelta / mTrackLength + mMin;
                    invalidate();

                    if (mProgressListener != null) {
                        mProgressListener.onProgressChanged(this, getProgress(), true);
                    }
                }

                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                getParent().requestDisallowInterceptTouchEvent(false);
                isThumbOnDragging = false;
                invalidate();
                if (mProgressListener != null) {
                    mProgressListener.getProgressOnActionUp(this, getProgress());
                }
                break;
        }
        return isThumbOnDragging || isTouchToSeek || super.onTouchEvent(event);
    }

    /**
     * 计算新的透明度颜色
     *
     * @param color 旧颜色
     * @param ratio 透明度系数
     */
    public int getColorWithAlpha(int color, float ratio) {
        int newColor = 0;
        int alpha = Math.round(Color.alpha(color) * ratio);
        int r = Color.red(color);
        int g = Color.green(color);
        int b = Color.blue(color);
        newColor = Color.argb(alpha, r, g, b);
        return newColor;
    }

    /**
     * Detect effective touch of thumb
     */
    private boolean isThumbTouched(MotionEvent event) {
        if (!isEnabled()) {
            return false;
        }
        float mCircleR = isThumbOnDragging ? mThumbRadiusOnDragging : mThumbRadius;
        float x = mTrackLength / mDelta * (mProgress - mMin) + mLeft;
        float y = getMeasuredHeight() / 2f;
        return (event.getX() - x) * (event.getX() - x) + (event.getY() - y) * (event.getY() - y)
                <= (mLeft + mCircleR) * (mLeft + mCircleR);
    }

    /**
     * Detect effective touch of track
     */
    private boolean isTrackTouched(MotionEvent event) {
        return isEnabled() && event.getX() >= getPaddingLeft() && event.getX() <= getMeasuredWidth() - getPaddingRight()
                && event.getY() >= getPaddingTop() && event.getY() <= getMeasuredHeight() - getPaddingBottom();
    }


    public float getMin() {
        return mMin;
    }

    public float getMax() {
        return mMax;
    }

    public void setProgress(float progress) {
        mProgress = progress;
//        if (mProgressListener != null) {
//            mProgressListener.onProgressChanged(this, getProgress(), false);
//            mProgressListener.getProgressOnFinally(this, getProgress(), false);
//        }
        postInvalidate();
    }

    public void setMax(float max) {
        this.mMax = max;
        //设置max后初始化 delta
        mDelta = mMax - mMin;
        postInvalidate();
    }

    public void setCustomArrayFloat(ArrayList<Float> customArrayFloat) {
        mCustomArrayFloat = customArrayFloat;
        postInvalidate();
    }

    public void setCacheProgress(float cacheProgress) {
        mCacheProgress = cacheProgress;
        postInvalidate();
    }

    public float getProgress() {
        return mProgress;
    }

    public void setOnProgressChangedListener(OnProgressChangedListener onProgressChangedListener) {
        mProgressListener = onProgressChangedListener;
    }


    @Override
    protected Parcelable onSaveInstanceState() {
        Bundle bundle = new Bundle();
        bundle.putParcelable("save_instance", super.onSaveInstanceState());
        bundle.putFloat("progress", mProgress);
        return bundle;
    }

    @Override
    protected void onRestoreInstanceState(Parcelable state) {
        if (state instanceof Bundle) {
            Bundle bundle = (Bundle) state;
            mProgress = bundle.getFloat("progress");
            super.onRestoreInstanceState(bundle.getParcelable("save_instance"));
            setProgress(mProgress);
            return;
        }
        super.onRestoreInstanceState(state);
    }

    private String float2String(float value) {
        return String.valueOf(formatFloat(value));
    }

    private float formatFloat(float value) {
        BigDecimal bigDecimal = BigDecimal.valueOf(value);
        return bigDecimal.setScale(1, BigDecimal.ROUND_HALF_UP).floatValue();
    }

    /**
     * 进度监听 onChanged, onActionUp, onFinally
     */
    public interface OnProgressChangedListener {

        //进度条在拖动中,或执行动画中改变回调
        void onProgressChanged(MarkSeekBar seekBar, float progress, boolean fromUser);

        //手指抬起后改变进度条
        void getProgressOnActionUp(MarkSeekBar seekBar, float progress);
    }
}

seekBar的命名空间 放到attrs中

<declare-styleable name="MarkSeekBar">
        <attr name="msb_min" format="float|reference"/> <!--min < max, default: 0.0f-->
        <attr name="msb_max" format="float|reference"/> <!--min < max, default: 100.0f-->
        <attr name="msb_progress" format="float|reference"/> <!--real time progress value, default: min-->
        <!--height of left-track(on the left of thumb), default: 2dp higher than right-track's height-->
        <attr name="msb_track_size" format="dimension|reference"/> <!--height of right-track(on the right of thumb), default: 2dp-->
        <attr name="msb_second_track_size" format="dimension|reference"/>
        <attr name="msb_cache_track_size" format="dimension|reference"/>
        <attr name="msb_thumb_radius" format="dimension|reference"/> <!--radius of thumb, default: 2dp higher than left-track's height-->
        <attr name="msb_mark_radius" format="dimension|reference"/> <!--radius of thumb, default: 2dp higher than left-track's height-->
        <attr name="msb_thumb_radius_on_dragging" format="dimension|reference"/>
        <!--radius of thumb when be dragging, default: 2 times of left-track's height-->
        <attr name="msb_track_color" format="color|reference"/> <!--color of right-track, default: R.color.colorPrimary-->
        <attr name="msb_second_track_color" format="color|reference"/> <!--color of left-track, default: R.color.colorAccent-->
        <attr name="msb_cache_track_color" format="color|reference"/> <!--color of left-track, default: R.color.colorAccent-->
        <attr name="msb_thumb_color" format="color|reference"/> <!--color of thumb, default: same as left-track's color-->
        <attr name="msb_is_show_thumb" format="boolean"/> <!--color of thumb, default: same as left-track's color-->
        <attr name="msb_show_section_mark" format="boolean"/> <!--show demarcation points or not, default: false-->
        <attr name="msb_section_mark_color" format="color|reference"/> <!--color of left-track, default: R.color.colorAccent-->
        <attr name="msb_anim_duration" format="integer"/>
        <!--text position of section-text relative to track, sides, bottom_sides, below_section_mark, default: sides-->
        <attr name="msb_touch_to_seek" format="boolean"/> <!--touch anywhere on track to quickly seek, default: false-->
        <attr name="msb_thumb_ratio" format="float|reference"/> <!--0.0f-1.0f, default: 0.7f-->
        <attr name="msb_thumb_bg_alpha" format="float|reference"/> <!--0.0f-1.0f, default: 0.2f-->
        <attr name="msb_show_thumb_shadow" format="boolean"/> <!--0.0f-1.0f, default: false-->
    </declare-styleable>