自定义时间刻度尺控件

废话少说,先上效果图:

在这里插入图片描述

自定义属性

 //中间线颜色
    private int mCenterlineColor;
    //刻度线颜色
    private int mLineColor;
    //文字颜色
    private int mTextColor;
    //视频段的背景颜色
    private int mVideoColor;
    //视频轴的背景颜色
    private int mBgColor;
    //文字大小
    private int mTextSize;

		TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.VideoLineView);
        mCenterlineColor = typedArray.getColor(R.styleable.VideoLineView_centerlineColor, Color.RED);
        mLineColor = typedArray.getColor(R.styleable.VideoLineView_lineColor, ContextCompat.getColor(getContext(),R.color.line_video));
        mTextColor = typedArray.getColor(R.styleable.VideoLineView_txColor, ContextCompat.getColor(getContext(),R.color.white));
        mVideoColor = typedArray.getColor(R.styleable.VideoLineView_videoColor, 0x770000FF);//默认带透明蓝色
        mBgColor = typedArray.getColor(R.styleable.VideoLineView_bgColor, Color.WHITE);
        mTextSize = typedArray.getDimensionPixelSize(R.styleable.VideoLineView_textSize,Functions.sp2px(getContext(),10));
        typedArray.recycle();

在attr.xml 标明属性,并在构造函数中获取并设置默认值

宽高处理

 @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        mHeight = measureVideoLine(heightMeasureSpec);
        mWidth = measureVideoLine(widthMeasureSpec);
        setMeasuredDimension(mWidth,mHeight);
    }

    private int measureVideoLine(int measureSpec) {
        int result = 0;
        int specMode = MeasureSpec. getMode(measureSpec);
        int specSize = MeasureSpec. getSize(measureSpec);
        switch (specMode) {
            case MeasureSpec. AT_MOST:
                result = Functions.dip2px(getContext(),32);//默认40dp
                break;
            case MeasureSpec. EXACTLY:
                result = Math.max(specSize,Functions.dip2px(getContext(),32));
                break;
        }
        return result;
    }

开始绘制

 @Override
    protected void onDraw(Canvas canvas) {
		paint.setColor(mLineColor);
        paint.setStrokeWidth(Functions.dip2px(getContext(), 2));
        //绘制刻度尺
        canvas.drawLine(0, mHeight / 2, mWidth, mHeight / 2, paint);
        //往左绘制
        drawLine(canvas, paint, mTime.getTime(), mWidth / 2, true);
        //往右绘制
        drawLine(canvas, paint, mTime.getTime(), mWidth / 2, false);
        //中间线最后绘制
        paint.setColor(mCenterlineColor);
        canvas.drawLine(mWidth / 2, 0, mWidth / 2, mHeight, paint);
    }

    private void drawLine(Canvas canvas, Paint paint, long time, float startX, boolean left) {
        paint.setColor(mLineColor);
        long residue = time % (60 * 1000 * intervalMinute);//是否能被十分钟整除,也就是是否移动了一个刻度
        long nextTime = 0;//下次绘制的时间
        float offset = Functions.dip2px(getContext(), intervalWidth);//一个刻度的距离
        if (residue == 0) {
            nextTime = left ? time - 60 * 1000 * intervalMinute : time + 60 * 1000 * intervalMinute;
            startX = left ? startX - offset : startX + offset;
        } else {//不够十分钟,也就是不到一个刻度
            nextTime = left ? time - residue : time - residue + 60 * 1000 * intervalMinute;
            //重新计算startX;
            float v = offset / (60 * 10);//计算出每一秒的距离
            startX = left ? startX - residue / 1000 * v : startX + (60 * 1000 * intervalMinute - residue) / 1000 * v;
        }
        if (startX < 0 || startX > mWidth) {
            return;
        }
        int endY = nextTime % (3600 * 1000) == 0 ? 6 : 3;//以一个小时求余
        paint.setStrokeWidth(Functions.dip2px(getContext(), 1));
        paint.setColor(ContextCompat.getColor(getContext(), R.color.white));
        canvas.drawLine(startX, mHeight / 2 + Functions.dip2px(getContext(), endY), startX, mHeight / 2 - Functions.dip2px(getContext(), endY), paint);
        if (endY == 6) {//60分钟整点
            Date t = new Date(nextTime);
            paint.setColor(mTextColor);
            paint.setTextSize(mTextSize);
            paint.setAntiAlias(true);
            Paint.FontMetrics fontMetrics = paint.getFontMetrics();
            float value = fontMetrics.bottom - fontMetrics.top;
            float v = paint.measureText(t.getHours() + ":00");
            canvas.drawText(t.getHours() + ":00", startX - v / 2, mHeight / 2 + value + 8, paint);
        }
        drawLine(canvas, paint, nextTime, startX, left);
    }

