仿淘宝物流信息控件

Posted z8z87878

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了仿淘宝物流信息控件相关的知识,希望对你有一定的参考价值。

——–商女不知亡国恨 隔江犹唱后庭花

先看下效果吧

在自定义一个控件前,我们一定要先想好自己怎么可以实现它,当然这是废话…..不过我是心血来潮的那种,大多都没有成功。。唉,学的还远远不够啊。做这个之前我是想过的,我一开始是想继承ViewGroup去实现它,可是想了一阵子,发现毫无头绪,然后我就想着继承View去实现它了,一拍脑袋,觉得可以实现,所以我就去做了。当然遇到了一些困难但学到了新的知识是吧,后话后话。万事开头难,我们想好了继承View来做,就是自己画呗是不是。做不熟的东西第一步是最难的。就让我们开始画第一个条目吧,迈出这最难的一步

画第一个条目,我们也要先分析下怎么画,首选我们要确定一个条目的宽度高度,宽度这里是屏幕的宽度,高度观察淘宝的物流控件,条目的高度由上往下是由 ’信息距离条目顶部的margin + 信息的高度 + 时间和信息的margin + 时间的高度 + 时间距离条目底部的margin‘ 构成。确定了高度!接着我们就可以画了,先画哪个东西随意是不是,所以来开始画吧

public class WuliuView extends View 

    private int mMaginTop;          //物流信息距离顶部magin
    private int mMaginMsg;          //时间距离物流信息magin
    private int mWidth;             //屏幕宽度
    private int mLineWidth;         //竖直线占条目的宽度
    private TextPaint mTextPaint;   //画物流信息的画笔

    private int mTextHeight;        //物流信息的高度
    private int mMaginBottom;       //时间距离条目底部的宽度
    private int mHeight;                    //条目高度
    private StaticLayout mStaticLayout;
    private Rect mBound;
    private String mMsg;
    private Paint mTimePaint;        //时间画笔
    private int mTimeHeight;         //时间文字高度

    public WuliuView(Context context) 
        super(context);
        init();
    

    public WuliuView(Context context, AttributeSet attrs) 
        super(context, attrs);
        init();
    

    public WuliuView(Context context, AttributeSet attrs, int defStyleAttr) 
        super(context, attrs, defStyleAttr);
        init();
    

    private void init() 

        mMaginTop = dp2px(15);            //15dp
        mMaginBottom = mMaginTop;

        mMaginMsg = dp2px(5);             //时间和信息距离5dp

        mLineWidth = dp2px(40);          //竖直线占40dp

        mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);  //抗锯齿
        mTextPaint.setDither(true);                           //仿抖动
        mTextPaint.setColor(0xFF00FF00);
        mTextPaint.setTextSize(dp2px(13));                  //
        mTextPaint.setTextAlign(Paint.Align.LEFT);          //左下角对齐

        mMsg = "【大天朝】已经签收,签收人是赵日天";


        mBound = new Rect(); //通过边框可以获得文字的高度
        mTextPaint.getTextBounds(mMsg, 0, mMsg.length(), mBound);
        mTextHeight = mBound.height(); //得到文字高度

        mTimePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mTimePaint.setDither(true);
        mTimePaint.setColor(0xFF00FF00);
        mTimePaint.setTextSize(dp2px(10));
        mTimePaint.setTextAlign(Paint.Align.LEFT);
        mTimePaint.setTypeface(Typeface.DEFAULT_BOLD);

        Rect boun = new Rect(); //同上理得到时间文字的高度
        mTimePaint.getTextBounds("7",0,1,boun);
        mTimeHeight = boun.height();

    

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) 

        mWidth = MeasureSpec.getSize(widthMeasureSpec);

        mHeight = mMaginTop + mTextHeight + mMaginMsg + mTimeHeight + mMaginBottom;//条目高度


        setMeasuredDimension(mWidth, mHeight); //由此确定条目的高度

    



    //
    @Override
    protected void onDraw(Canvas canvas) 


        mTimePaint.setColor(0xFF00FF00);

        canvas.drawText(mMsg,mLineWidth,mMaginTop+mTextHeight,mTextPaint); //画信息

        canvas.drawText(getFormateTime(new Date(System.currentTimeMillis())), mLineWidth, mMaginTop + mTextHeight +
                mMaginMsg + mTimeHeight, mTimePaint);//画时间

        mTimePaint.setColor(Color.GRAY);//左边那条线 //参数一线起点x坐标,参数二线起点y坐标,参数3,4是终点坐标
        canvas.drawLine(mLineWidth/2,mMaginTop + mBound.height(),mLineWidth/2,mHeight,mTimePaint);

        mTimePaint.setColor(0x550AE93E);//外面的更大的圆,但带点透明
        canvas.drawCircle(mLineWidth/2,mMaginTop + mBound.height() /2,mBound.height()/2,mTimePaint);

        mTimePaint.setColor(0xFF00FF00); //内部更小的圆不透明和外面圆形成光晕
        canvas.drawCircle(mLineWidth/2,mMaginTop + mBound.height() /2,mBound.height()/2 - dp2px(2),mTimePaint);

        mTimePaint.setColor(Color.GRAY);  //画底部那条线
        canvas.drawLine(mLineWidth,mHeight,mWidth,mHeight,mTimePaint);
    

    public int dp2px(float dp) 

        return (int) (getResources().getDisplayMetrics().density * dp + .5f);
    

    public String getFormateTime(Date date) 
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        return sdf.format(date);
    



