美文网首页UI效果仿写Android自定义View
2018-09-09 Android 仿抖音音乐裁剪选择控件

2018-09-09 Android 仿抖音音乐裁剪选择控件

作者: 馒Care | 来源:发表于2018-09-09 14:17 被阅读227次

    先看效果图


    image.png

    public class ScrollTrackView extends HorizontalScrollView {

    private Handler mScrollHandler;
    private OnScrollTrackListener mOnScrollTrackListener;
    private OnProgressRunListener mProgressRunListener;
    
    private int mBackgroundColor = Color.LTGRAY;
    private int mForegroundColor = Color.BLUE;
    private int mSpaceSize = 16;//方块中间空格大小
    private int mTrackItemWidth = 6;//每个方块的宽度
    private int mDelayTime = 20;//ms
    private int mTrackFragmentCount = 10;
    private boolean isAutoRun = true;//是否自动跑进度
    private boolean isLoopRun = false;//是否循环跑进度
    private int mCutDuration = 10 * 1000;//裁剪区间,也就是控件左边,跑到右边的时间
    private float mSpeed = 10;
    
    /**
     * 滚动状态:
     * IDLE=滚动停止
     * TOUCH_SCROLL=手指拖动滚动
     * FLING=滚动
     */
    enum ScrollStatus {
        IDLE, TOUCH_SCROLL, FLING
    }
    
    /**
     * 记录当前滚动的距离
     */
    private int currentX = -9999999;
    
    /**
     * 当前滚动状态
     */
    private ScrollStatus scrollStatus = ScrollStatus.IDLE;
    
    private Track track;//每个方块的高度要通过addView添加到当前View中
    private boolean disableTouch;
    private TrackMoveController moveController;//方块移动的控制类
    
    private int audioDuration;
    
    public ScrollTrackView(Context context) {
        super(context);
        initView(context);
    }
    
    public ScrollTrackView(Context context, AttributeSet attrs) {
        super(context, attrs);
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.ScrollTrackView);
    
        mBackgroundColor = typedArray.getColor(R.styleable.ScrollTrackView_background_color, mBackgroundColor);
        mForegroundColor = typedArray.getColor(R.styleable.ScrollTrackView_foreground_color, mForegroundColor);
    
        //px
        mSpaceSize = Math.round(typedArray.getDimension(R.styleable.ScrollTrackView_space_size, mSpaceSize));
        //mSpaceSize =ViewUtil.dp2px(context,Math.round(spaceSizeDp));
        mTrackItemWidth = Math.round(typedArray.getDimension(R.styleable.ScrollTrackView_track_item_width, mTrackItemWidth));
        //mTrackItemWidth = ViewUtil.dp2px(context,Math.round(trackItemWidthDp));
        isAutoRun = typedArray.getBoolean(R.styleable.ScrollTrackView_auto_run, isAutoRun);
        mTrackFragmentCount = typedArray.getInteger(R.styleable.ScrollTrackView_track_fragment_count, mTrackFragmentCount);
        mCutDuration = typedArray.getInteger(R.styleable.ScrollTrackView_cut_duration, mCutDuration);
        isLoopRun = typedArray.getBoolean(R.styleable.ScrollTrackView_loop_run, isLoopRun);
    
        typedArray.recycle();
        initView(context);
    }
    
    public ScrollTrackView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView(context);
    }
    
    private void initView(final Context context) {
    
        track = new Track(context);
        track.setBackgroundColorInt(mBackgroundColor);
        track.setForegroundColor(mForegroundColor);
        track.setSpaceSize(mSpaceSize);
        track.setTrackFragmentCount(mTrackFragmentCount);
        track.setTrackItemWidth(mTrackItemWidth);
    
        HorizontalScrollView.LayoutParams lp = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
        addView(track, lp);
        setSmoothScrollingEnabled(false);
    
    
        moveController = new TrackMoveController(mDelayTime, new TrackMoveController.OnProgressChangeListener() {
            @Override
            public void onProgressChange(int progress) {
                Message msg = progressHandler.obtainMessage(1);
                msg.arg1 = progress;
                progressHandler.sendMessage(msg);
            }
    
            @Override
            public void onProgressStart() {
                if (mProgressRunListener != null) {
                    mProgressRunListener.onTrackStart(getStartTime());
                }
            }
    
            @Override
            public void onProgressEnd() {
                if (mProgressRunListener != null) {
                    mProgressRunListener.onTrackEnd();
                }
            }
        });
    
        post(new Runnable() {
            @Override
            public void run() {
                //可视的时候开始走进度
                moveController.setScrollTrackViewWidth(getWidth());
                mSpeed = ((getWidth() * 1f) / (mCutDuration * 1f));//根据时间和控件的宽度计算速度
                float delayTime = 1f / mSpeed;//根据速度来算走每个像素点需要多久时间
                moveController.setDelayTime(Math.round(delayTime));//四舍五入
                moveController.setLoopRun(isLoopRun);
                if (isAutoRun) {
                    startMove();
                }
    
            }
        });
        mScrollHandler = new Handler();
        //滑动状态监听
        mOnScrollTrackListener = new OnScrollTrackListener() {
            @Override
            public void onScrollChanged(ScrollStatus scrollStatus) {
                switch (scrollStatus) {
                    case IDLE:
                        if (moveController != null) {
                            moveController.setScrollTrackStartX(getScrollX());
                            moveController.continueRun();
                        }
                        if (mProgressRunListener != null) {
                            mProgressRunListener.onTrackStartTimeChange(getStartTime());
                        }
    
                        break;
                    case FLING:
                        break;
                    case TOUCH_SCROLL:
                        if (moveController != null) {
                            moveController.pause();
                        }
                        break;
                    default:
                        break;
                }
    
            }
        };
    
    
    }
    
    public void setCutDuration(int cutDuration) {
        mCutDuration = cutDuration;
    }
    
    /**
     * 设置循环播放
     *
     * @param isLoop
     */
    public void setLoopRun(boolean isLoop) {
        isLoopRun = isLoop;
    }
    
    public void setTrackTemplateData(float[] data) {
        if (track != null && data != null) {
            track.setTrackTemplateData(data);
        }
    }
    
    public void setTrackFragmentCount(int count) {
        if (track != null) {
            track.setTrackFragmentCount(count);
        }
    }
    
    public void setSpaceSize(int px) {
        if (track != null) {
            track.setSpaceSize(px);
        }
    }
    
    public void setTrackItemWidth(int px) {
        if (track != null) {
            track.setTrackItemWidth(px);
        }
    }
    
    //-------------scroll control-----------------
    private interface OnScrollTrackListener {
        void onScrollChanged(ScrollStatus scrollStatus);
    }
    
    
    /**
     * 滚动监听runnable 方便获取滑动状态
     */
    private Runnable scrollRunnable = new Runnable() {
        @Override
        public void run() {
            if (getScrollX() == currentX) {
                //滚动停止,取消监听线程
                scrollStatus = ScrollStatus.IDLE;
                if (mOnScrollTrackListener != null) {
                    mOnScrollTrackListener.onScrollChanged(scrollStatus);
                }
                mScrollHandler.removeCallbacks(this);
                return;
            } else {
    
                //手指离开屏幕,但是view还在滚动
                scrollStatus = ScrollStatus.FLING;
                if (mOnScrollTrackListener != null) {
                    mOnScrollTrackListener.onScrollChanged(scrollStatus);
                }
            }
            currentX = getScrollX();
            //滚动监听间隔:milliseconds
            mScrollHandler.postDelayed(this, 20);
        }
    };
    
    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_MOVE:
                this.scrollStatus = ScrollStatus.TOUCH_SCROLL;
                mOnScrollTrackListener.onScrollChanged(scrollStatus);
                mScrollHandler.removeCallbacks(scrollRunnable);
                break;
            case MotionEvent.ACTION_UP:
                mScrollHandler.post(scrollRunnable);
                break;
        }
        return super.onTouchEvent(ev);
    }
    
    
    /**
     * 进度控制
     */
    Handler progressHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if (msg.what == 1) {
                track.setProgress(msg.arg1);
            }
        }
    };
    
    /*@Override
    public void fling(int velocity) {
        super.fling(velocity / 1000);
    }*/
    
    
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        if (disableTouch) {
            return true;
        }
        return super.onInterceptTouchEvent(ev);
    }
    
    /**
     * 开始
     */
    public void startMove() {
        disableTouch = true;
        if (moveController != null) {
            moveController.start();
    
        }
    }
    
    /**
     * 重新开始播放
     */
    public void restartMove() {
        disableTouch = true;
    
        if (moveController != null) {
            scrollTo(0, 0);
            smoothScrollTo(0, 0);
            moveController.restart();
            if (mProgressRunListener != null) {
                mProgressRunListener.onTrackStartTimeChange(0);
            }
        }
    }
    
    
    /**
     * 停止
     */
    public void stopMove() {
        disableTouch = false;
        if (moveController != null) {
            moveController.stop();
        }
    }
    
    
    public void pauseMove() {
        disableTouch = false;
        if (moveController != null) {
            moveController.pause();
        }
    }
    
    /**
     * 轨道开始播放到轨道结束监听
     */
    public interface OnProgressRunListener {
        void onTrackStart(int ms);
    
        void onTrackStartTimeChange(int ms);
    
        void onTrackEnd();
    
    }
    
    public void setOnProgressRunListener(OnProgressRunListener listener) {
        mProgressRunListener = listener;
    }
    
    /**
     * 设置音频总时间
     */
    public void setDuration(int ms) {
        audioDuration = ms;
    }
    
    /**
     * 获取歌曲开始时间 (毫秒)
     */
    public int getStartTime() {
        float rate = Math.abs(getScrollX()) / (track.getWidth() * 1f);
        return (int) (audioDuration * rate);
    }
    
    public void setDraftTime(int time) {
        float rate = time / audioDuration;
        int scrollX = (int) (rate * track.getWidth() * 1f);
        setScrollX(Math.abs(scrollX));
    }
    
    public void setProgressContinue(boolean isContinue) {
        if (moveController != null) {
            moveController.setProgressContinue(isContinue);
        }
    }
    
    /**
     * 设置进度
     *
     * @param percent 浮点数,当前位置在整个view 中的比例
     */
    public void setRealProgress(float percent) {
        if (moveController != null) {
            float position = getWidth() * 1f * percent;
            moveController.setCurrentProgressPosition(Math.round(position));
        }
    }
    
    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        stopMove();
    }
    

    }

    控件比较简单,这里处理的方式,主要是继承一个HorizontalScrollView,来实现左右滑动的效果,当然如果你用RecycleView也是可以的。相关注释在代码内部有提供
    接下来看Track的定义

    public class Track extends View {

    private int trackWidth = 0;
    private Paint slipPaint = null;
    private Paint maskPaint = null;
    private Paint mTrackPaint = null;
    private Paint borderPaint = null;
    private int borderWidth = DensityUtil.dp2px(3);
    private int progress = 0;
    private Bitmap shape = null;
    private Bitmap mask = null;
    private boolean isNewMask = true;
    private int trackTemplateCount;
    private int mBackgroundColor;
    private int mForegroundColor;
    private int mSpaceSize = DensityUtil.dp2px(3);
    private int mTrackItemWidth = DensityUtil.dp2px(3);
    private int mTrackFragmentCount;
    //    25。55。50。40。30。25。30。25。55。25。30。  15。25。15。15。
    //0.41F,0.9F,0.82F,0.66F,0.49F,0.41F,0.49F,0.41F,0.9F,0.41F,0.49F,0.25F,0.41F,0.25F,0.25F
    private float[] mTrackTemplateData = new float[]{0.41F, 0.9F, 0.82F, 0.66F, 0.49F, 0.41F, 0.49F, 0.41F, 0.9F, 0.41F, 0.49F, 0.25F, 0.41F, 0.25F, 0.25F};//这里定义每个大的片段需要多少个小方块,我这里是15个小方块组合成一个大的方块,每个大的方块循环滚动渲染
    
    public void setBackgroundColorInt(int backgroundColor) {
        this.mBackgroundColor = backgroundColor;
        this.invalidate();
    }
    
    public void setForegroundColor(int foregroundColor) {
        this.mForegroundColor = foregroundColor;
        this.invalidate();
    }
    
    public void setSpaceSize(int spaceSize) {
        this.mSpaceSize = spaceSize;
        this.invalidate();
    }
    
    public void setTrackItemWidth(int trackItemWidth) {
        this.mTrackItemWidth = trackItemWidth;
        this.invalidate();
    }
    
    public void setTrackFragmentCount(int trackFragmentCount) {
        this.mTrackFragmentCount = trackFragmentCount;
        this.invalidate();
    }
    
    public void setTrackTemplateData(float[] mTrackTemplateData) {
        this.mTrackTemplateData = mTrackTemplateData;
        this.invalidate();
    }
    
    public Track(Context context) {
        super(context);
        this.init();
    }
    
    public Track(Context paramContext, AttributeSet paramAttributeSet) {
        super(paramContext, paramAttributeSet);
        this.init();
    }
    
    private void init() {
        this.slipPaint = new Paint();
        this.slipPaint.setXfermode(new PorterDuffXfermode(Mode.DST_IN));
        this.slipPaint.setFilterBitmap(false);
        this.maskPaint = new Paint();
        this.maskPaint.setXfermode(new PorterDuffXfermode(Mode.DST_IN));
        this.maskPaint.setFilterBitmap(false);
        this.mTrackPaint = new Paint();
        this.mTrackPaint.setAntiAlias(true);
        this.mTrackPaint.setStrokeWidth((float) this.mTrackItemWidth);
        this.mTrackPaint.setColor(-3355444);
        this.mTrackPaint.setStyle(Style.FILL);
        this.mTrackPaint.setStrokeCap(Cap.ROUND);
        this.borderPaint = new Paint();
        this.borderPaint.setAntiAlias(true);
        this.borderPaint.setStrokeWidth((float) this.borderWidth);
        this.borderPaint.setColor(-16777216);
        this.borderPaint.setStyle(Style.STROKE);
        this.borderPaint.setStrokeCap(Cap.SQUARE);
        this.trackTemplateCount = this.mTrackTemplateData.length;
    }
    
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int width = 0;
        int height = 0;
        int modeW = MeasureSpec.getMode(widthMeasureSpec);
        if (modeW == -2147483648) {
            width = MeasureSpec.getSize(widthMeasureSpec);
        }
    
        if (modeW == 1073741824) {
            width = widthMeasureSpec;
        }
    
        if (modeW == 0) {
            this.trackWidth = this.mSpaceSize + (this.mTrackItemWidth + this.mSpaceSize) * this.trackTemplateCount * this.mTrackFragmentCount;
            width = this.trackWidth;
        }
    
        int modeH = MeasureSpec.getMode(height);
        if (modeH == -2147483648) {
            height = MeasureSpec.getSize(heightMeasureSpec);
        }
    
        if (modeH == 1073741824) {
            height = heightMeasureSpec;
        }
    
        if (modeH == 0) {
            height = MeasureSpec.getSize(heightMeasureSpec);
        }
    
        this.setMeasuredDimension(width, height);
    }
    
    @Override
    protected void onDraw(Canvas canvas) {
        try {
            this.drawTrack(canvas, this.mBackgroundColor);
            int layer = canvas.saveLayer(0.0F, 0.0F, (float) this.getWidth(), (float) this.getHeight(), (Paint) null, 31);
            this.drawTrack(canvas, this.mForegroundColor);
            if (this.shape == null || this.shape.isRecycled()) {
                this.shape = this.getShape(this.getWidth(), this.getHeight());
            }
    
            canvas.drawBitmap(this.shape, 0.0F, 0.0F, this.slipPaint);
            if (this.isNewMask) {
                this.mask = this.getMask(this.getWidth(), this.getHeight());
                this.isNewMask = false;
            }
    
            canvas.drawBitmap(this.mask, 0.0F, 0.0F, this.maskPaint);
            canvas.restoreToCount(layer);
        } catch (Exception var3) {
            var3.printStackTrace();
        }
    
    }
    
    private Bitmap getShape(int width, int height) {
        Bitmap bitmap = Bitmap.createBitmap(width, height, Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        RectF localRectF = new RectF(0.0F, 0.0F, (float) width, (float) height);
        Paint paint = new Paint();
        paint.setAntiAlias(true);
        canvas.drawRect(localRectF, paint);
        return bitmap;
    }
    
    private Bitmap getMask(int width, int height) {
        Bitmap bitmap = Bitmap.createBitmap(width, height, Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        Paint paint = new Paint();
        paint.setAntiAlias(true);
        paint.setColor(this.mForegroundColor);
        canvas.drawRect(0.0F, 0.0F, (float) this.progress, (float) height, paint);
        return bitmap;
    }
    
    public void setProgress(int progress) {
        this.progress = progress;
        this.isNewMask = true;
        this.invalidate();
    }
    
    private void drawTrack(Canvas canvas, int color) {
        this.mTrackPaint.setColor(color);
        if (this.trackTemplateCount > 0) {
            int cy = canvas.getHeight() / 2;
    
            for (int j = 0; j < this.mTrackFragmentCount; ++j) {
                for (int i = 0; i < this.trackTemplateCount; ++i) {
                    int x = this.mSpaceSize + (this.mTrackItemWidth + this.mSpaceSize) * i + (this.mTrackItemWidth + this.mSpaceSize) * this.trackTemplateCount * j;
                    canvas.drawLine((float) x, (float) cy - this.mTrackTemplateData[i] * (float) this.getHeight() / 2.0F, (float) x, (float) cy + this.mTrackTemplateData[i] * (float) this.getHeight() / 2.0F, this.mTrackPaint);
                }
            }
    
        }
    }
    

    }

    接下来也比较简单TrackMoveController
    这个类主要是用来控制滑动的Track滚动状态
    public class TrackMoveController {

    private Timer mTimer;
    private long mDelayTime = 10;//ms
    private int mProgress = 0;
    private int mScrollTrackViewWidth;
    private int mScrollTrackStartX = 0;
    private OnProgressChangeListener mListener;
    private boolean isCanRun = true;
    private boolean isLoopRun = false;
    private boolean isStarted = true;
    public TrackMoveController(int delayTime){
        mDelayTime = delayTime;
    }
    public TrackMoveController(int delayTime, OnProgressChangeListener listener){
        mDelayTime = delayTime;
        mListener = listener;
    }
    
    public void setDelayTime(long ms){
        mDelayTime = ms;
    }
    public void setLoopRun(boolean loop){
        isLoopRun = loop;
    }
    
    public synchronized void start() {
        if (mTimer == null) {
            mTimer = new Timer();
            mListener.onProgressStart();
            mTimer.scheduleAtFixedRate(new TimerTask() {
                @Override
                public void run() {
                    if (isCanRun) {
                        if (isLoopRun) {
                            //移动到最右边的时候,重新从启始位置开始移动
                            if((mProgress-mScrollTrackStartX) >= mScrollTrackViewWidth){
                                mProgress = mScrollTrackStartX;
                                mListener.onProgressEnd();
                                mListener.onProgressStart();
                            }
                            if (isProgressContinue) {
                                mProgress ++;
                            }
    
                            if(mListener!=null){
                                mListener.onProgressChange(mProgress);
                            }
    
                        }else{
                            //onProgressStart 只执行一次
                            if(isStarted){
                                mListener.onProgressStart();
                                isStarted = false;
                            }
    
                            if(mListener!=null){
                                mListener.onProgressChange(mProgress);
                            }
    
                            if((mProgress-mScrollTrackStartX) >= mScrollTrackViewWidth){
                                mListener.onProgressEnd();
                            }else{
                                mProgress ++;
                            }
    
                        }
    
                        /*//移动到最右边的时候,重新从启始位置开始移动
                        if((mProgress-mScrollTrackStartX) >= mScrollTrackViewWidth){
                            mProgress = mScrollTrackStartX;
                            mListener.onProgressStart();
                        }
                        mProgress ++;
                        if(mListener!=null){
                            mListener.onProgressChange(mProgress);
                        }*/
                    }
                }
            }, 0,mDelayTime );//延时时间,间隔时间
        }else{
            isCanRun = true;
        }
    }
    
    public boolean isRunning() {
        return mTimer != null;
    }
    
    
    public synchronized void stop() {
        if (isRunning()) {
            mTimer.cancel();
            mTimer = null;
        }
    }
    
    public synchronized void pause(){
        if(isRunning()){
            isCanRun = false;
        }
    }
    
    public synchronized void continueRun(){
        isCanRun = true;
        mProgress = mScrollTrackStartX;
    }
    
    public synchronized void restart(){
        stop();
        mScrollTrackStartX = 0;
        mProgress = 0;
        isCanRun = true;
        start();
    }
    
    private boolean isProgressContinue = true;
    //绘制到当前位置然后暂停进度
    public void setProgressContinue(boolean bPause){
        isProgressContinue = bPause;
    }
    
    
    public int getProgress(){
        return mProgress;
    }
    
    public interface OnProgressChangeListener{
        void onProgressChange(int progress);
        void onProgressStart();
        void onProgressEnd();
    }
    
    public void setOnProgressChangeListener(OnProgressChangeListener listener){
        mListener = listener;
    }
    
    
    public void setScrollTrackViewWidth(int mScrollTrackViewWidth) {
        this.mScrollTrackViewWidth = mScrollTrackViewWidth;
    }
    
    /**
     * 设置当前进度条位置
     * @param positionX px
     */
    public void setCurrentProgressPosition(int positionX){
        mProgress = mScrollTrackStartX + positionX;
    }
    
    public void setScrollTrackStartX(int x){
        this.mScrollTrackStartX = x;
    }
    

    }
    资源声明
    <declare-styleable name="ScrollTrackView">

        <attr name="background_color" format="color"></attr>
        <attr name="foreground_color" format="color"></attr>
        <attr name="space_size" format="dimension"></attr>
        <attr name="track_item_width" format="dimension"></attr>
        <attr name="track_fragment_count" format="integer" />
        <attr name="auto_run" format="boolean" />
        <attr name="cut_duration" format="integer" />
        <attr name="loop_run" format="boolean" />
    </declare-styleable>
    

    使用方法
    <com.xxx.ScrollTrackView

                android:id="@+id/scroll_track"
                android:layout_width="match_parent"
                android:layout_height="55dp"
                android:layout_marginLeft="@dimen/dimen_7dp"
                android:layout_marginRight="@dimen/dimen_7dp"
                app:background_color="@color/white"
                app:foreground_color="@color/pat_record_fill_music" />
    

    调用方法
    public void initAudioCrop(ScrollTrackView mScrollTrackView, String videoPath) {

        mScrollTrackView.setSpaceSize(DensityUtil.dp2px(3));//设置空格宽度
        mScrollTrackView.setTrackItemWidth(DensityUtil.dp2px(3));
        mScrollTrackView.setLoopRun(true);
        mScrollTrackView.setCutDuration(FileUtils.getMediaDuration(videoPath));//屏幕左边跑到右边持续的时间,以视频长度为准
        mScrollTrackView.stopMove();
        mScrollTrackView.setOnProgressRunListener(new ScrollTrackView.OnProgressRunListener() {
            @Override
            public void onTrackStart(int i) {
    
            }
    
            @Override
            public void onTrackStartTimeChange(int i) {
                //保留两位小数
                NumberFormat nf = NumberFormat.getNumberInstance();
                nf.setMaximumFractionDigits(2);
    

    // String timeData = "从 " + nf.format((i * 1f) / 1000f) + " 秒开始";
    String times = String.format(getContext().getString(R.string.patrecord_public_time), FileUtils.stringForTime(i));
    mView.showAutioCutStartTime(times, i);
    }

            @Override
            public void onTrackEnd() {
            }
        });
    }
    

    第一次在简书上面写这个。不是很习惯啊。这个格式弄了我老半天,等我慢慢熟悉,我再写详细点。欢迎大家提供更优化的方案让我修改。

    相关文章

      网友评论

      • ea9ca0046d62:请问,滑动时返回的时间总是0?
        馒Care:你指的是FileUtils.stringForTime(i)这段时间?要确保本地是有音频文件的。否则都是0,有问题可以继续问我
      • 乙座破桥:请问 FileUtils.stringForTime(i)是什么
        馒Care:只是一个时间转换的工具,可以根据需求显示与否

      本文标题:2018-09-09 Android 仿抖音音乐裁剪选择控件

      本文链接:https://www.haomeiwen.com/subject/axzrgftx.html