注释写得挺清晰啦,判断是否移动了一个刻度,如果是而且是向左绘制,下次绘制的时间(nextTime)为当前时间减去一个刻度时间(time - 60 * 1000 * intervalMinute);如果是而且是向右绘制,下次绘制的时间(nextTime)为当前时间加上一个刻度时间(time + 60 * 1000 * intervalMinute),绘制刻度线的横坐标(startx)也就相应移到一个刻度距离(startX - offset : startX + offset)

如果不是移动了一个刻度而且是向左绘制,下次绘制的时间(nextTime)为当前时间减去求余剩下的时间(time - residue);如果不是而且是向右绘制,下次绘制时间(nextTime)为当前时间加上一个刻度时间家去求余剩下时间(time - residue + 60 * 1000 * intervalMinute),绘制刻度线的横坐标(startx)也就相应移动距离(startX - residue / 1000 * v : startX + (60 * 1000 * intervalMinute - residue) / 1000 * v)。

然后递归调用,60分钟整点时绘制文字

触摸事件处理

 @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mStartX = event.getX();
                stopRunTime();
                break;
            case MotionEvent.ACTION_MOVE:
            case MotionEvent.ACTION_UP:
                float distance = event.getX() - mStartX;
                long zeng = getTimeByDistance(distance);
                mStartX = event.getX();
                mTime.setTime(mTime.getTime() - zeng);
                invalidate();
                if (event.getAction() == MotionEvent.ACTION_UP) {
                    if (videoLineViewChangeListener != null) {
                        if (mTime.getTime() > new Date().getTime()) {
                            Toast.makeText(getContext(), "不可以超过当前时间", Toast.LENGTH_LONG).show();
                            mTime.setTime(mTime.getTime() + zeng);//滑动条撤回原处
                            invalidate();
                            return true;
                        }
                        videoLineViewChangeListener.onChange(mTime, this);
                    }
                    startRunTime();
                }
                break;
        }
        return true;
    }
    
private int getTimeByDistance(float distance) {
        float offset_px = Functions.dip2px(getContext(), intervalWidth);
        float secondsWidth = offset_px / (60 * 10);//每秒多少像素
        return Math.round(distance / secondsWidth) * 1000;
    }

监听ACTION_UP 事件 获取移动距离转为移动时间,并在videoLineViewChangeListener接口中返回当前时间

源码


public class VideoLineView extends View {
    Paint paint = new Paint();
    //中间线颜色
    private int mCenterlineColor;
    //刻度线颜色
    private int mLineColor;
    //文字颜色
    private int mTextColor;
    //视频段的背景颜色
    private int mVideoColor;
    //视频轴的背景颜色
    private int mBgColor;
    //文字大小
    private int mTextSize;
    //当前刻度时间
    private Date mTime = new Date();
    private int mHeight;
    private int mWidth;
    private int intervalWidth = 30;//每个刻度30dp宽度
    private int intervalMinute = 10;//每个刻度表示10分钟
    private List<VideoInfo> videoInfos = new ArrayList<VideoInfo>();
    private float mStartX = 0;
    private VideoLineViewChangeListener videoLineViewChangeListener;
    private boolean autoRun = true;//是否自动改变时间
    private Object data;//用来绑定用户数据
    @SuppressLint("HandlerLeak")
    private Handler handler = new Handler() {
    };
    private Runnable autoRunTime = new Runnable() {
        @Override
        public void run() {
            if (mTime != null && mTime.getTime() < videoInfos.get(videoInfos.size() - 1).getEndTime().getTime()) {
                mTime.setTime(mTime.getTime() + 1000);
                invalidate();
                handler.postDelayed(this, 1000);
            }
        }
    };


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

