时间轴控件TimeLineView的实现

转载请标明出处:http://blog.csdn.net/newhope1106/article/details/53525120
首先来看看效果图吧,可以根据需要设置出很炫的效果
时间轴控件TimeLineView的实现_第1张图片
android中经常会用到时间轴,那么如何实现时间轴呢?首先我们了解时间轴的构成
(1)时间球
(2)直线
(3)位置
下面介绍一个时间轴实现的开源代码: https://github.com/newhope1106/TimeLineView
把这几个问题解决再掌握基本的绘制过程就可以实现时间轴了。下面先来看代码。

(1)定义属性,用户可以自定义的属性值



    
        
        
        
        
        
    

(2)代码实现
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.View;

/**
 * @author appleye
 * @date 2016-12-02
 * 时间轴控件
 */

public class TimeLineView extends View {

    /**顶部时间球*/
    private Drawable mTopTimeBall;
    /**底部时间球*/
    private Drawable mBottomTimeBall;
    /**顶部时间球到view的时间线*/
    private Drawable mStartLine;
    /**顶部时间球到底部的时间线*/
    private Drawable mEndLine;
    /**时间球大小*/
    private int mBallSize;
    /**时间线宽度*/
    private int mLineWidth;
    /**顶部时间球中心距离顶部的距离*/
    private int mBallCenterMarginTop;

    private Rect mBounds;
    private Context mContext;

    /**顶部时间线是否可见*/
    private boolean mIsTopLineVisible = false;
    /**底部时间球是否可见*/
    private boolean mIsBottomBallVisible = false;

