国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

Android音視頻剪輯器自定義View實(shí)戰(zhàn)!

這篇具有很好參考價(jià)值的文章主要介紹了Android音視頻剪輯器自定義View實(shí)戰(zhàn)!。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問(wèn)。

Android音視頻剪輯器自定義View實(shí)戰(zhàn)! - 掘金

/**
 * Created by zhouxuming on 2023/3/30
 *
 * @descr 音視頻剪輯器
 */
public class AudioViewEditor extends View {
    //進(jìn)度文本顯示格式-數(shù)字格式
    public static final int HINT_FORMAT_NUMBER = 0;
    //進(jìn)度文本顯示格式-時(shí)間格式
    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; //播控實(shí)際左邊界
    private final float playControlRight = 80; //播控實(shí)際右邊界
    //滑塊bitmap
    private Bitmap mThumbImage;
    //progress bar 選中背景
//    private Bitmap mProgressBarSelBg;
    private Bitmap mMaxThumbImage;
    private Bitmap mMinThumbImage;
    //progress bar 背景
    private Bitmap mProgressBarBg;
    private float mThumbWidth;
    private float mThumbHalfWidth; //觸摸響應(yīng)寬度的一半
    private float mThumbHalfHeight;
    //seekbar 進(jìn)度條高度
    private float mProgressBarHeight;
    //寬度左右padding
    private float mWidthPadding;
    //最小值(絕對(duì))
    private float mAbsoluteMinValue;
    //最大值(絕對(duì))
    private float mAbsoluteMaxValue;
    //已選標(biāo)準(zhǔn)(占滑動(dòng)條百分比)最小值
    private double mPercentSelectedMinValue = 0d;
    //已選標(biāo)準(zhǔn)(占滑動(dòng)條百分比)最大值
    private double mPercentSelectedMaxValue = 1d;
    //當(dāng)前事件處理的thumb滑塊
    private Thumb mPressedThumb = null;
    //滑塊事件
    private ThumbListener mThumbListener;
    private RectF mProgressBarRect;
    private RectF mProgressBarSelRect;
    //是否可以滑動(dòng)
    private boolean mIsEnable = true;
    //最大值和最小值之間要求的最小范圍絕對(duì)值
    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; //播控狀態(tài)
    private Bitmap playResumeBitmap;
    private Bitmap playPauseBitmap;
    private PlayerControlListener playerControlListener;


    private float cur;// 光標(biāo)坐標(biāo)
    private float pre;// 100 份每一份的偏移量
    private float min;//起始坐標(biāo)
    private float max;//最大坐標(biāo)
    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 提供定義attr
        mWidthPadding = mThumbHalfHeight;
        mWidthPadding += playControlRight;//為了加左右側(cè)播控按鈕, 特地添加出來(lái)的空間
        Paint.FontMetrics metrics = mPaint.getFontMetrics();
        mWordHeight = (int) (metrics.descent - metrics.ascent);