    public VideoLineView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, -1);
    }

    public VideoLineView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.VideoLineView);
        mCenterlineColor = typedArray.getColor(R.styleable.VideoLineView_centerlineColor, Color.RED);
        mLineColor = typedArray.getColor(R.styleable.VideoLineView_lineColor, ContextCompat.getColor(getContext(), R.color.line_video));
        mTextColor = typedArray.getColor(R.styleable.VideoLineView_txColor, ContextCompat.getColor(getContext(), R.color.white));
        mVideoColor = typedArray.getColor(R.styleable.VideoLineView_videoColor, 0x770000FF);//默认带透明蓝色
        mBgColor = typedArray.getColor(R.styleable.VideoLineView_bgColor, Color.WHITE);
        mTextSize = typedArray.getDimensionPixelSize(R.styleable.VideoLineView_textSize, Functions.sp2px(getContext(), 10));
        typedArray.recycle();
    }

    public Date getmTime() {
        return mTime;
    }

    public void setmTime(Date mTime) {
        if (mTime != null) {
            this.mTime.setTime(mTime.getTime());
            startRunTime();
        }
    }

    public List<VideoInfo> getVideoInfos() {
        return videoInfos;
    }

    public void setVideoInfos(List<VideoInfo> videoInfos) {
        this.videoInfos = videoInfos;
        if (videoInfos != null && !videoInfos.isEmpty()) {
            setmTime(videoInfos.get(0).getStartTime());
        }
    }

    public boolean isAutoRun() {
        return autoRun;
    }

    public void setAutoRun(boolean autoRun) {
        this.autoRun = autoRun;
    }

    public VideoLineViewChangeListener getVideoLineViewChangeListener() {
        return videoLineViewChangeListener;
    }

    public void setVideolineChangeListener(VideoLineViewChangeListener videoLineViewChangeListener) {
        this.videoLineViewChangeListener = videoLineViewChangeListener;
    }

    public Object getData() {
        return data;
    }

    public VideoLineView setData(Object data) {
        this.data = data;
        return this;
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        mHeight = getHeight();
        mWidth = getWidth();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        mHeight = measureVideoLine(heightMeasureSpec);
        mWidth = measureVideoLine(widthMeasureSpec);
        setMeasuredDimension(mWidth, mHeight);
    }

    private int measureVideoLine(int measureSpec) {
        int result = 0;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);
        switch (specMode) {
            case MeasureSpec.AT_MOST:
                result = Functions.dip2px(getContext(), 32);//默认40dp
                break;
            case MeasureSpec.EXACTLY:
                result = Math.max(specSize, Functions.dip2px(getContext(), 32));
                break;
        }
        return result;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mStartX = event.getX();
                stopRunTime();
                break;
            case MotionEvent.ACTION_MOVE:
            case MotionEvent.ACTION_UP:
                float distance = event.getX() - mStartX;
                long zeng = getTimeByDistance(distance);
                mStartX = event.getX();
                mTime.setTime(mTime.getTime() - zeng);
                invalidate();
                if (event.getAction() == MotionEvent.ACTION_UP) {
                    if (videoLineViewChangeListener != null) {
                        if (mTime.getTime() > new Date().getTime()) {
                            Toast.makeText(getContext(), "不可以超过当前时间", Toast.LENGTH_LONG).show();
                            mTime.setTime(mTime.getTime() + zeng);//滑动条撤回原处
                            invalidate();
                            return true;
                        }
                        videoLineViewChangeListener.onChange(mTime, this);
                    }
                    startRunTime();
                }
                break;
        }
        return true;
    }

    @Override
    protected void onDraw(Canvas canvas) {
       
        paint.setColor(mLineColor);
        paint.setStrokeWidth(Functions.dip2px(getContext(), 2));
        //绘制刻度尺
        canvas.drawLine(0, mHeight / 2, mWidth, mHeight / 2, paint);
        //往左绘制
        drawLine(canvas, paint, mTime.getTime(), mWidth / 2, true);
        //往右绘制
        drawLine(canvas, paint, mTime.getTime(), mWidth / 2, false);
        //中间线最后绘制
        paint.setColor(mCenterlineColor);
        canvas.drawLine(mWidth / 2, 0, mWidth / 2, mHeight, paint);
    }

    private void drawLine(Canvas canvas, Paint paint, long time, float startX, boolean left) {
        paint.setColor(mLineColor);
        long residue = time % (60 * 1000 * intervalMinute);//是否能被十分钟整除,也就是是否移动了一个刻度
        long nextTime = 0;//下次绘制的时间
        float offset = Functions.dip2px(getContext(), intervalWidth);//一个刻度的距离
        if (residue == 0) {
            nextTime = left ? time - 60 * 1000 * intervalMinute : time + 60 * 1000 * intervalMinute;
            startX = left ? startX - offset : startX + offset;
        } else {//不够十分钟,也就是不到一个刻度
            nextTime = left ? time - residue : time - residue + 60 * 1000 * intervalMinute;
            //重新计算startX;
            float v = offset / (60 * 10);//计算出每一秒的距离
            startX = left ? startX - residue / 1000 * v : startX + (60 * 1000 * intervalMinute - residue) / 1000 * v;
        }
        if (startX < 0 || startX > mWidth) {
            return;
        }
        int endY = nextTime % (3600 * 1000) == 0 ? 6 : 3;//以一个小时求余
        paint.setStrokeWidth(Functions.dip2px(getContext(), 1));
        paint.setColor(ContextCompat.getColor(getContext(), R.color.white));
        canvas.drawLine(startX, mHeight / 2 + Functions.dip2px(getContext(), endY), startX, mHeight / 2 - Functions.dip2px(getContext(), endY), paint);
        if (endY == 6) {//60分钟整点
            Date t = new Date(nextTime);
            paint.setColor(mTextColor);
            paint.setTextSize(mTextSize);
            paint.setAntiAlias(true);
            Paint.FontMetrics fontMetrics = paint.getFontMetrics();
            float value = fontMetrics.bottom - fontMetrics.top;
            float v = paint.measureText(t.getHours() + ":00");
            canvas.drawText(t.getHours() + ":00", startX - v / 2, mHeight / 2 + value + 8, paint);
        }
        drawLine(canvas, paint, nextTime, startX, left);
    }


    /**
     * 根据时间获取该时间在当前数轴的X坐标
     *
     * @param date
     * @return
     */
    private float getXByTime(Date date) {
        float offset_px = Functions.dip2px(getContext(), intervalWidth);
        float secondsWidth = offset_px / (60 * 10);
        long s = (date.getTime() - mTime.getTime()) / 1000;
        float x = mWidth / 2.0f + s * secondsWidth;
        return x;
    }

    private int getTimeByDistance(float distance) {
        float offset_px = Functions.dip2px(getContext(), intervalWidth);
        float secondsWidth = offset_px / (60 * 10);//每秒多少像素
        return Math.round(distance / secondsWidth) * 1000;
    }

    /**
     * 开启自动计时  前提autoRun=true
     */
    public void startRunTime() {
        handler.removeCallbacks(autoRunTime);
        if (autoRun && videoInfos != null && !videoInfos.isEmpty()) {
            handler.postDelayed(autoRunTime, 1000);
        }
    }

    public void stopRunTime() {
        handler.removeCallbacks(autoRunTime);
    }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值