这样一看,好像解决了是不是,我们来试试多一点文字看一看

mMsg = "【大天朝】已经签收,签收人是赵日天,感谢使用飞毛腿快递,期待再次为您服务什么鬼啊,我了个去,你大爷的啊,怎么会这样子呢,一点都不科学13177786453sdawdsadwfawadwawddfsa";


我靠,不换行啊,是不是瞬间感觉谷歌太不靠谱了。。。。然后这里我也不会了,但是textview是View啊,它可以支持换行,所以一定有换行的方法,点进去看看源码,我了个草,谷歌的当然是大神,但是再大的神,尼玛那代码也不忍直视,基本没注释,有注释也是英文不详细,没过六级的表示压力有点大,上网搜吧,去网上搜说StaticLayout这个东东可以换行,所以这个问题就可以解决了,来看看这个东东的用法

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) 

        mWidth = MeasureSpec.getSize(widthMeasureSpec);

        mStaticLayout = new StaticLayout(mMsg,
                mTextPaint, mWidth - mLineWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 1.0f, false);

        mTextHeight = mStaticLayout.getHeight();


        mHeight = mMaginTop + mTextHeight + mMaginMsg + mTimeHeight + mMaginBottom;//条目高度


        setMeasuredDimension(mWidth, mHeight); //由此确定条目的高度

    


    //
    @Override
    protected void onDraw(Canvas canvas) 


        mTimePaint.setColor(0xFF00FF00);
        canvas.save();

        canvas.translate(mLineWidth, mMaginTop);

        mStaticLayout.draw(canvas);  //画信息

        canvas.restore();


        canvas.drawText(getFormateTime(new Date(System.currentTimeMillis())), mLineWidth, mMaginTop + mTextHeight +
                mMaginMsg + mTimeHeight, mTimePaint);//画时间

先看构造函数

mStaticLayout = new StaticLayout(mMsg,
                mTextPaint, mWidth - mLineWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 1.0f, false);

第一个参数是要画的信息;第二个参数是对应的画笔;第三个参数是画一行的长度;第四个是画的方式,正常就是从左上角开始画,我们原来的canvas.drawText默认是从左下角开始画的;第五个参数,网上人说是列间距,第六个参数网上说是行间距,第七个不解,后面这三个参数对于我们这个控件没影响,我也不知道干嘛用的,我改变了参数值好像没什么变化啊,我没看出来,点进去看源码也没有介绍,所以真心觉得是大坑。。。然后来看它在onDraw是怎么画的

 canvas.save();

        canvas.translate(mLineWidth, mMaginTop);

        mStaticLayout.draw(canvas);  //画信息

        canvas.restore();