        /*光標(biāo)*/
        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值轉(zhuǎn)換為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);
    }

    @Override
    protected 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);

    }

    /**
     * 設(shè)置seekbar 是否接收事件
     *
     * @param enabled
     */
    @Override
    public void setEnabled(boolean enabled) {
        super.setEnabled(enabled);
        this.mIsEnable = enabled;
    }

    /**
     * 返回被選擇的最小值(絕對(duì)值)
     *
     * @return The currently selected min value.
     */
    public float getSelectedAbsoluteMinValue() {
        return percentToAbsoluteValue(mPercentSelectedMinValue);
    }

    /**
     * 設(shè)置被選擇的最小值(絕對(duì)值)
     *
     * @param value 最小值的絕對(duì)值
     *              return 如果最小值與最大值的最小間距達(dá)到閾值返回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);
    }

    /**
     * 返回被選擇的最大值(絕對(duì)值).
     */
    public float getSelectedAbsoluteMaxValue() {
        return percentToAbsoluteValue(mPercentSelectedMaxValue);
    }

    /**
     * 設(shè)置被選擇的最大值(絕對(duì)值)
     *
     * @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;
    }

    @Override
    public 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 TouchEvent
                if (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)) {//防止光標(biāo)靜態(tài)越界
                            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)) {//防止光標(biāo)靜態(tài)越界
                            cur = percentToScreen(mPercentSelectedMaxValue);
                        }

                        setPercentSelectedMaxValue(absoluteValueToPercent(maxValue));
                        if (mThumbListener != null)
                            mThumbListener.onMaxMove(getSelectedAbsoluteMaxValue(), getSelectedAbsoluteMinValue());
                    }
                }
                //Intercept parent TouchEvent
                if (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 TouchEvent
                if (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 TouchEvent
                if (getParent() != null) {
                    getParent().requestDisallowInterceptTouchEvent(true);
                }
                break;
        }
        return true;
    }

    private boolean isTouchPlayControl(float eventX) {
        if (eventX > playControlLeft && eventX < playControlRight) {
            return true;
        }
        return false;
    }

    @Override
    protected 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);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        // draw seek bar background line
        mPaint.setStyle(Paint.Style.FILL);
        drawPlayControl(canvas);
        canvas.drawBitmap(mProgressBarBg, null, mProgressBarRect, mPaint);
        // draw seek bar active range line
        mProgressBarSelRect.left = percentToScreen(mPercentSelectedMinValue);
        mProgressBarSelRect.right = percentToScreen(mPercentSelectedMaxValue);
        //canvas.drawBitmap(mProgressBarSelBg, mWidthPadding, 0.5f * (getHeight() - mProgressBarHeight), mPaint);

//        canvas.drawBitmap(mProgressBarSelBg, null, mProgressBarSelRect, mPaint); //原中部選中進(jìn)度

        // draw minimum thumb
        drawThumb(percentToScreen(mPercentSelectedMinValue), Thumb.MIN.equals(mPressedThumb), canvas, false);
        // draw maximum thumb
        drawThumb(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軸距離未計(jì)算準(zhǔn)確 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) {
        //top
        float borderLeft = mProgressBarSelRect.left;
        float borderRight = mProgressBarSelRect.right;
        canvas.drawRect(borderLeft - 1, mProgressBarRect.top, borderRight + 1, mProgressBarRect.top + 10, borderPaint);
        //bottom
        canvas.drawRect(borderLeft - 1, mProgressBarRect.bottom, borderRight + 1, mProgressBarRect.bottom - 10, borderPaint);
    }

    private void drawCursor(Canvas canvas) {
        min = percentToScreen(mPercentSelectedMinValue);//開(kāi)始坐標(biāo)
        max = percentToScreen(mPercentSelectedMaxValue);//終點(diǎn)坐標(biāo)
        pre = (getWidth() - 2 * mWidthPadding) / 1000; //每一份的坐標(biāo)
        if (isFirst) {
            cur = min;
            isFirst = false;
        }
        canvas.drawRect(cur - 2, mProgressBarRect.top + 5, cur + 2, mProgressBarRect.bottom - 5, cursorPaint);
    }

    //啟動(dòng)播放線程檢查 pts
    public void startMove() {
        new Thread(new Runnable() {
            @Override
            public 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();
    }

    /**
     * 根據(jù)播放器 pts 控制游標(biāo)進(jìn)度
     *
     * @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;

    @Override
    protected Parcelable onSaveInstanceState() {
        Bundle bundle = new Bundle();
        bundle.putParcelable("SUPER", super.onSaveInstanceState());
        bundle.putDouble("MIN", mPercentSelectedMinValue);
        bundle.putDouble("MAX", mPercentSelectedMaxValue);
        return bundle;
    }

    @Override
    protected 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) {
        //基準(zhǔn)點(diǎn) bar 居中位置
//        canvas.drawBitmap(isMax ? mMaxThumbImage : mThumbImage, screenCoord - mThumbHalfWidth, (mWordHeight + 0.5f * (getHeight() - mWordHeight) - mThumbHalfHeight), mPaint);
        //基準(zhǔn)點(diǎn)頂兩邊位置
        canvas.drawBitmap(isMax ? mMaxThumbImage : mThumbImage, isMax ? screenCoord : screenCoord - mThumbHalfWidth * 2, (mWordHeight + 0.5f * (getHeight() - mWordHeight) - mThumbHalfHeight), mPaint);
    }

    /**
     * 畫(huà)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);
    }

    /**
     * 畫(huà)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);
    }

    /**
     * 根據(jù)touchX, 判斷是哪一個(gè)thumb(Min or Max)
     *
     * @param touchX 觸摸的x在屏幕中坐標(biāo)(相對(duì)于容器)
     */
    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是否在滑塊點(diǎn)擊范圍內(nèi)
     *
     * @param touchX            需要被檢測(cè)的 屏幕中的x坐標(biāo)(相對(duì)于容器)
     * @param percentThumbValue 需要檢測(cè)的滑塊x坐標(biāo)百分比值(滑塊x坐標(biāo))
     */
    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; //居中基準(zhǔn)時(shí)
    }

    /**
     * 判斷用戶是否觸碰光標(biāo)
     *
     * @param touchX  需要被檢測(cè)的 屏幕中的x坐標(biāo)(相對(duì)于容器)
     * @param cursorX 光標(biāo)x坐標(biāo)(滑塊x坐標(biāo))
     * @return
     */
    private boolean isInCursorRange(float touchX, float cursorX) {
        return Math.abs(touchX - cursorX) <= mThumbHalfWidth;
    }

    /**
     * 設(shè)置已選擇最小值的百分比值
     */
    public void setPercentSelectedMinValue(double value) {
        mPercentSelectedMinValue = Math.max(0d, Math.min(1d, Math.min(value, mPercentSelectedMaxValue)));
        invalidate();
    }

    /**
     * 設(shè)置已選擇最大值的百分比值
     */
    public void setPercentSelectedMaxValue(double value) {
        mPercentSelectedMaxValue = Math.max(0d, Math.min(1d, Math.max(value, mPercentSelectedMinValue)));
        invalidate();
    }

    /**
     * 進(jìn)度值,從百分比到絕對(duì)值
     *
     * @return
     */
    @SuppressWarnings("unchecked")
    private float percentToAbsoluteValue(double normalized) {
        return (float) (mAbsoluteMinValue + normalized * (mAbsoluteMaxValue - mAbsoluteMinValue));
    }

    /**
     * 進(jìn)度值,從絕對(duì)值到百分比
     */
    private double absoluteValueToPercent(float value) {
        if (0 == mAbsoluteMaxValue - mAbsoluteMinValue) {
            // prevent division by zero, simply return 0.
            return 0d;
        }
        return (value - mAbsoluteMinValue) / (mAbsoluteMaxValue - mAbsoluteMinValue);
    }

    /**
     * 進(jìn)度值,從百分比值轉(zhuǎn)換到屏幕中坐標(biāo)值
     */
    private float percentToScreen(double percentValue) {
        return (float) (mWidthPadding + percentValue * (getWidth() - 2 * mWidthPadding));
    }

    /**
     * 進(jìn)度值,轉(zhuǎn)換屏幕像素值到百分比值
     */
    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;
    }

    /**
     * 重置總時(shí)長(zhǎng)-單位秒
     *
     * @param totalSecond
     */
    public void resetTotalSecond(int totalSecond) {
        if (totalSecond > 0 && totalSecond < 12000) {
            mAbsoluteMaxValue = totalSecond * 1000;
            mAbsoluteMinValue = 0.0f;
            mProgressTextFormat = HINT_FORMAT_TIME;
            invalidate();
        }
    }

    /**
     * 重置總時(shí)長(zhǎng)-單位毫秒
     *
     * @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);
    }


    /**
     * 游標(biāo)以及滑塊事件
     */
    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實(shí)戰(zhàn)!,android,前端

