1. 程式人生 > >自定義view,仿淘寶快遞的物流資訊的時間軸

自定義view,仿淘寶快遞的物流資訊的時間軸

 學了Android有一段時間了,一直沒有時間寫部落格,趁著週末有點空,就把自己做的一些東西寫下來. 一方面鍛鍊一下自己的寫文件的能力,另一方面分享程式碼的同時也希望能與大家交流一下技術,共同學習,共同進步.
 廢話不多少說,我們先來看看我們自定義view要實現的效果:
 
 效果圖

自定義屬性

<resources>
    <declare-styleable name="TimeLineView">
        <attr name="timelineRadius" format="dimension" />
        <attr
name="timelineHeadRadius" format="dimension" />
<attr name="timelineHeadColor" format="color" /> <attr name="timelineOtherColor" format="color" /> <attr name="timelineRadiusDistance" format="dimension" /> <attr name="timelineCount" format="integer"
/>
<attr name="timelineMarginTop" format="dimension" /> <attr name="timelineWidth" format="dimension" /> </declare-styleable> </resources>

onDraw方法
關鍵方法,寫了大量的程式碼註釋,希望大家能看明白。

@Override
    protected void onDraw(Canvas canvas) {
        //預設設定時間軸的位置位於view的中間
viewWidth = getMeasuredWidth() / 2; //設定第一個節點的顏色 mPaint.setColor(timelineHeadColor); /** * 根據時間軸的節點數目,畫對應的節點和軸 */ for (int j = 1; j <= timelineCount; j++) { /** * 當j==1,畫第一個節點的時候,有點特殊,我們需要在節點的外面再換一個圓環 */ if (j == 1) { //畫筆設定為空心 canvas.drawCircle(viewWidth, timelineHeadRadius + marginTop, timelineRadius, mPaint); mPaint.setStyle(Paint.Style.STROKE); mPaint.setStrokeWidth(5.0f); //畫第一個節點外圍的圓環 canvas.drawCircle(viewWidth, timelineHeadRadius + marginTop, timelineHeadRadius, mPaint); //設定畫筆顏色,畫其他時間節點的顏色 mPaint.setColor(timelineOtherColor); //畫筆設定為實心 mPaint.setStyle(Paint.Style.FILL); /** * 畫第一個節點下面的軸 */ canvas.drawRect(new Rect(viewWidth - timelineWidth / 2, (int) (2 * timelineHeadRadius + marginTop) + 5, viewWidth + timelineWidth / 2, (int) (2 * timelineHeadRadius + timelineRadiusDistance + marginTop + 5)), mPaint); continue; } /** * 畫時間軸的節點,即畫圓形 * 圓心的x都是一樣的,view的中間 * 圓心的y的計算是根據節點的位置來計算的,例如:第一個節點的y是根據第一個節點距離上面的距離加上第一個節點的半徑:timelineHeadRadius + marginTop * 其餘的節點就是在一個節點的y的基礎上,加上兩倍半徑和節點之間的軸的長度*節點數:(2 * timelineRadius + timelineRadiusDistance) * (j - 1) + timelineHeadRadius - timelineRadius + marginTop * */ canvas.drawCircle(viewWidth, (2 * timelineRadius + timelineRadiusDistance) * (j - 1) + 2 * timelineHeadRadius - timelineRadius + marginTop, timelineRadius, mPaint); /** *   * 畫其餘的軸 * left:每個軸距離左邊距離都是一樣的   時間軸的中心位置-1/2的時間軸的寬度 viewWidth - timelineWidth / 2 * top: (int) (j * (2 * timelineRadius + timelineRadiusDistance) - timelineRadiusDistance + 2 * (timelineHeadRadius-timelineRadius)+ marginTop)  * right:每個軸距離右邊距離都是一樣的   時間軸的中心位置+1/2的時間軸的寬度 viewWidth + timelineWidth / 2 * bottom: (int) (j * (2 * timelineRadius + timelineRadiusDistance) + 2 * (timelineHeadRadius-timelineRadius)+ marginTop) */ canvas.drawRect(new Rect(viewWidth - timelineWidth / 2, (int) (j * (2 * timelineRadius + timelineRadiusDistance) - timelineRadiusDistance + 2 * (timelineHeadRadius - timelineRadius) + marginTop), viewWidth + timelineWidth / 2, (int) (j * (2 * timelineRadius + timelineRadiusDistance) + 2 * (timelineHeadRadius - timelineRadius) + marginTop)), mPaint); } }

最後附上全部程式碼

package com.demo.zes.timelinedemo;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.View;

/**
 * TODO: document your custom view class.
 */
public class TimeLineView extends View {


    private Paint mPaint;
    /**
     * 第一個節點的外半徑
     */
    private float timelineHeadRadius;
    /**
     * 第一個節點的顏色值
     */
    private int timelineHeadColor;
    /**
     * 第二個節點的顏色值
     */
    private int timelineOtherColor;
    /**
     * 時間線的節點數
     */
    private int timelineCount;
    /**
     * 時間軸的位置
     */
    private int viewWidth;
    /**
     * 時間軸到距離頂部的距離
     */
    private int marginTop;
    /**
     * 時間軸的節點的半徑
     */
    private int timelineRadius;
    /**
     * 時間軸節點之間的距離
     */
    private int timelineRadiusDistance;
    /**
     * 時間軸的寬度
     */
    private int timelineWidth;
    /**
     * 時間軸的高度
     */
    private float timeLineViewHeight;

    public TimeLineView(Context context) {
        this(context, null);
    }

    public TimeLineView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public TimeLineView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(context, attrs, defStyle);
    }

    /**
     * 初始化
     *
     * @param context
     * @param attrs
     * @param defStyle
     */
    private void init(Context context, AttributeSet attrs, int defStyle) {

        final TypedArray a = getContext().obtainStyledAttributes(
                attrs, R.styleable.TimeLineView, defStyle, 0);
        timelineRadiusDistance = (int) a.getDimension(R.styleable.TimeLineView_timelineRadiusDistance, convertDIP2PX(context, 20));
        timelineHeadRadius = a.getDimension(R.styleable.TimeLineView_timelineHeadRadius, convertDIP2PX(context, 10));
        timelineRadius = (int) a.getDimension(R.styleable.TimeLineView_timelineRadius, convertDIP2PX(context, 5));
        timelineHeadColor = a.getColor(R.styleable.TimeLineView_timelineHeadColor, Color.parseColor("#73be36"));
        timelineOtherColor = a.getColor(R.styleable.TimeLineView_timelineOtherColor, Color.parseColor("#e0e0e0"));
        timelineCount = a.getInteger(R.styleable.TimeLineView_timelineCount, 0);
        timelineWidth = (int) a.getDimension(R.styleable.TimeLineView_timelineWidth, convertDIP2PX(context, 1));
        marginTop = (int) a.getDimension(R.styleable.TimeLineView_timelineMarginTop, convertDIP2PX(context, 50));
        a.recycle();

        mPaint = new Paint();
        mPaint.setAntiAlias(true);

    }


    @Override
    protected void onDraw(Canvas canvas) {
        //預設設定時間軸的位置位於view的中間
        viewWidth = getMeasuredWidth() / 2;
        //設定第一個節點的顏色
        mPaint.setColor(timelineHeadColor);
        /**
         * 根據時間軸的節點數目,畫對應的節點和軸
         */
        for (int j = 1; j <= timelineCount; j++) {

            /**
             * 當j==1,畫第一個節點的時候,有點特殊,我們需要在節點的外面再換一個圓環
             */
            if (j == 1) {
                //畫筆設定為空心
                canvas.drawCircle(viewWidth, timelineHeadRadius + marginTop, timelineRadius, mPaint);
                mPaint.setStyle(Paint.Style.STROKE);
                mPaint.setStrokeWidth(5.0f);
                //畫第一個節點外圍的圓環
                canvas.drawCircle(viewWidth, timelineHeadRadius + marginTop, timelineHeadRadius, mPaint);
                //設定畫筆顏色,畫其他時間節點的顏色
                mPaint.setColor(timelineOtherColor);
                //畫筆設定為實心
                mPaint.setStyle(Paint.Style.FILL);
                /**
                 * 畫第一個節點下面的軸
                 */
                canvas.drawRect(new Rect(viewWidth - timelineWidth / 2, (int) (2 * timelineHeadRadius + marginTop) + 5, viewWidth + timelineWidth / 2, (int) (2 * timelineHeadRadius + timelineRadiusDistance + marginTop + 5)), mPaint);
                continue;
            }
            /**
             * 畫時間軸的節點,即畫圓形
             * 圓心的x都是一樣的,view的中間
             * 圓心的y的計算是根據節點的位置來計算的,例如:第一個節點的y是根據第一個節點距離上面的距離加上第一個節點的半徑:timelineHeadRadius + marginTop
             * 其餘的節點就是在一個節點的y的基礎上,加上兩倍半徑和節點之間的軸的長度*節點數:(2 * timelineRadius + timelineRadiusDistance) * (j - 1) + timelineHeadRadius - timelineRadius + marginTop
             *
             */
            canvas.drawCircle(viewWidth, (2 * timelineRadius + timelineRadiusDistance) * (j - 1) + 2 * timelineHeadRadius - timelineRadius + marginTop, timelineRadius, mPaint);
            /**
             *  
             *  畫其餘的軸
             *  left:每個軸距離左邊距離都是一樣的   時間軸的中心位置-1/2的時間軸的寬度  viewWidth - timelineWidth / 2
             *  top:   (int) (j * (2 * timelineRadius + timelineRadiusDistance) - timelineRadiusDistance + 2 * (timelineHeadRadius-timelineRadius)+ marginTop) 
             *  right:每個軸距離右邊距離都是一樣的   時間軸的中心位置+1/2的時間軸的寬度  viewWidth + timelineWidth / 2
             *  bottom: (int) (j * (2 * timelineRadius + timelineRadiusDistance) + 2 * (timelineHeadRadius-timelineRadius)+ marginTop)
             */

            canvas.drawRect(new Rect(viewWidth - timelineWidth / 2, (int) (j * (2 * timelineRadius + timelineRadiusDistance) - timelineRadiusDistance + 2 * (timelineHeadRadius - timelineRadius) + marginTop), viewWidth + timelineWidth / 2, (int) (j * (2 * timelineRadius + timelineRadiusDistance) + 2 * (timelineHeadRadius - timelineRadius) + marginTop)), mPaint);
        }
    }

    public float getTimelineHeadRadius() {
        return timelineHeadRadius;
    }

    public void setTimelineHeadRadius(float timelineHeadRadius) {

        this.timelineHeadRadius = timelineHeadRadius;
        invalidate();
    }

    public int getTimelineHeadColor() {
        return timelineHeadColor;
    }

    public void setTimelineHeadColor(int timelineHeadColor) {

        this.timelineHeadColor = timelineHeadColor;
        invalidate();
    }

    public int getTimelineOtherColor() {
        return timelineOtherColor;
    }

    public void setTimelineOtherColor(int timelineOtherColor) {
        this.timelineOtherColor = timelineOtherColor;
        invalidate();
    }

    public int getTimelineCount() {
        return timelineCount;
    }

    public void setTimelineCount(int timelineCount) {
        this.timelineCount = timelineCount;
        invalidate();
    }

    public int getMarginTop() {
        return marginTop;
    }

    public void setMarginTop(int marginTop) {

        this.marginTop = marginTop;
        invalidate();
    }

    public int getTimelineRadius() {
        return timelineRadius;
    }

    public void setTimelineRadius(int timelineRadius) {

        this.timelineRadius = timelineRadius;
        invalidate();
    }

    public int getTimelineRadiusDistance() {
        return timelineRadiusDistance;
    }

    public void setTimelineRadiusDistance(int timelineRadiusDistance) {

        this.timelineRadiusDistance = timelineRadiusDistance;
        invalidate();
    }

    public int getTimelineWidth() {
        return timelineWidth;
    }

    public void setTimelineWidth(int timelineWidth) {
        this.timelineWidth = timelineWidth;
    }

    public float getTimeLineViewHeight() {
        this.timeLineViewHeight = getMarginTop() + getTimelineCount() * (2 * getTimelineRadius() + getTimelineRadiusDistance());
        return timeLineViewHeight;
    }

    public void setTimeLineViewHeight(float timeLineViewHeight) {
        this.timeLineViewHeight = timeLineViewHeight;
        invalidate();

    }

    public int getViewWidth() {
        return viewWidth;
    }

    public void setViewWidth(int viewWidth) {
        this.viewWidth = viewWidth;
        invalidate();
    }

    /**
     * 轉換dip為px
     */
    public static int convertDIP2PX(Context context, int dip) {
        float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dip * scale + 0.5f * (dip >= 0 ? 1 : -1));
    }

}

