Android自定义View实现垂直时间轴布局

Android自定义View实现垂直时间轴布局,第1张

概述时间轴,顾名思义就是将发生的事件按照时间顺序罗列起来,给用户带来一种更加直观的体验。京东和淘宝的物流顺序就是一个时间轴,想必大家都不陌生,如下图:

时间轴,顾名思义就是将发生的事件按照时间顺序罗列起来,给用户带来一种更加直观的体验。京东和淘宝的物流顺序就是一个时间轴,想必大家都不陌生,如下图:

分析

实现这个最常用的一个方法就是用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实现垂直时间轴布局所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/1146668.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-31
下一篇 2022-05-31

发表评论

登录后才能评论

评论列表(0条)

保存