您好,登錄后才能下訂單哦!
這篇文章將為大家詳細講解有關如何在Android中利用View實現一個垂直時間軸布局,文章內容質量較高,因此小編分享給大家做個參考,希望大家閱讀完這篇文章后對相關知識有一定的了解。
時間軸,顧名思義就是將發生的事件按照時間順序羅列起來,給用戶帶來一種更加直觀的體驗。
分析
實現這個最常用的一個方法就是用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, attrs, 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, mFirstY, mLastX, mLastY, 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, mPointSize, 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, mTimelineLayout.getLineMarginLeft()); mTimelineLayout.setLineMarginLeft(UIHelper.dipToPx(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實現一個垂直時間軸布局就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。