网站建设是属于虚拟产品吗,做网站没有活,如何制作h5海报,手机网站大全下载Android音视频剪辑器自定义View实战#xff01; - 掘金
/*** Created by zhouxuming on 2023/3/30** descr 音视频剪辑器*/
public class AudioViewEditor extends View {//进度文本显示格式-数字格式public static final int HINT_FORMAT_NUMBER 0;//进度文本显示格式-时间…Android音视频剪辑器自定义View实战 - 掘金
/*** Created by zhouxuming on 2023/3/30** descr 音视频剪辑器*/
public class AudioViewEditor extends View {//进度文本显示格式-数字格式public static final int HINT_FORMAT_NUMBER 0;//进度文本显示格式-时间格式public static final int HINT_FORMAT_TIME 1;private final Paint mPaint new Paint();//空间最小宽度private final int MIN_WIDTH 200;private final float playControlLeft 10; //播控实际左边界private final float playControlRight 80; //播控实际右边界//滑块bitmapprivate Bitmap mThumbImage;//progress bar 选中背景
// private Bitmap mProgressBarSelBg;private Bitmap mMaxThumbImage;private Bitmap mMinThumbImage;//progress bar 背景private Bitmap mProgressBarBg;private float mThumbWidth;private float mThumbHalfWidth; //触摸响应宽度的一半private float mThumbHalfHeight;//seekbar 进度条高度private float mProgressBarHeight;//宽度左右paddingprivate float mWidthPadding;//最小值绝对private float mAbsoluteMinValue;//最大值绝对private float mAbsoluteMaxValue;//已选标准占滑动条百分比最小值private double mPercentSelectedMinValue 0d;//已选标准占滑动条百分比最大值private double mPercentSelectedMaxValue 1d;//当前事件处理的thumb滑块private Thumb mPressedThumb null;//滑块事件private ThumbListener mThumbListener;private RectF mProgressBarRect;private RectF mProgressBarSelRect;//是否可以滑动private boolean mIsEnable true;//最大值和最小值之间要求的最小范围绝对值private float mBetweenAbsoluteValue;//文字格式private int mProgressTextFormat;//文本高度private int mWordHeight;//文本字体大小private float mWordSize;private float mStartMinPercent;private float mStartMaxPercent;private boolean fixedMode; //固定模式private Paint cursorPaint;private Paint borderPaint;//播控按钮部分逻辑private Paint playControlPaint;private boolean isPlay true; //播控状态private Bitmap playResumeBitmap;private Bitmap playPauseBitmap;private PlayerControlListener playerControlListener;private float cur;// 光标坐标private float pre;// 100 份每一份的偏移量private float min;//起始坐标private float max;//最大坐标private boolean isFirst true;public AudioViewEditor(Context context) {super(context);}public AudioViewEditor(Context context, AttributeSet attrs) {super(context, attrs);TypedArray a getContext().obtainStyledAttributes(attrs, R.styleable.AudioViewEditor, 0, 0);mAbsoluteMinValue a.getFloat(R.styleable.AudioViewEditor_absoluteMin, (float) 0.0);mAbsoluteMaxValue a.getFloat(R.styleable.AudioViewEditor_absolutemMax, (float) 100.0);mStartMinPercent a.getFloat(R.styleable.AudioViewEditor_startMinPercent, 0);mStartMaxPercent a.getFloat(R.styleable.AudioViewEditor_startMaxPercent, 1);mThumbImage BitmapFactory.decodeResource(getResources(), a.getResourceId(R.styleable.AudioViewEditor_thumbImage, R.drawable.drag_left_bar));mMaxThumbImage BitmapFactory.decodeResource(getResources(), R.drawable.drag_right_bar);mProgressBarBg BitmapFactory.decodeResource(getResources(), a.getResourceId(R.styleable.AudioViewEditor_progressBarBg, R.drawable.seekbar_bg));playPauseBitmap BitmapFactory.decodeResource(getResources(), R.drawable.play_control_pause);playResumeBitmap BitmapFactory.decodeResource(getResources(), R.drawable.play_control_resume);// mProgressBarSelBg BitmapFactory.decodeResource(getResources(), a.getResourceId(R.styleable.CustomRangeSeekBar_progressBarSelBg, R.mipmap.seekbar_sel_bg));mBetweenAbsoluteValue a.getFloat(R.styleable.AudioViewEditor_betweenAbsoluteValue, 0);mProgressTextFormat a.getInt(R.styleable.AudioViewEditor_progressTextFormat, HINT_FORMAT_NUMBER);mWordSize a.getDimension(R.styleable.AudioViewEditor_progressTextSize, dp2px(context, 16));mPaint.setTextSize(mWordSize);mThumbWidth mThumbImage.getWidth();mThumbHalfWidth 0.5f * mThumbWidth;mThumbHalfHeight 0.5f * mThumbImage.getHeight();
// mProgressBarHeight 0.3f * mThumbHalfHeight;mProgressBarHeight mThumbImage.getHeight();//TOOD 提供定义attrmWidthPadding mThumbHalfHeight;mWidthPadding playControlRight;//为了加左右侧播控按钮, 特地添加出来的空间Paint.FontMetrics metrics mPaint.getFontMetrics();mWordHeight (int) (metrics.descent - metrics.ascent);/*光标*/cursorPaint new Paint();cursorPaint.setAntiAlias(true);cursorPaint.setColor(Color.WHITE);borderPaint new Paint();borderPaint.setAntiAlias(true);borderPaint.setColor(Color.parseColor(#DBAE6A));playControlPaint new Paint();playControlPaint.setAntiAlias(true);playControlPaint.setColor(Color.parseColor(#1E1F21));restorePercentSelectedMinValue();restorePercentSelectedMaxValue();a.recycle();}/*** 格式化毫秒-00:00*/private static String formatSecondTime(int millisecond) {if (millisecond 0) {return 00:00;}int second millisecond / 1000;int m second / 60;int s second % 60;if (m 60) {int hour m / 60;int minute m % 60;return hour : (minute 9 ? minute : 0 minute) : (s 9 ? s : 0 s);} else {return (m 9 ? m : 0 m) : (s 9 ? s : 0 s);}}/*** 将dip或dp值转换为px值保证尺寸大小不变** param dipValue DisplayMetrics类中属性density* return*/public static int dp2px(Context context, float dipValue) {final float scale context.getResources().getDisplayMetrics().density;return (int) (dipValue * scale 0.5f);}/*** 还原min滑块到初始值*/public void restorePercentSelectedMinValue() {setPercentSelectedMinValue(mStartMinPercent);}/*** 还原max滑块到初始值*/public void restorePercentSelectedMaxValue() {setPercentSelectedMaxValue(mStartMaxPercent);}Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);mProgressBarRect new RectF(mWidthPadding, mWordHeight 0.5f * (h - mWordHeight - mProgressBarHeight),w - mWidthPadding, mWordHeight 0.5f * (h - mWordHeight mProgressBarHeight));mProgressBarSelRect new RectF(mProgressBarRect);}/*** 设置seekbar 是否接收事件** param enabled*/Overridepublic void setEnabled(boolean enabled) {super.setEnabled(enabled);this.mIsEnable enabled;}/*** 返回被选择的最小值(绝对值)** return The currently selected min value.*/public float getSelectedAbsoluteMinValue() {return percentToAbsoluteValue(mPercentSelectedMinValue);}/*** 设置被选择的最小值绝对值** param value 最小值的绝对值* return 如果最小值与最大值的最小间距达到阈值返回false,正常返回true*/public boolean setSelectedAbsoluteMinValue(float value) {boolean status true;if (0 (mAbsoluteMaxValue - mAbsoluteMinValue)) {setPercentSelectedMinValue(0d);} else {float maxValue percentToAbsoluteValue(mPercentSelectedMaxValue);if (mBetweenAbsoluteValue 0 maxValue - value mBetweenAbsoluteValue) {value new Float(maxValue - mBetweenAbsoluteValue);status false;}if (maxValue - value 0) {status false;value maxValue;}setPercentSelectedMinValue(absoluteValueToPercent(value));}return status;}public float getAbsoluteMaxValue() {return mAbsoluteMaxValue;}public void setAbsoluteMaxValue(double maxvalue) {this.mAbsoluteMaxValue new Float(maxvalue);}/*** 返回被选择的最大值绝对值.*/public float getSelectedAbsoluteMaxValue() {return percentToAbsoluteValue(mPercentSelectedMaxValue);}/*** 设置被选择的最大值绝对值** param value*/public boolean setSelectedAbsoluteMaxValue(float value) {boolean status true;if (0 (mAbsoluteMaxValue - mAbsoluteMinValue)) {setPercentSelectedMaxValue(1d);} else {float minValue percentToAbsoluteValue(mPercentSelectedMinValue);if (mBetweenAbsoluteValue 0 value - minValue mBetweenAbsoluteValue) {value new Float(minValue mBetweenAbsoluteValue);status false;}if (value - minValue 0) {status false;value minValue;}setPercentSelectedMaxValue(absoluteValueToPercent(value));}return status;}Overridepublic boolean onTouchEvent(MotionEvent event) {if (!mIsEnable)return true;switch (event.getAction()) {case MotionEvent.ACTION_DOWN:if (isTouchPlayControl(event.getX())) {isPlay !isPlay;playerControlListener.onPlayerControl(isPlay);invalidate();return true;}if (mPressedThumb null isInCursorRange(event.getX(), cur)) {
// if (mThumbListener ! null){
// mThumbListener.onCursor(cur);
// }} else {mPressedThumb evalPressedThumb(event.getX());if (Thumb.MIN.equals(mPressedThumb)) {if (mThumbListener ! null)mThumbListener.onClickMinThumb(getSelectedAbsoluteMaxValue(), getSelectedAbsoluteMinValue());}if (Thumb.MAX.equals(mPressedThumb)) {if (mThumbListener ! null)mThumbListener.onClickMaxThumb();}}invalidate();//Intercept parent TouchEventif (getParent() ! null) {getParent().requestDisallowInterceptTouchEvent(true);}break;case MotionEvent.ACTION_MOVE:if (mPressedThumb null isInCursorRange(event.getX(), cur)) {isMoving true;float eventX event.getX();if (eventX percentToScreen(mPercentSelectedMaxValue)) {eventX percentToScreen(mPercentSelectedMaxValue);} else if (eventX percentToScreen(mPercentSelectedMinValue)) {eventX percentToScreen(mPercentSelectedMinValue);}cur eventX;if (mThumbListener ! null) {mThumbListener.onCursorMove(percentToAbsoluteValue(screenToPercent(cur)));}invalidate();} else if (mPressedThumb ! null) {float eventX event.getX();float maxValue percentToAbsoluteValue(mPercentSelectedMaxValue);float minValue percentToAbsoluteValue(mPercentSelectedMinValue);float eventValue percentToAbsoluteValue(screenToPercent(eventX));if (Thumb.MIN.equals(mPressedThumb)) {minValue eventValue;if (mBetweenAbsoluteValue 0 maxValue - minValue mBetweenAbsoluteValue) {minValue new Float((maxValue - mBetweenAbsoluteValue));}
// setPercentSelectedMinValue(screenToPercent(event.getX()));if (isFixedMode()) {mPercentSelectedMaxValue Math.max(0d, Math.min(1d, Math.max(absoluteValueToPercent(eventValue (maxValue - minValue)), mPercentSelectedMinValue)));}if (cur percentToScreen(mPercentSelectedMinValue)) {//防止光标静态越界cur percentToScreen(mPercentSelectedMinValue);}setPercentSelectedMinValue(absoluteValueToPercent(minValue));if (mThumbListener ! null)mThumbListener.onMinMove(getSelectedAbsoluteMaxValue(), getSelectedAbsoluteMinValue());} else if (Thumb.MAX.equals(mPressedThumb)) {maxValue eventValue;if (mBetweenAbsoluteValue 0 maxValue - minValue mBetweenAbsoluteValue) {maxValue new Float(minValue mBetweenAbsoluteValue);}
// setPercentSelectedMaxValue(screenToPercent(event.getX()));if (isFixedMode()) {mPercentSelectedMinValue Math.max(0d, Math.min(1d, Math.min(absoluteValueToPercent(eventValue - (maxValue - minValue)), mPercentSelectedMaxValue)));}if (cur percentToScreen(mPercentSelectedMaxValue)) {//防止光标静态越界cur percentToScreen(mPercentSelectedMaxValue);}setPercentSelectedMaxValue(absoluteValueToPercent(maxValue));if (mThumbListener ! null)mThumbListener.onMaxMove(getSelectedAbsoluteMaxValue(), getSelectedAbsoluteMinValue());}}//Intercept parent TouchEventif (getParent() ! null) {getParent().requestDisallowInterceptTouchEvent(true);}break;case MotionEvent.ACTION_UP:if (isMoving) {if (mThumbListener ! null) {mThumbListener.onCursorUp(percentToAbsoluteValue(screenToPercent(cur)));}isMoving false;}if (Thumb.MIN.equals(mPressedThumb)) {if (mThumbListener ! null)mThumbListener.onUpMinThumb(getSelectedAbsoluteMaxValue(), getSelectedAbsoluteMinValue());}if (Thumb.MAX.equals(mPressedThumb)) {if (mThumbListener ! null)mThumbListener.onUpMaxThumb(getSelectedAbsoluteMaxValue(), getSelectedAbsoluteMinValue());}//Intercept parent TouchEventif (getParent() ! null) {getParent().requestDisallowInterceptTouchEvent(true);}break;case MotionEvent.ACTION_CANCEL:if (Thumb.MIN.equals(mPressedThumb)) {if (mThumbListener ! null)mThumbListener.onUpMinThumb(getSelectedAbsoluteMaxValue(), getSelectedAbsoluteMinValue());}if (Thumb.MAX.equals(mPressedThumb)) {if (mThumbListener ! null)mThumbListener.onUpMaxThumb(getSelectedAbsoluteMaxValue(), getSelectedAbsoluteMinValue());}mPressedThumb null;//Intercept parent TouchEventif (getParent() ! null) {getParent().requestDisallowInterceptTouchEvent(true);}break;}return true;}private boolean isTouchPlayControl(float eventX) {if (eventX playControlLeft eventX playControlRight) {return true;}return false;}Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {int width MIN_WIDTH;if (MeasureSpec.UNSPECIFIED ! MeasureSpec.getMode(widthMeasureSpec)) {width MeasureSpec.getSize(widthMeasureSpec);}int height mThumbImage.getHeight() mWordHeight * 2;if (MeasureSpec.UNSPECIFIED ! MeasureSpec.getMode(heightMeasureSpec)) {height Math.min(height, MeasureSpec.getSize(heightMeasureSpec));}setMeasuredDimension(width, height);}Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);// draw seek bar background linemPaint.setStyle(Paint.Style.FILL);drawPlayControl(canvas);canvas.drawBitmap(mProgressBarBg, null, mProgressBarRect, mPaint);// draw seek bar active range linemProgressBarSelRect.left percentToScreen(mPercentSelectedMinValue);mProgressBarSelRect.right percentToScreen(mPercentSelectedMaxValue);//canvas.drawBitmap(mProgressBarSelBg, mWidthPadding, 0.5f * (getHeight() - mProgressBarHeight), mPaint);// canvas.drawBitmap(mProgressBarSelBg, null, mProgressBarSelRect, mPaint); //原中部选中进度// draw minimum thumbdrawThumb(percentToScreen(mPercentSelectedMinValue), Thumb.MIN.equals(mPressedThumb), canvas, false);// draw maximum thumbdrawThumb(percentToScreen(mPercentSelectedMaxValue), Thumb.MAX.equals(mPressedThumb), canvas, true);mPaint.setColor(Color.rgb(255, 165, 0));mPaint.setAntiAlias(true);
// mPaint.setTextSize(DensityUtils.dp2px(getContext(), 16));drawThumbMinText(percentToScreen(mPercentSelectedMinValue), getSelectedAbsoluteMinValue(), canvas);drawThumbMaxText(percentToScreen(mPercentSelectedMaxValue), getSelectedAbsoluteMaxValue(), canvas);drawBorder(canvas);drawCursor(canvas);}private void drawPlayControl(Canvas canvas) {canvas.drawRoundRect(playControlLeft, mProgressBarRect.top, playControlRight mThumbWidth mThumbHalfWidth, mProgressBarRect.bottom, 5, 5, playControlPaint);Bitmap targetBitmap isPlay ? playPauseBitmap : playResumeBitmap;//x轴距离未计算准确 y轴正确canvas.drawBitmap(targetBitmap, (playControlLeft (playControlRight - playControlLeft) / 2) - mThumbHalfWidth (targetBitmap.getWidth() 1), mProgressBarRect.top (mProgressBarRect.bottom - mProgressBarRect.top) / 2 - (targetBitmap.getHeight() 1), playControlPaint);}private void drawBorder(Canvas canvas) {//topfloat borderLeft mProgressBarSelRect.left;float borderRight mProgressBarSelRect.right;canvas.drawRect(borderLeft - 1, mProgressBarRect.top, borderRight 1, mProgressBarRect.top 10, borderPaint);//bottomcanvas.drawRect(borderLeft - 1, mProgressBarRect.bottom, borderRight 1, mProgressBarRect.bottom - 10, borderPaint);}private void drawCursor(Canvas canvas) {min percentToScreen(mPercentSelectedMinValue);//开始坐标max percentToScreen(mPercentSelectedMaxValue);//终点坐标pre (getWidth() - 2 * mWidthPadding) / 1000; //每一份的坐标if (isFirst) {cur min;isFirst false;}canvas.drawRect(cur - 2, mProgressBarRect.top 5, cur 2, mProgressBarRect.bottom - 5, cursorPaint);}//启动播放线程检查 ptspublic void startMove() {new Thread(new Runnable() {Overridepublic void run() {while (true) {if (isPlay) {long pts playerCallback ! null ? playerCallback.getCurrentPosition() : 0;updatePTS(pts);}try {Thread.sleep(30);} catch (InterruptedException e) {e.printStackTrace();}}}}).start();}/*** 根据播放器 pts 控制游标进度** param pts*/public void updatePTS(float pts) {if (isMoving) {return;}if (pts 0) {double v absoluteValueToPercent(pts);cur percentToScreen(v);if (cur max || cur min) {cur min;}invalidate();}}public boolean isPlay() {return isPlay;}public void setPlay(boolean play) {isPlay play;}private boolean isMoving false;Overrideprotected Parcelable onSaveInstanceState() {Bundle bundle new Bundle();bundle.putParcelable(SUPER, super.onSaveInstanceState());bundle.putDouble(MIN, mPercentSelectedMinValue);bundle.putDouble(MAX, mPercentSelectedMaxValue);return bundle;}Overrideprotected void onRestoreInstanceState(Parcelable parcel) {Bundle bundle (Bundle) parcel;super.onRestoreInstanceState(bundle.getParcelable(SUPER));mPercentSelectedMinValue bundle.getDouble(MIN);mPercentSelectedMaxValue bundle.getDouble(MAX);}/*** Draws the normal resp. pressed thumb image on specified x-coordinate.** param screenCoord The x-coordinate in screen space where to draw the image.* param pressed Is the thumb currently in pressed state?* param canvas The canvas to draw upon.*/private void drawThumb(float screenCoord, boolean pressed, Canvas canvas, boolean isMax) {//基准点 bar 居中位置
// canvas.drawBitmap(isMax ? mMaxThumbImage : mThumbImage, screenCoord - mThumbHalfWidth, (mWordHeight 0.5f * (getHeight() - mWordHeight) - mThumbHalfHeight), mPaint);//基准点顶两边位置canvas.drawBitmap(isMax ? mMaxThumbImage : mThumbImage, isMax ? screenCoord : screenCoord - mThumbHalfWidth * 2, (mWordHeight 0.5f * (getHeight() - mWordHeight) - mThumbHalfHeight), mPaint);}/*** 画min滑块值text** param screenCoord* param value* param canvas*/private void drawThumbMinText(float screenCoord, Number value, Canvas canvas) {String progress getProgressStr(value.intValue());float progressWidth mPaint.measureText(progress);canvas.drawText(progress, (screenCoord - progressWidth / 2) - mThumbHalfWidth, mWordSize, mPaint);}/*** 画max滑块值text** param screenCoord* param value* param canvas*/private void drawThumbMaxText(float screenCoord, Number value, Canvas canvas) {String progress getProgressStr(value.intValue());float progressWidth mPaint.measureText(progress);canvas.drawText(progress, (screenCoord - progressWidth / 2) mThumbHalfWidth, mWordSize, mPaint);}/*** 根据touchX, 判断是哪一个thumb(Min or Max)** param touchX 触摸的x在屏幕中坐标相对于容器*/private Thumb evalPressedThumb(float touchX) {Thumb result null;boolean minThumbPressed isInThumbRange(touchX, mPercentSelectedMinValue, false);boolean maxThumbPressed isInThumbRange(touchX, mPercentSelectedMaxValue, true);if (minThumbPressed maxThumbPressed) {// if both thumbs are pressed (they lie on top of each other), choose the one with more room to drag. this avoids stalling the thumbs in a corner, not being able to drag them apart anymore.result (touchX / getWidth() 0.5f) ? Thumb.MIN : Thumb.MAX;} else if (minThumbPressed) {result Thumb.MIN;} else if (maxThumbPressed) {result Thumb.MAX;}return result;}/*** 判断touchX是否在滑块点击范围内** param touchX 需要被检测的 屏幕中的x坐标相对于容器* param percentThumbValue 需要检测的滑块x坐标百分比值滑块x坐标*/private boolean isInThumbRange(float touchX, double percentThumbValue, boolean isMax) {if (isMax) {return Math.abs(touchX - mThumbHalfWidth - percentToScreen(percentThumbValue)) mThumbHalfWidth;} else {return Math.abs(touchX mThumbHalfWidth - percentToScreen(percentThumbValue)) mThumbHalfWidth;}
// return Math.abs(touchX - percentToScreen(percentThumbValue)) mThumbHalfWidth; //居中基准时}/*** 判断用户是否触碰光标** param touchX 需要被检测的 屏幕中的x坐标相对于容器* param cursorX 光标x坐标滑块x坐标* return*/private boolean isInCursorRange(float touchX, float cursorX) {return Math.abs(touchX - cursorX) mThumbHalfWidth;}/*** 设置已选择最小值的百分比值*/public void setPercentSelectedMinValue(double value) {mPercentSelectedMinValue Math.max(0d, Math.min(1d, Math.min(value, mPercentSelectedMaxValue)));invalidate();}/*** 设置已选择最大值的百分比值*/public void setPercentSelectedMaxValue(double value) {mPercentSelectedMaxValue Math.max(0d, Math.min(1d, Math.max(value, mPercentSelectedMinValue)));invalidate();}/*** 进度值从百分比到绝对值** return*/SuppressWarnings(unchecked)private float percentToAbsoluteValue(double normalized) {return (float) (mAbsoluteMinValue normalized * (mAbsoluteMaxValue - mAbsoluteMinValue));}/*** 进度值从绝对值到百分比*/private double absoluteValueToPercent(float value) {if (0 mAbsoluteMaxValue - mAbsoluteMinValue) {// prevent division by zero, simply return 0.return 0d;}return (value - mAbsoluteMinValue) / (mAbsoluteMaxValue - mAbsoluteMinValue);}/*** 进度值从百分比值转换到屏幕中坐标值*/private float percentToScreen(double percentValue) {return (float) (mWidthPadding percentValue * (getWidth() - 2 * mWidthPadding));}/*** 进度值转换屏幕像素值到百分比值*/private double screenToPercent(float screenCoord) {int width getWidth();if (width 2 * mWidthPadding) {// prevent division by zero, simply return 0.return 0d;} else {double result (screenCoord - mWidthPadding) / (width - 2 * mWidthPadding);return Math.min(1d, Math.max(0d, result));}}public void setThumbListener(ThumbListener mThumbListener) {this.mThumbListener mThumbListener;}private String getProgressStr(int progress) {String progressStr;if (mProgressTextFormat HINT_FORMAT_TIME) {progressStr formatSecondTime(progress);} else {progressStr String.valueOf(progress);}return progressStr;}public boolean isFixedMode() {return fixedMode;}public void setFixedMode(boolean fixedMode) {this.fixedMode fixedMode;}/*** 重置总时长-单位秒** param totalSecond*/public void resetTotalSecond(int totalSecond) {if (totalSecond 0 totalSecond 12000) {mAbsoluteMaxValue totalSecond * 1000;mAbsoluteMinValue 0.0f;mProgressTextFormat HINT_FORMAT_TIME;invalidate();}}/*** 重置总时长-单位毫秒** param totalMillisecond*/public void resetTotalMillisecond(float totalMillisecond) {if (totalMillisecond 0 totalMillisecond 1200000) {mAbsoluteMaxValue totalMillisecond;mAbsoluteMinValue 0.0f;mProgressTextFormat HINT_FORMAT_TIME;invalidate();}}public void setPlayerControlListener(PlayerControlListener playerControlListener) {this.playerControlListener playerControlListener;}/*** Thumb枚举 最大或最小*/private enum Thumb {MIN, MAX}public interface PlayerControlListener {void onPlayerControl(boolean isPlay);}/*** 游标以及滑块事件*/public interface ThumbListener {void onClickMinThumb(Number max, Number min);void onClickMaxThumb();void onUpMinThumb(Number max, Number min);void onUpMaxThumb(Number max, Number min);void onMinMove(Number max, Number min);void onMaxMove(Number max, Number min);void onCursorMove(Number cur);void onCursorUp(Number cur);}public interface IPlayerCallback {long getCurrentPosition();}private IPlayerCallback playerCallback null;public void setPlayerCallback(IPlayerCallback playerCallback) {this.playerCallback playerCallback;}public void release() {isPlay false;}
}Android音视频剪辑器自定义View实战 - 掘金话不多说先上一个代码完成效果。 动图好像录成横屏的了也没找到调整反转 GIF 的位置下面再补一张设计稿静态图吧 最近这几年音视频应用越来越广泛随之而来的音视频相关的需求也越来越多音视频的剪辑https://juejin.cn/post/7236635197071802424?utm_sourcegold_browser_extension#heading-10