仿淘寶物流資訊控制元件
——–商女不知亡國恨 隔江猶唱後庭花
先看下效果吧
在自定義一個控制元件前,我們一定要先想好自己怎麼可以實現它,當然這是廢話…..不過我是心血來潮的那種,大多都沒有成功。。唉,學的還遠遠不夠啊。做這個之前我是想過的,我一開始是想繼承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);
}
}
相關推薦
仿淘寶物流資訊控制元件
——–商女不知亡國恨 隔江猶唱後庭花 先看下效果吧 在自定義一個控制元件前,我們一定要先想好自己怎麼可以實現它,當然這是廢話…..不過我是心血來潮的那種,大多都沒有成功。。唉,學的還遠遠不夠啊。做這個之前我是想過的,我一開始是想繼承ViewGro
獲取物流資訊並動態展示(仿淘寶物流資訊)
最終效果如圖 前端程式碼如下 <div class="track-rcol"> <divclass="track-list">
仿淘寶物流時間線的實現
物流時間線是根據物流狀態改變而改變的一種動態效果。 貼張圖: 下面看一下自定義的View: 第一步: //初始化引數 private void init(AttributeSet attrs) { TypedArray typedArray = getConte
仿支付寶快遞資訊控制元件
程式碼地址:https://github.com/ky48302430/myflowstatess 效果圖 Xml 佈局 <declare-styl
Android 放淘寶物流資訊
一個第三方的東西,挺好使的一.maven { url 'https://jitpack.io' }二.compile 'com.github.dcq123:StepView:v0.0.2'三.物流實體類public class StepItemData { priv
仿淘寶京東評分控制元件
由於在專案中碰到了評分控制元件,使用的地方也比較多,像淘寶,京東這些都有,於是就寫了一個。 京東淘寶和我們自己最終實現的效果: 實現思路:繪製5張沒有選中的圖片,自定義屬性,屬性包括選中狀態下的圖片和正常狀態下的圖片,以及圖片數量,圖片間距。
自定義一個分段控制元件(仿QQ頂部的分段控制元件)
在Android當中並沒有分段控制元件,怎樣去實現一個這樣的分段控制元件,那就要自己去自定義了; 先看一張效果圖: 整體的思路: 1. 設定分段控制元件背景資源 2. 自定義segmentView繼承LinearLayout 3. 設定分段控制元件的屬性(文字的顏色、文
Android開發技巧——定製仿微信圖片裁剪控制元件
拍照——裁剪,或者是選擇圖片——裁剪,是我們設定頭像或上傳圖片時經常需要的一組操作。上篇講了Camera的使用,這篇講一下我對圖片裁剪的實現。 背景 下面的需求都來自產品。 裁剪圖片要像微信那樣,拖動和放大的是圖片,裁剪框不動。 裁剪框外的內容要有半透
android仿ios的時間滾動控制元件WheelView
<LinearLayout android:layout_width="200dp" android:orientation="horizontal" android:layout_gravity=
android仿ios實現分段選擇控制元件UISegmentedControl
在ios7中有一種扁平風格的控制元件叫做分段選擇控制元件UISegmentedControl,控制元件上橫放或豎放著幾個被簡單線條隔開的按鈕,每次點選能切換不同的按鈕和按鈕所對應的介面,比如qq客戶端V6.5.3版本中訊息頁與電話頁分離就是用的這種原理。但是很可
Android自定義View,高仿QQ音樂歌詞滾動控制元件!
最近在以QQ音樂為樣板做一個手機音樂播放器,原始碼下篇博文放出。今天我想聊的是這個QQ音樂播放器中歌詞顯示控制元件的問題,和小夥伴們一起來探討怎麼實現這個歌詞滾動的效果。OK,廢話不多說,先來看看效果圖:好,接下來我們就來看看怎麼實現這樣一個效果。本文主要包括如下幾方面內容:
仿淘寶物流彈框
兩種實現效果: 第一種是背景是透明度的: ViewPager+TabLayout +fragment是在一個dialog形式的Activity上的 Dialogactivity的佈局: <?xml version="1.0" encoding="utf-
Android仿iOS左右滑動開關控制元件(Android4.0以上適用)
上週使用Android的switch模仿iOS的左右滑動開關控制元件,程式碼如下: aty_switch.xml: <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android
Android自定義控制元件實戰——仿淘寶商品瀏覽介面
用手機淘寶瀏覽商品詳情時,商品圖片是放在後面的,在第一個ScrollView滾動到最底下時會有提示,繼續拖動才能瀏覽圖片。仿照這個效果寫一個出來並不難,只要定義一個Layout管理兩個ScrollView就行了,當第一個ScrollView滑到底部時,再次向上滑動進入第二
android 仿淘寶、京東商品詳情頁 向上拖動檢視圖文詳情控制元件
/** * Created by baoyunlong on 16/6/8. */ public class PullUpToLoadMore extends ViewGroup { public static String TAG = PullUpToLoadMore.class.getName
Android自定義控制元件-仿淘寶ios客戶端天貓商品詳情介面動效
效果圖 原始碼和例子 效果描述 一個自定義控制元件繼承自ScrollView,下拉時header會放大鬆開後會恢復原狀,上滑時header會被下面的內容吃掉蓋住而且會稍稍往上滑,在header高度範圍內滑動時導航欄背景和導航欄的按鈕會反向改變透明度形成一種對比
Android自定義控制元件——仿淘寶、網易、彩票等廣告條、Banner的製作
最近翻看以前的某專案時,發現了一個極其常用的效果——廣告條,或者也稱不上自定義元件,但是使用頻率還是相當普遍的。 開啟市面上各大App主介面,或多或少會出現這樣的東西,甚至一個應用中出現N多個,這種展示廣告的效果,不僅動態效果好,而且眾所周知的“不佔屏”,想想在手機裝
Android 開發:(三)安卓常用控制元件以及仿《微門戶》登入介面實現
一、常用控制元件: 1、文字類控制元件 TextView 負責展示文字,非編輯 EditText 可編輯文字控制元件 2、按鈕類控制元件 Button 按鈕 ImageButton 圖片按鈕 RadioButton與RadioGroup 單
C# WPF 低仿網易雲音樂(PC)Banner動畫控制元件
原文: C# WPF 低仿網易雲音樂(PC)Banner動畫控制元件 由於技術有限沒能做到一模一樣的動畫,只是粗略地做了一下。動畫有點生硬,還有就是沒做出網易雲音樂的立體感。程式碼非常簡單粗暴,而且我也寫有很多註釋,這裡就不多囉嗦了,直接貼程式碼。 算了,囉嗦幾句。原理是這樣的,在自定義使用者控制元件內新
C# WPF 低仿網易雲音樂(PC)歌詞控制元件
原文: C# WPF 低仿網易雲音樂(PC)歌詞控制元件 提醒:本篇部落格記錄了修改的過程,廢話比較多,需要專案原始碼和看演示效果的直接拉到文章最底部~ 網易雲音樂獲取歌詞的api地址 http://music.163.com/api/song/media?id=歌曲ID 填