相關推薦

定義view仿快遞物流資訊時間

 學了Android有一段時間了,一直沒有時間寫部落格,趁著週末有點空,就把自己做的一些東西寫下來. 一方面鍛鍊一下自己的寫文件的能力,另一方面分享程式碼的同時也希望能與大家交流一下技術,共同學習,共同進步.  廢話不多少說,我們先來看看我們自定義view

android 定義ViewGroup實現仿的商品詳情頁

最近公司在新版本上有一個需要, 要在首頁新增一個滑動效果, 具體就是仿照X寶的商品詳情頁, 拉到頁面底部時有一個粘滯效果,  如下圖 X東的商品詳情頁,如果使用者繼續向上拉的話就進入商品圖文描述介面: 剛開始是想拿來主義,直接從網上找個現成的demo來用, 但是網上無一

android定義ProgressBar(仿)的載入效果

三種方式實現自定義圓形頁面載入中效果的進度條 To get a ProgressBar in the default theme that is to be used on white/light back ground, use one of the inverse st

一個簡單的定義View仿圓形進度條

一個很簡單的小例子,,整理一下 效果圖 上程式碼,程式碼中應該註釋很清楚了,就不再贅述,是模仿洋大神寫的,然後自己又加了一點兒自己的理解。 程式碼如下 package com.example.roundview; import android.content.Cont

