View hScreenController.kt in landscape

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 ()) // 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)} /** * 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 }Copy the code

View layout player_h_controller.xml in landscape mode

<? The 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.5 dp" 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 = "2 dp" app: msb_mark_radius = "2.5 dp" 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>Copy the code

Loading 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, state Richard armitage nim. Loading_animation) / / 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)} /** * The server timestamp returns before Loading * does not display speed */ 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() } }Copy the code

Loading view layout player_loading_view.xml

<? The 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=" # CCFFFFFF "Android :textSize="10dp" Android :visibility="gone" /> </LinearLayout> </LinearLayout>Copy the code

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={} =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 Var tempX= touchx-startx if(tempX (tempX)>5){val percentProgress = getStepPosition(tempX) CurrentPosition = getSeekToPosition(percentProgress, mProgress? :0, mDuration? :0) // Update text text (currentPosition? // Update the progress bar 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? */ 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}} /** ** getStepPosition(tempX: Float): Int { return (tempX.toInt()*100/context.resources.displayMetrics.widthPixels) } fun hideMonitorView() { visibility = GONE } }Copy the code

Layout of progress view player_seek_monitor_view.xml

<? The 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>Copy the code

The parent class for adjusting volume and brightness, 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); // text 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); }}Copy the code

The animation framework 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, "Float ", 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, ", 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, "Float ", 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 ()}); "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.0 f (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.0 f, 0.9 f); 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.9 f, 0.0 f); ofFloat.setInterpolator(new DecelerateInterpolator()); ofFloat.setDuration(mDurationTime); ofFloat.addListener(new ViewGoneAnimatorListener(view)); ofFloat.start(); return ofFloat; }}Copy the code

Volume adjustment 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); }}Copy the code

Brightness adjustment 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.0 f); 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); }}Copy the code

Network playback error 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) } }Copy the code

Play error layout view_play_error.xml

<? The 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! Video load failed "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>Copy the code

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 }Copy the code

Player_play_complete_portrait_view.xml

<? The 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" /> <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=" replay "/> <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" </LinearLayout> </LinearLayout> </LinearLayout>Copy the code

Player 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);
    }
}
Copy the code

The seekBar namespace is placed in attrs

<declare-styleable name="MarkSeekBar"> <attr name="msb_min" format="float|reference"/> <! < Max, min, default: 0.0 f - > < attr name = "msb_max" format = "float | reference" / > <! < Max, min, default: 100.0 f - > < 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.0 f to 1.0 f, default: 0.7 f - > < attr name = "msb_thumb_bg_alpha" format = "float | reference" / > <! <attr name="msb_show_thumb_shadow" format=" Boolean "/> <! -- 0.0 f to 1.0 f, default: false -- > < / declare - styleable >Copy the code