这里canvas.translate(mLineWidth, mMaginTop);画板要下移mMaginTop是因为StaticLayout.draw(canvas)默认是在画板的(0,0)点画的,我们修改不了画的点,而且它是从字的左上角开始画的,所以根据我们画的物流信息是要距离条目顶部mMaginTop的距离,距离左边mLineWidth,所以画板下移mMaginTop,右移mLineWidth这时就画在我们像画的那个位置了,画完后,再让画板canvas.restore();恢复原来canvas.save();的位置,这里可能有人会想,画板都移回去了,那不是物流信息也移回去了,你前面还移个什么劲浪费表情。这里是不对的,其实画板每次画一个东西是画在一个图层上而不是画在画板上的,画板一开始默认和你的view坐标系一样,这两者之间有一个图层,我们每次画一个东西,画在这个图层上,然后图层在显示在view上,当再画一个东西的时候,又有一个新的图层再中间,画完,在与原来的view结合形成新的view。依次循环。因为我们的StaticLayout.draw(canvas)它只能画在画板的(0,0)点,一开始画板与图层坐标系对应,所以当我们把它canvas.translate(mLineWidth, mMaginTop)这样子画板的(0.0)点就对应图层的(mLineWidth, mMaginTop)的点了,画是画在图层上的,不是画在画板上的,所以这正是我们想要的!。一般我们是不会去移的,因为坐标系对应,这里没办法只能移,后面画的东西坐标系要对应,所以我们要恢复,让它坐标系对应。好了,这就是我的理解,说了这么多来看看效果吧

对了,我上面的文字高度改成了这样

 mTextHeight = mStaticLayout.getHeight();

因为通过mBoung.hright()获得的只是一行的高度。 嗯,看到效果图发现有问题,很难看是不是,是这样子的StaticLayout不允许字母截断,什么鸟意思呢,就是说它很聪明,认为你的字母是一个单词,截断了就破坏意思了!改改吧

mMsg = "【大天朝】已经签收,签收人是赵日天,感谢使用飞毛腿快递,期待再次为您服务什么鬼啊,我了个去,你大爷的啊,怎么会这样子呢,一点都不科学13177786453saddfsa";


当然我们的物流信息也不会有那么多字母,说实话,我就没看过超过三个字母。好了,最难得一步是不是被我们完成了。其实就是这么简单是不是。没做过真的不知道啊。然后就是画多条条目了,这我就直接贴代码了吧,里面也有注释。有兴趣的要下载源码的,我会在审核通过后再评论区把下载路径贴出来

/**
 * Created by Root on 2016/7/8.
 */