    public TimeLineView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mContext = context;
        init(attrs);
    }

    private void init(AttributeSet attrs) {
        /*获取xml中设置的属性值*/
        TypedArray typedArray = getContext().obtainStyledAttributes(attrs,R.styleable.timeline_style);
        mTopTimeBall = typedArray.getDrawable(R.styleable.timeline_style_ball);
        mBottomTimeBall = typedArray.getDrawable(R.styleable.timeline_style_ball);
        mStartLine = typedArray.getDrawable(R.styleable.timeline_style_line);
        mEndLine = typedArray.getDrawable(R.styleable.timeline_style_line);
        mBallSize = typedArray.getDimensionPixelSize(R.styleable.timeline_style_ballSize, 25);
        mLineWidth = typedArray.getDimensionPixelSize(R.styleable.timeline_style_lineWidth, 2);
        mBallCenterMarginTop = typedArray.getDimensionPixelSize(R.styleable.timeline_style_ballCenterMargin, 0);
        typedArray.recycle();

        /*没有设置drawable属性值,则采用缺省值*/
        if(mTopTimeBall == null) {
            mTopTimeBall = mContext.getResources().getDrawable(R.drawable.ball);
        }

        if(mBottomTimeBall == null) {
            mBottomTimeBall = mContext.getResources().getDrawable(R.drawable.ball);
        }

        if(mStartLine == null) {
            mStartLine = mContext.getResources().getDrawable(R.drawable.time_line);
        }

        if(mEndLine == null) {
            mEndLine = mContext.getResources().getDrawable(R.drawable.time_line);
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int w = mBallSize + getPaddingLeft() + getPaddingRight();
        int h = mBallSize + getPaddingTop() + getPaddingBottom();

        int widthSize = resolveSizeAndState(w, widthMeasureSpec, 0);
        int heightSize = resolveSizeAndState(h, heightMeasureSpec, 0);

        setMeasuredDimension(widthSize, heightSize);
        initTimeLineView();
    }

    /**
     * 初始化时间轴控件的位置大小等
     * */
    private void initTimeLineView() {
        int paddingLeft = getPaddingLeft();
        int paddingRight = getPaddingRight();
        int paddingTop = getPaddingTop();
        int paddingBottom = getPaddingBottom();

        int width = getWidth();// Width of current custom view
        int height = getHeight();

        /*有效的宽度*/
        int effectWidth = width - paddingLeft - paddingRight;
        /*有效高度*/
        int effectHeight = height - paddingTop - paddingBottom;

        /*时间球实际大小不能大于有效宽高度*/
        int ballSize = Math.min(mBallSize, Math.min(effectWidth, effectHeight));

        /*顶部时间球位置*/
        if(mTopTimeBall != null) {
            mTopTimeBall.setBounds(paddingLeft, paddingTop + mBallCenterMarginTop,
                    paddingLeft + ballSize, paddingTop + ballSize + mBallCenterMarginTop);
            mBounds = mTopTimeBall.getBounds();
        } else {
            mBounds = new Rect(paddingLeft, 0, paddingLeft + ballSize, 0);
        }

        int centerX = mBounds.centerX();
        int lineLeft = centerX - (mLineWidth >> 1);

        if(mIsTopLineVisible && mStartLine!=null) {
            mStartLine.setBounds(lineLeft, 0, mLineWidth + lineLeft, mBounds.top);
        }

        if(mEndLine != null) {
            mEndLine.setBounds(lineLeft, mBounds.bottom, mLineWidth + lineLeft, height);
        }

        if(mIsBottomBallVisible && mBottomTimeBall != null){
            mBottomTimeBall.setBounds(paddingLeft,height - paddingBottom - ballSize,
                    paddingLeft + ballSize, height - paddingBottom);
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        if(mTopTimeBall != null) {
            mTopTimeBall.draw(canvas);
        }

        if(mIsTopLineVisible && mStartLine != null) {
            mStartLine.draw(canvas);
        }

        if(mEndLine != null) {
            mEndLine.draw(canvas);
        }

        if(mIsBottomBallVisible && mBottomTimeBall != null) {
            mBottomTimeBall.draw(canvas);
        }
    }

    /**
     * 设置顶部时间球
     * */
    public void setTopTimeBallDrawable(Drawable ball) {
        mTopTimeBall = ball;
        initTimeLineView();
    }

    /**
     * 设置底部时间球
     * */
    public void setBottomTimeDrawable(Drawable ball) {
        mBottomTimeBall = ball;

        initTimeLineView();
    }

    /**
     * 时间球大小
     * */
    public void setTimeBallSize(int ballSize) {
        mBallSize = ballSize;

        initTimeLineView();
    }

    /**
     * 时间球中心位于顶部的距离
     * */
    public void setBallMarginTop(int top) {
        mBallCenterMarginTop = top;

        initTimeLineView();
    }

    /**
     * 时间轴宽度
     * */
    public void setLineWidth(int width) {
        mLineWidth = width;

        initTimeLineView();
    }

    /**
     * 顶部时间线是否可见
     * */
    public void setTopLineVisible(boolean visible) {
        mIsTopLineVisible = visible;
    }

    /**
     * 底部时间球是否可见
     * */
    public void setBottomBallVisible(boolean visible) {
        mIsBottomBallVisible = visible;
    }
}
上述代码200行左右,核心的代码也非常少,在控件的构造方法里面,把用户自定义的值获取到,如果没有定义,使用缺省值。在onMeasure里面计算好宽高,以及时间球,直线的位置,然后在控件的onDraw方法中把它绘制好。基本逻辑比较简单
(3)使用,把它加到xml布局当中,设置一些属性值,也可以在代码中重新设置一些值
代码重新设置属性,具体代码可以查看源码里面的demo
TimeLineView timeLineView = (TimeLineView) convertView.findViewById(R.id.time_line);
textView.setText(mData.get(position));

timeLineView.setTopLineVisible(false);
timeLineView.setBottomBallVisible(false);

if(position != 0) {
    timeLineView.setTopLineVisible(true);
}

if(getCount() - 1 == position) {
    timeLineView.setBottomBallVisible(true);
}

switch (position % 3) {
    case 0 :{
        timeLineView.setTopTimeBallDrawable(getResources().getDrawable(R.drawable.green_ball));
        break;
    }

    case 1 :{
        timeLineView.setTopTimeBallDrawable(getResources().getDrawable(R.drawable.yellow_ball));
        break;
    }

    case 2 :{
        timeLineView.setTopTimeBallDrawable(getResources().getDrawable(R.drawable.pink_ball));
        break;
    }
}

/*调用下面强制绘制,否则会出现显示错位问题*/
timeLineView.invalidate();
注意: 在listview中使用的时候,由于adapter使用了缓存,因此如果时间轴控件显示不一致的情况下,使用缓存会导致显示错位问题,这时可以调用invalidate强制绘制即可。具体的实现,在 https://github.com/newhope1106/TimeLineView有demo可以看到效果。




你可能感兴趣的:(android,控件,android,时间轴,控件)