Android定義控制元件-仿ios客戶端天貓商品詳情介面動效

效果圖 原始碼和例子 效果描述 一個自定義控制元件繼承自ScrollView,下拉時header會放大鬆開後會恢復原狀,上滑時header會被下面的內容吃掉蓋住而且會稍稍往上滑,在header高度範圍內滑動時導航欄背景和導航欄的按鈕會反向改變透明度形成一種對比

定義ViewGroup實現仿的商品詳情頁

最近公司在新版本上有一個需要, 要在首頁新增一個滑動效果, 具體就是仿照X寶的商品詳情頁, 拉到頁面底部時有一個粘滯效果,  如下圖 X東的商品詳情頁,如果使用者繼續向上拉的話就進入商品圖文描述介面: 剛開始是想拿來主義,直接從網上找個現成的demo來用, 但是網上無一

Android定義控制元件——仿、網易、彩票等廣告條、Banner的製作

最近翻看以前的某專案時,發現了一個極其常用的效果——廣告條,或者也稱不上自定義元件,但是使用頻率還是相當普遍的。 開啟市面上各大App主介面,或多或少會出現這樣的東西,甚至一個應用中出現N多個,這種展示廣告的效果,不僅動態效果好,而且眾所周知的“不佔屏”,想想在手機裝