?Android音視頻剪輯器自定義View實(shí)戰(zhàn)! - 掘金話不多說(shuō),先上一個(gè)代碼完成效果。 動(dòng)圖好像錄成橫屏的了,也沒(méi)找到調(diào)整反轉(zhuǎn) GIF 的位置,下面再補(bǔ)一張?jiān)O(shè)計(jì)稿靜態(tài)圖吧 最近這幾年音視頻應(yīng)用越來(lái)越廣泛,隨之而來(lái)的音視頻相關(guān)的需求也越來(lái)越多,音視頻的剪輯https://juejin.cn/post/7236635197071802424?utm_source=gold_browser_extension#heading-10文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-652284.html

到了這里,關(guān)于Android音視頻剪輯器自定義View實(shí)戰(zhàn)!的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來(lái)自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 【原理+實(shí)戰(zhàn)+視頻+源碼】抖音,快手大熱背后——Android-貼心的音視頻學(xué)習(xí)指南來(lái)咯

    【原理+實(shí)戰(zhàn)+視頻+源碼】抖音,快手大熱背后——Android-貼心的音視頻學(xué)習(xí)指南來(lái)咯

    下面將這份文檔的內(nèi)容以圖片的形式展現(xiàn)出來(lái),但篇幅有限只能展示部分,如果你需要“高清完整的pdf版”,可以直接點(diǎn)擊我的【GitHub】即可免費(fèi)領(lǐng)取。 一、初級(jí)入門(mén)篇 初級(jí)入門(mén)篇主要是接觸Android多媒體展示相關(guān)的API, 通過(guò)單獨(dú)的列舉和使用這些API,對(duì)Android音視頻處理有

    2024年04月17日
    瀏覽(18)
  • 【原理+實(shí)戰(zhàn)+視頻+源碼】抖音,快手大熱背后——Android 貼心的音視頻學(xué)習(xí)指南來(lái)咯

    【原理+實(shí)戰(zhàn)+視頻+源碼】抖音,快手大熱背后——Android 貼心的音視頻學(xué)習(xí)指南來(lái)咯

    (三)C 與 C++之預(yù)處理命令與用 typedef 命名已有類型 JNI 模塊 JNI 開(kāi)發(fā)之 靜態(tài)注冊(cè)與動(dòng)態(tài)注冊(cè)(一) JNI 開(kāi)發(fā)之方法簽名與 Java 通信(二) JNI 開(kāi)發(fā)之局部引用、全局引用和弱全局引用(三) 二、中級(jí)進(jìn)階篇 學(xué)習(xí) Android 平臺(tái) OpenGL ES API,了解 OpenGL 開(kāi)發(fā)的基本流程,使用 OpenG

    2024年04月22日
    瀏覽(23)
  • 抖音視頻批量智能剪輯/智能一鍵成片功能如何技術(shù)開(kāi)發(fā)源頭?

    抖音視頻批量智能剪輯/智能一鍵成片功能如何技術(shù)開(kāi)發(fā)源頭?

    ?抖音seo,視頻剪輯,批量發(fā)布,賬號(hào)矩陣管理,無(wú)人直播自動(dòng)詢盤(pán)鎖定客戶,想實(shí)現(xiàn)以上功能都要有正規(guī)的接口權(quán)限,這個(gè)權(quán)限接口已經(jīng)在前面文章發(fā)過(guò)。 智能剪輯:咱們研發(fā)公司自主研發(fā)的,包括算法,算法是階乘算法,無(wú)限產(chǎn)出,六大剪輯模式已經(jīng)滿足當(dāng)下需求了,當(dāng)

    2024年02月09日
    瀏覽(39)
  • 音視頻開(kāi)發(fā):Qt在視頻剪輯3D桌面軟件獲勝, 嵌入式不敵安卓

    音視頻開(kāi)發(fā):Qt在視頻剪輯3D桌面軟件獲勝, 嵌入式不敵安卓

    1 Qt Android嵌入式應(yīng)用層開(kāi)發(fā)方向?qū)Ρ?? 大家都知道啊,做嵌入式linux設(shè)備,一些沒(méi)有屏幕,比如安防攝像頭,門(mén)鈴之類的,另外一些嵌入式設(shè)備是有觸控屏,在觸控屏上還跑應(yīng)用軟件的,這種比如商場(chǎng)各種自動(dòng)售賣(mài)機(jī),鐵路賣(mài)票,銀行自助服務(wù),車(chē)載系統(tǒng)等。 10年前,我大學(xué)

    2024年02月09日
    瀏覽(21)
  • windows10|音視頻剪輯|FFMPEG錄屏和網(wǎng)絡(luò)推流源初步的生成

    windows10|音視頻剪輯|FFMPEG錄屏和網(wǎng)絡(luò)推流源初步的生成

    FFMPEG的功能強(qiáng)大是毋庸置疑的,那么錄屏的需求大家在某些時(shí)候大家可能是非常需要的,例如,現(xiàn)有的項(xiàng)目需要演示,因此錄制一段演示視頻;亦或者做內(nèi)容分發(fā)直播的,比如游戲主播,需要錄制在玩某個(gè)游戲的精彩片段,以創(chuàng)建一個(gè)后期的視頻素材庫(kù); 亦或者通過(guò)FFMPEG抓取

    2024年02月20日
    瀏覽(37)
  • WebRTC音視頻通話-WebRTC視頻自定義RTCVideoCapturer相機(jī)

    WebRTC音視頻通話-WebRTC視頻自定義RTCVideoCapturer相機(jī)

    WebRTC音視頻通話-WebRTC視頻自定義RTCVideoCapturer相機(jī) 在之前已經(jīng)實(shí)現(xiàn)了WebRTC調(diào)用ossrs服務(wù),實(shí)現(xiàn)直播視頻通話功能。但是在使用過(guò)程中,RTCCameraVideoCapturer類提供的方法不能修改及調(diào)節(jié)相機(jī)的燈光等設(shè)置,那就需要自定義RTCVideoCapturer自行采集畫(huà)面了。 iOS端WebRTC調(diào)用ossrs相關(guān),實(shí)現(xiàn)

    2024年02月12日
    瀏覽(29)
  • 精選58道——Android 音視頻面試題_安卓音視頻面試題(3)

    精選58道——Android 音視頻面試題_安卓音視頻面試題(3)

    先自我介紹一下,小編浙江大學(xué)畢業(yè),去過(guò)華為、字節(jié)跳動(dòng)等大廠,目前阿里P7 深知大多數(shù)程序員,想要提升技能,往往是自己摸索成長(zhǎng),但自己不成體系的自學(xué)效果低效又漫長(zhǎng),而且極易碰到天花板技術(shù)停滯不前! 因此收集整理了一份《2024年最新Android移動(dòng)開(kāi)發(fā)全套學(xué)習(xí)資

    2024年04月28日
    瀏覽(34)
  • 5G時(shí)代下,Android音視頻強(qiáng)勢(shì)崛起,我們?cè)撊绾慰焖偃腴T(mén)音視頻技術(shù)?

    5G時(shí)代下,Android音視頻強(qiáng)勢(shì)崛起,我們?cè)撊绾慰焖偃腴T(mén)音視頻技術(shù)?

    作為Android開(kāi)發(fā)者的我們到底應(yīng)不應(yīng)該上音視頻這條船? 接下來(lái)一起分析下。 大趨勢(shì) 從未來(lái)的大趨勢(shì)來(lái)看,隨著5G時(shí)代的到來(lái),音視頻慢慢變成人們?nèi)粘I钪械谋匦杵贰3嗽诰€教育、音視頻會(huì)議、即時(shí)通訊這些必須使用音視頻技術(shù)的產(chǎn)品外,其它的產(chǎn)品也需要加入音頻、

    2024年04月15日
    瀏覽(28)
  • Android音視頻-MediaCodec

    Android音視頻-MediaCodec

    原文:https://mp.weixin.qq.com/s?__biz=MzU3NTA3MDU1OQ==mid=2247484865idx=1sn=174b8ca702466e83e72c7115d91b06eachksm=fd298df1ca5e04e7b2df9dc9f21e5cfe3e910204c905d8605f648ce6f6404432a83ae52a23a3scene=178cur_album_id=1638784435628064770#rd MediaCodec 支持處理三種數(shù)據(jù)類型,分別是壓縮數(shù)據(jù)(compressed data)、原始音頻數(shù)據(jù)(raw audio d

    2023年04月08日
    瀏覽(17)
  • Android音視頻編碼(2)

    Android音視頻編碼(2)

    Android本身提供了音視頻編解碼工具,很多時(shí)候是不需要第三方工具的,比如 ffmpeg , OpenCV 等,在android中引入第三庫(kù)比較復(fù)雜,在Android音視頻編碼中介紹了如何引入第三方庫(kù)libpng來(lái)進(jìn)行進(jìn)行圖片處理,同時(shí)引入這些第三方庫(kù),是程序結(jié)構(gòu)變得復(fù)雜。 本文介紹的音視頻編解碼利

    2024年01月17日
    瀏覽(22)

覺(jué)得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包