时间轴,顾名思义就是将发生的事件按照时间顺序罗列起来,给用户带来一种更加直观的体验。京东和淘宝的物流顺序就是一个时间轴,想必大家都不陌生,如下图:
分析
实现这个最常用的一个方法就是用ListVIEw,我这里用继承linearLayout的方式来实现。首先定义了一些自定义属性:
attrs.xml
<?xml version="1.0" enCoding="utf-8"?> <resources> <declare-styleable name="TimelineLayout"> <!--时间轴左偏移值--> <attr name="line_margin_left" format="dimension"/> <!--时间轴上偏移值--> <attr name="line_margin_top" format="dimension"/> <!--线宽--> <attr name="line_stroke_wIDth" format="dimension"/> <!--线的颜色--> <attr name="line_color" format="color"/> <!--点的大小--> <attr name="point_size" format="dimension"/> <!--点的颜色--> <attr name="point_color" format="color"/> <!--图标--> <attr name="icon_src" format="reference"/> </declare-styleable> </resources>
TimelineLayout.java
package com.jackIE.timeline; import androID.content.Context; import androID.content.res.TypedArray; import androID.graphics.Bitmap; import androID.graphics.Canvas; import androID.graphics.Paint; import androID.graphics.drawable.BitmapDrawable; import androID.support.annotation.Nullable; import androID.util.AttributeSet; import androID.vIEw.VIEw; import androID.Widget.linearLayout; /** * Created by JackIE on 2017/3/8. * 时间轴控件 */ public class TimelineLayout extends linearLayout { private Context mContext; private int mlinemarginleft; private int mlinemargintop; private int mlinestrokeWIDth; private int mlinecolor;; private int mPointSize; private int mPointcolor; private Bitmap mIcon; private Paint mlinePaint; //线的画笔 private Paint mPointPaint; //点的画笔 //第一个点的位置 private int mFirstX; private int mFirstY; //最后一个图标的位置 private int mLastX; private int mLastY; public TimelineLayout(Context context) { this(context,null); } public TimelineLayout(Context context,@Nullable AttributeSet attrs) { this(context,attrs,0); } public TimelineLayout(Context context,@Nullable AttributeSet attrs,int defStyleAttr) { super(context,defStyleAttr); TypedArray ta = context.obtainStyledAttributes(attrs,R.styleable.TimelineLayout); mlinemarginleft = ta.getDimensionPixelOffset(R.styleable.TimelineLayout_line_margin_left,10); mlinemargintop = ta.getDimensionPixelOffset(R.styleable.TimelineLayout_line_margin_top,0); mlinestrokeWIDth = ta.getDimensionPixelOffset(R.styleable.TimelineLayout_line_stroke_wIDth,2); mlinecolor = ta.getcolor(R.styleable.TimelineLayout_line_color,0xff3dd1a5); mPointSize = ta.getDimensionPixelSize(R.styleable.TimelineLayout_point_size,8); mPointcolor = ta.getDimensionPixelOffset(R.styleable.TimelineLayout_point_color,0xff3dd1a5); int iconRes = ta.getResourceID(R.styleable.TimelineLayout_icon_src,R.drawable.ic_ok); BitmapDrawable drawable = (BitmapDrawable) context.getResources().getDrawable(iconRes); if (drawable != null) { mIcon = drawable.getBitmap(); } ta.recycle(); setwillNotDraw(false); initVIEw(context); } private voID initVIEw(Context context) { this.mContext = context; mlinePaint = new Paint(); mlinePaint.setAntiAlias(true); mlinePaint.setDither(true); mlinePaint.setcolor(mlinecolor); mlinePaint.setstrokeWIDth(mlinestrokeWIDth); mlinePaint.setStyle(Paint.Style.FILL_AND_stroke); mPointPaint = new Paint(); mPointPaint.setAntiAlias(true); mPointPaint.setDither(true); mPointPaint.setcolor(mPointcolor); mPointPaint.setStyle(Paint.Style.FILL); } @OverrIDe protected voID onDraw(Canvas canvas) { super.onDraw(canvas); drawTimeline(canvas); } private voID drawTimeline(Canvas canvas) { int childCount = getChildCount(); if (childCount > 0) { if (childCount > 1) { //大于1,证明至少有2个,也就是第一个和第二个之间连成线,第一个和最后一个分别有点和icon drawFirstPoint(canvas); drawLastIcon(canvas); drawBetweenline(canvas); } else if (childCount == 1) { drawFirstPoint(canvas); } } } private voID drawFirstPoint(Canvas canvas) { VIEw child = getChildAt(0); if (child != null) { int top = child.gettop(); mFirstX = mlinemarginleft; mFirstY = top + child.getpaddingtop() + mlinemargintop; //画圆 canvas.drawCircle(mFirstX,mFirstY,mPointSize,mPointPaint); } } private voID drawLastIcon(Canvas canvas) { VIEw child = getChildAt(getChildCount() - 1); if (child != null) { int top = child.gettop(); mLastX = mlinemarginleft; mLastY = top + child.getpaddingtop() + mlinemargintop; //画图 canvas.drawBitmap(mIcon,mLastX - (mIcon.getWIDth() >> 1),mLastY,null); } } private voID drawBetweenline(Canvas canvas) { //从开始的点到最后的图标之间,画一条线 canvas.drawline(mFirstX,mLastX,mlinePaint); for (int i = 0; i < getChildCount() - 1; i++) { //画圆 int top = getChildAt(i).gettop(); int y = top + getChildAt(i).getpaddingtop() + mlinemargintop; canvas.drawCircle(mFirstX,y,mPointPaint); } } public int getlinemarginleft() { return mlinemarginleft; } public voID setlinemarginleft(int linemarginleft) { this.mlinemarginleft = linemarginleft; invalIDate(); } }
从上面的代码可以看出,分三步绘制,首先绘制开始的实心圆,然后绘制结束的图标,然后在开始和结束之间先绘制一条线,然后在线上在绘制每个步骤的实心圆。
activity_main.xml
<?xml version="1.0" enCoding="utf-8"?> <linearLayout xmlns:androID="http://schemas.androID.com/apk/res/androID" xmlns:app="http://schemas.androID.com/apk/res-auto" androID:layout_wIDth="match_parent" androID:layout_height="match_parent" androID:orIEntation="vertical"> <linearLayout androID:layout_wIDth="match_parent" androID:layout_height="50dp" androID:weightSum="2"> <button androID:ID="@+ID/add_item" androID:layout_wIDth="0dp" androID:layout_height="match_parent" androID:layout_weight="1" androID:text="add"/> <button androID:ID="@+ID/sub_item" androID:layout_wIDth="0dp" androID:layout_height="match_parent" androID:layout_weight="1" androID:text="sub"/> </linearLayout> <linearLayout androID:layout_wIDth="match_parent" androID:layout_height="wrap_content" androID:orIEntation="horizontal" androID:weightSum="2"> <button androID:ID="@+ID/add_margin" androID:layout_wIDth="0dp" androID:layout_weight="1" androID:layout_height="wrap_content" androID:text="+"/> <button androID:ID="@+ID/sub_margin" androID:layout_wIDth="0dp" androID:layout_weight="1" androID:layout_height="wrap_content" androID:text="-"/> </linearLayout> <TextVIEw androID:ID="@+ID/current_margin" androID:layout_wIDth="match_parent" androID:layout_height="40dp" androID:gravity="center" androID:text="current line margin left is 25dp"/> <ScrollVIEw androID:layout_wIDth="match_parent" androID:layout_height="wrap_content" androID:scrollbars="none"> <com.jackIE.timeline.TimelineLayout androID:ID="@+ID/timeline_layout" androID:layout_wIDth="match_parent" androID:layout_height="wrap_content" app:line_margin_left="25dp" app:line_margin_top="8dp" androID:orIEntation="vertical" androID:background="@androID:color/white"> </com.jackIE.timeline.TimelineLayout> </ScrollVIEw> </linearLayout>
MainActivity.java
package com.jackIE.timeline; import androID.os.Bundle; import androID.support.v7.app.AppCompatActivity; import androID.vIEw.LayoutInflater; import androID.vIEw.VIEw; import androID.Widget.button; import androID.Widget.TextVIEw; public class MainActivity extends AppCompatActivity implements VIEw.OnClickListener { private button addItembutton; private button subItembutton; private button addmarginbutton; private button submarginbutton; private TextVIEw mCurrentmargin; private TimelineLayout mTimelineLayout; @OverrIDe protected voID onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentVIEw(R.layout.activity_main); initVIEw(); } private voID initVIEw() { addItembutton = (button) findVIEwByID(R.ID.add_item); subItembutton = (button) findVIEwByID(R.ID.sub_item); addmarginbutton= (button) findVIEwByID(R.ID.add_margin); submarginbutton= (button) findVIEwByID(R.ID.sub_margin); mCurrentmargin= (TextVIEw) findVIEwByID(R.ID.current_margin); mTimelineLayout = (TimelineLayout) findVIEwByID(R.ID.timeline_layout); addItembutton.setonClickListener(this); subItembutton.setonClickListener(this); addmarginbutton.setonClickListener(this); submarginbutton.setonClickListener(this); } private int index = 0; private voID addItem() { VIEw vIEw = LayoutInflater.from(this).inflate(R.layout.item_timeline,mTimelineLayout,false); ((TextVIEw) vIEw.findVIEwByID(R.ID.tv_action)).setText("步骤" + index); ((TextVIEw) vIEw.findVIEwByID(R.ID.tv_action_time)).setText("2017年3月8日16:55:04"); ((TextVIEw) vIEw.findVIEwByID(R.ID.tv_action_status)).setText("完成"); mTimelineLayout.addVIEw(vIEw); index++; } private voID subItem() { if (mTimelineLayout.getChildCount() > 0) { mTimelineLayout.removeVIEws(mTimelineLayout.getChildCount() - 1,1); index--; } } @OverrIDe public voID onClick(VIEw v) { switch (v.getID()){ case R.ID.add_item: addItem(); break; case R.ID.sub_item: subItem(); break; case R.ID.add_margin: int currentmargin = UIHelper.pxToDip(this,mTimelineLayout.getlinemarginleft()); mTimelineLayout.setlinemarginleft(UIHelper.diptopx(this,++currentmargin)); mCurrentmargin.setText("current line margin left is " + currentmargin + "dp"); break; case R.ID.sub_margin: currentmargin = UIHelper.pxToDip(this,--currentmargin)); mCurrentmargin.setText("current line margin left is " + currentmargin + "dp"); break; default: break; } } }
item_timeline.xml
<?xml version="1.0" enCoding="utf-8"?> <relativeLayout xmlns:androID="http://schemas.androID.com/apk/res/androID" androID:layout_wIDth="match_parent" androID:layout_height="wrap_content" androID:paddingleft="65dp" androID:paddingtop="20dp" androID:paddingRight="20dp" androID:paddingBottom="20dp"> <TextVIEw androID:ID="@+ID/tv_action" androID:layout_wIDth="wrap_content" androID:layout_height="wrap_content" androID:textSize="14sp" androID:textcolor="#1a1a1a" androID:text="测试一"/> <TextVIEw androID:ID="@+ID/tv_action_time" androID:layout_wIDth="wrap_content" androID:layout_height="wrap_content" androID:textSize="12sp" androID:textcolor="#8e8e8e" androID:layout_below="@ID/tv_action" androID:layout_margintop="10dp" androID:text="2017年3月8日16:49:12"/> <TextVIEw androID:ID="@+ID/tv_action_status" androID:layout_wIDth="wrap_content" androID:layout_height="wrap_content" androID:textSize="14sp" androID:textcolor="#3dd1a5" androID:layout_alignParentRight="true" androID:text="完成"/> </relativeLayout>
附上像素工具转化的工具类:
package com.jackIE.timeline; import androID.content.Context; /** * Created by JackIE on 2017/3/8. */ public final class UIHelper { private UIHelper() throws InstantiationException { throw new InstantiationException("This class is not for instantiation"); } /** * dip转px */ public static int diptopx(Context context,float dip) { return (int) (dip * context.getResources().getdisplayMetrics().density + 0.5f); } /** * px转dip */ public static int pxToDip(Context context,float pxValue) { final float scale = context.getResources().getdisplayMetrics().density; return (int) (pxValue / scale + 0.5f); } }
效果图如下:
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持编程小技巧。
总结以上是内存溢出为你收集整理的Android自定义View实现垂直时间轴布局全部内容,希望文章能够帮你解决Android自定义View实现垂直时间轴布局所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)