android定義View仿通訊錄側邊欄滑動實現A-Z字母檢索

我們的手機通訊錄一般都有這樣的效果,如下圖: OK,這種效果大家都見得多了,基本上所有的android手機通訊錄都有這樣的效果。那我們今天就來看看這個效果該怎麼實現。 一.概述 1.頁面功能分析 整體上來說,左邊是一個ListView,右邊是一個自定義View,但

Android 仿微信聯絡人Demo(定義ViewViewgroup)

上週在某部落格發現博主分享了一篇很經典的程式---------聯絡人效果。感覺很神祕很強大,但在閱讀和理解博主的demo的同時也發現了一些冗餘和不完美。於是帶著寶寶的痛一咬牙自己開工了,大約花了一週的時間(當然我白天還得上班的),做出了這種效果。如下圖: now跟著

Android定義View仿QQ音樂歌詞滾動控制元件!

最近在以QQ音樂為樣板做一個手機音樂播放器,原始碼下篇博文放出。今天我想聊的是這個QQ音樂播放器中歌詞顯示控制元件的問題,和小夥伴們一起來探討怎麼實現這個歌詞滾動的效果。OK,廢話不多說,先來看看效果圖:好,接下來我們就來看看怎麼實現這樣一個效果。本文主要包括如下幾方面內容:

定義View流式佈局

  寫的比較基礎, 備忘使用。 public class FlowLayout extends ViewGroup { public FlowLayout(Context context) { this(context, null); }

定義view繪製階段進度progressBar階段與圖片和文字對齊

  沒用seekbar或者progressbar原生控制元件,通過繪製實現。只講下有用的思想,無關屬性不解釋,也不用看。 主要看onDraw方法程式碼: 繪製背景線,canvas.drawRect線繪製了第一條線,因為需要漸變,可以看到canvas.drawPath是從第

無需定義View徹底放棄shapeselector吧

前言 作為一個android程式設計師,對於shape、selector這兩個標籤一定不陌生。每當UI設計師給我們設計出一個個button背景的時候,我們就需要去drawable資料夾下去新建一個bg_xxx.xml,然後很多時候區別僅僅是一個邊框的顏色或者填充的顏色。這就導致

無需定義View徹底解放shapeselector吧

作者的思路很贊,希望對大家有啟發。 1 前言 作為一個android程式設計師,對於shape、selector這兩個標籤一定不陌生。每當UI設計師給我們設計出一個個button背景的時候,我們就需要去drawable資料夾下去新建一個bg_xxx.xml,然後

定義View仿ios分段選擇器

一:效果 1.1 可動態新增或刪除tab,更改指定tab的文字。 二:實現思路 自定義view,實現效果 動態建立textview,有幾個tab建立幾個textview 第一個tab和最後一個tab為圓角矩形,其餘的為直角矩形,通過shape檔案完成 預設選

定義view貝塞爾曲線實現水波紋效果的動畫

作為一名碼農,除了用基本的姿勢去搬磚,還應該get一些炫酷的技能,用高逼格的姿態去搬磚。而貝塞爾曲線無疑是炫酷技能之一。 簡介: Bézier curve(貝塞爾曲線)是應用於二維圖形應用程式的數學曲線。 曲線定義:起始點、終止點(也稱錨點)、控制點。通過調整控

ViewFlipper的使用仿頭條垂直滾動廣告條

ViewFlipper是安卓自帶的控制元件,很多人可能很少知道這個控制元件,這個控制元件很簡單,也很好理解,能不能用上實戰就看你們的本事了。下面是淘寶頭條廣告的原效果 下面是我們今天要實現的效果,圖片是Gif,執行效果是很流暢的,由於這個圖片反應有點慢,會浪費大家點時間,所以我把它調快了,

android 定義view在xml中引用內部類View

java.lang.ClassCastException: android.view.View cannot be cast to com.voice.VoiceFragment$AnimationView E/AndroidRuntime( 3543): at com.voice.VoiceFragme

不用定義view放棄使用selector shape

前言 作為一個android程式設計師,對於shape、selector這兩個標籤一定不陌生。每當UI設計師給我們設計出一個個button背景的時候,我們就需要去drawable資料夾下去新建一個bg_xxx.xml,然後很多時候區別僅僅是一個邊框的顏色或者填充的顏色。這就導致了很多非常

定義view可拖拽進度和吸附效果的圓形進度條

前言 最近接到一個需求,第一眼看到ui互動效果時,瞬間想對產品小哥說“尼瑪,這麼會玩,你咋不上天”。確認了具體互動細節,喝了兩口農夫三拳,開始了兩耳不聞窗外事,一心只想擼程式碼的過程。 先上ui效果 說明: 外圈弧形上面是進度的標記點,預設在12點位置,也是