public class WuliuView extends View 


    private int mMaginTop;          //物流信息距离顶部magin
    private int mMaginMsg;          //时间距离物流信息magin
    private int mWidth;             //屏幕宽度
    private int mLineWidth;         //竖直线占条目的宽度
    private TextPaint mTextPaint;   //画物流信息的画笔

    private int mTextHeight;        //物流信息的高度
    private int mMaginBottom;       //时间距离条目底部的宽度
    private int mHeight;                    //条目高度
    private Rect mBound;
    private String mMsg;
    private Paint mTimePaint;        //时间画笔
    private int mTimeHeight;         //时间文字高度

    private ArrayList<WuLiuData> mDatas;
    private ArrayList<StaticLayout> mStaticLayoutList;

    public WuliuView(Context context, ArrayList<WuLiuData> datas) 
        super(context);
        init(datas);
    


    private void init(ArrayList<WuLiuData> datas) 

        mMaginTop = dp2px(15);
        mMaginBottom = mMaginTop;

        mMaginMsg = dp2px(5);  //时间和信息距离5dp

        mLineWidth = dp2px(40);

        mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
        mTextPaint.setDither(true);
        mTextPaint.setColor(0xFF00FF00);
        mTextPaint.setTextSize(dp2px(13));
        mTextPaint.setTextAlign(Paint.Align.LEFT);

       mMsg = "【大天朝】已经签收,签收人是赵日天,感谢使用飞毛腿快递,期待再次为您服务什么鬼啊,我了个去,你大爷的啊,怎么会这样子呢,一点都不科学13177786453sdaw";

        mBound = new Rect();
        mTextPaint.getTextBounds(mMsg, 0, mMsg.length(), mBound);

        mTimePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mTimePaint.setDither(true);
        mTimePaint.setColor(0xFF00FF00);
        mTimePaint.setTextSize(dp2px(10));
        mTimePaint.setTextAlign(Paint.Align.LEFT);
        mTimePaint.setTypeface(Typeface.DEFAULT_BOLD);

        Rect boun = new Rect();
        mTimePaint.getTextBounds("7",0,1,boun);
        mTimeHeight = boun.height();

        mDatas = datas;          //物流数据

        mStaticLayoutList = new ArrayList<StaticLayout>();

    

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) 

        mWidth = MeasureSpec.getSize(widthMeasureSpec);

        mStaticLayoutList.clear();   //这里会重复测量几次,所以我们每次进来的时候需要清零一下
        mHeight = 0;
        for (int i = mDatas.size() - 1; i >= 0; i--)   //因为数据要从后面时间的往前面的时间绘制

            mStaticLayoutList.add(new StaticLayout(mDatas.get(i).mMsg,mTextPaint,mWidth - mLineWidth, Layout
 .Alignment.ALIGN_NORMAL,1.0f,1.0f,true));
        

        for (int i = 0; i < mStaticLayoutList.size(); i++)  //每个条目的高度

            mTextHeight = mStaticLayoutList.get(i).getHeight();
            mHeight += mMaginTop + mTextHeight + mMaginMsg + mTimeHeight +mMaginBottom; //从左到右依次为最顶上margin,描述信息高度,时间与描述的margin,时间文字高度,与底部的margin
        

        setMeasuredDimension(mWidth, mHeight);

    

    @Override
    protected void onDraw(Canvas canvas) 

        mHeight = 0;
        for (int i = 0; i < mStaticLayoutList.size(); i++) 

            StaticLayout staticLayout = mStaticLayoutList.get(i);
            if (i == 0)      //第一次因为要改变一些颜色,所以这里简单点直接分开处理
                mTimePaint.setColor(0xFF00FF00);
                mTextPaint.setColor(0xFF00FF00);
                canvas.save();

                canvas.translate(mLineWidth, mMaginTop);

                staticLayout.draw(canvas);  //画描述信息

                canvas.restore();

                mTextHeight = staticLayout.getHeight();
                mHeight += mMaginTop + mTextHeight + mMaginMsg + mTimeHeight + mMaginBottom;


                canvas.drawText(mDatas.get(i).mTime, mLineWidth, mHeight - mMaginBottom, mTimePaint);

                mTimePaint.setColor(Color.GRAY);


                canvas.drawLine(mLineWidth/2,mMaginTop + mBound.height(),mLineWidth/2,mHeight,mTimePaint); //竖直线

                mTimePaint.setColor(0x550AE93E);
                canvas.drawCircle(mLineWidth/2,mMaginTop + mBound.height() /2,mBound.height()/2,mTimePaint);//最近的信息圆,带透明

                mTimePaint.setColor(0xFF00FF00);
                canvas.drawCircle(mLineWidth/2,mMaginTop + mBound.height() /2,mBound.height()/2 - dp2px(2),
 mTimePaint);//小一点,不透明

                mTimePaint.setColor(Color.GRAY);
                canvas.drawLine(mLineWidth,mHeight,mWidth,mHeight,mTimePaint);//底部线

                mTextPaint.setColor(Color.GRAY);
            else  //不是第一个条目,这里不改变颜色了


                canvas.save();

                canvas.translate(mLineWidth, mMaginTop + mHeight);

                staticLayout.draw(canvas);

                canvas.restore();

                mTextHeight = staticLayout.getHeight();

                int addHeight = mMaginTop + mTextHeight + mMaginMsg + mTimeHeight + mMaginBottom;
                mHeight += addHeight;


                canvas.drawText(mDatas.get(i).mTime, mLineWidth, mHeight - mMaginBottom, mTimePaint); //画时间

                //              起点横坐标       起点纵坐标         终点横坐标   终点纵坐标
                canvas.drawLine(mLineWidth/2,mHeight - addHeight,mLineWidth/2,mHeight,mTimePaint); //竖直线

                canvas.drawLine(mLineWidth,mHeight,mWidth,mHeight,mTimePaint);//底部线
            
        


    

    public int dp2px(float dp) 

        return (int) (getResources().getDisplayMetrics().density * dp + .5f);
    

    public  String getFormateTime(Date date) 
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        return sdf.format(date);
    

以上是关于仿淘宝物流信息控件的主要内容,如果未能解决你的问题,请参考以下文章

Android之仿京东淘宝的自动无限轮播控件

Android基础控件——SeekBar的使用仿淘宝滑动验证

仿淘宝双色球走势图自定义控件

仿淘宝双色球走势图自定义控件

仿淘宝双色球走势图自定义控件

仿淘宝双色球走势图自定义控件