閱讀655 返回首頁    go 技術社區[雲棲]


MyCalendar 開發日誌 (9th Apr- 11st Apr)

9th Apr

轉一篇blog,裏麵介紹了一個calendar的界麵

https://blog.csdn.net/h7870181/article/details/8960478

按照文章中給出的內容,新建了一個類CalendarView

但是我ctrl+shift+o自動補全引用頭文件後,裏麵有幾個錯誤,都和和date類型相關的,這個問題困擾了我好幾天才解決。之前總是崩潰,而且在layout界麵也看不到日曆,後來發現有一行錯誤,java.util.Date cannot be cast to java.sql.Date

之前我在init 函數裏麵new Date();總是報錯,後來我改成new Date(0)以後編譯成功了,之後還有好幾個關於getDate()函數的,我都強製轉換成Date類型的了。可是我忘記了一個關鍵的問題,代碼自動補全的時候,引用的是java.sql.Date 而不是util.Date.這就導致了我一係列後續的問題。

頭文件裏麵改成util.date以後,不用修改原blog中的任何內容,程序就可以跑通了!

正好提到util和sql的兩種date了就順便學習一下吧:

Date 的類型轉換:首先記住java.util.Date 為 java.sql.Date的父類

1.將java.util.Date 轉換為 java.sql.Date

Date d=new Date();  //java.util.Date
new java.sql.Date(d.getTime())  //將java.util.Date 轉換為 java.sql.Date

 

2.使用SimpleDateFormat 進行類型轉換

 DateFormat df=new SimpleDateFormat("yyyy-MM-dd");
 df.format(new Date()); //將java.util.Date轉換為String
 df.parse("");  //將String 轉為成java.util.Date

 

3. 將String 轉換為 java.sql.Date
String str="1989-11-23 12:25:20";
java.sql.Date date=java.sql.Date.valueof(str);


11st Apr

今天仔細看了下網上calendar的代碼,發現裏麵有幾個類是從來沒有接觸過的,研究了一下,做個總結:

SurfaceView

extends View

java.lang.Object

   ↳

android.view.View

 

   ↳

android.view.SurfaceView

由手冊可知:

SurfaceView是View類的繼承類,這個View裏內嵌了一個專門用於繪製的Surface,這個可以類似的理解成為一個在View裏的Canvas。你可以控製這個Surface的格式和尺寸。Surfaceview類則控製這個Surface在屏幕上的正確位置。

在一般情況下,應用程序的View都是在相同的GUI線程中繪製的。這個主應用程序線程同時也用來處理所有的用戶交互(例如按鈕單擊或者文本輸入)。

對於一個View的onDraw()方法,不能夠滿足將其移動到後台線程中去。因為從後台線程修改一個GUI元素會被顯式地禁止的。

當需要快速地更新View的UI,或者當前渲染代碼阻塞GUI線程的時間過長的時候,SurfaceView就是解決上述問題的最佳選擇。SurfaceView封裝了一個Surface對象,而不是Canvas。這一點很重要,因為Surface可以使用後台線程繪製。對於那些資源敏感的操作,或者那些要求快速更新或者高速幀率的地方,例如使用3D圖形,創建遊戲,或者實時預覽攝像頭,這一點特別有用。


1. 何時應該使用SurfaceView

SurfaceView使用的方式與任何View所派生的類都是完全相同的。可以像其他View那樣應用動畫,並把它們放到布局中。

SurfaceView封裝的Surface支持所有標準的Canvas方法進行繪圖,同時也支持完全的OpenGL ES 庫。

使用OpenGL,你可以在Surface上繪製任何支持2D或者3D對象,與在2D畫布上模擬相同的效果相比,這種方法可以依靠硬件加速(可用的時候)來極大地提高性能。

對於顯示動態的3D圖像來說,例如,那些使用Google Earth 功能的應用程序,或者那些提供沉浸體驗的交互式遊戲,Surface特別有用。它還是實時顯示攝像頭預覽的最佳選擇。


2. 創建一個新的SurfaceView控件

創建一個新的SurfaceView控件需要創建一個新的擴展了SurfaceView的類,並實現SurfaceHolder.Callback。

SurfaceHolder回調可以在底層的Surface被創建和銷毀的時候通知View,並傳遞給它SurfaceHolder對象的引用,其中包含了當前有效的Surface。

一個典型的SurfaceView 設計模型包括一個由Thread所派生的類,它可以接收對當前的SurfaceHolder的引用,並獨立地更新它。


3. 使用SurfaceView創建3D控件

Android完全支持OpenGL ES 3D 渲染框架,其中包含了對設備的硬件加速的支持。SurfaceView控件提供了一個表麵,可以在它上麵渲染你的OpenGL場景。

那麼我們在使用的時候可以這樣使用:

被動更新畫麵的。比如棋類,這種用view就好了。因為畫麵的更新是依賴於 onTouch 來更新,可以直接使用 invalidate。 因為這種情況下,這一次Touch和下一次的Touch需要的時間比較長些,不會產生影響。

主動更新。比如一個人在一直跑動。這就需要一個單獨的thread不停的重繪人的狀態,避免阻塞main UI thread。所以顯然view不合適,需要surfaceView來控製。


Canvas:


Canvas類主要實現了屏幕的繪製過程,其中包含了很多實用的方法,比如繪製一條路徑、區域、貼圖、畫點、畫線、渲染文本,下麵是Canvas類常用的方法:

void drawRect(RectF rect, Paint paint) //繪製區域,參數一為RectF一個區域

由這裏可以得出,如果我想把每個calendar的日期畫成圓形,隻需要調用drawCicle就可以了


void drawPath(Path path, Paint paint) //繪製一個路徑,參數一為Path路徑對象 


void  drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint)   //貼圖,參數一就是我們常規的Bitmap對象,參數二是源區域(Android123提示這裏是bitmap),參數三是目標區域(應該在canvas的位置和大小),參數四是Paint畫刷對象,因為用到了縮放和拉伸的可能,當原始Rect不等於目標Rect時性能將會有大幅損失。 


void  drawLine(float startX, float startY, float stopX, float stopY, Paint paint) //畫線,參數一起始點的x軸位置,參數二起始點的y軸位置,參數三終點的x軸水平位置,參數四y軸垂直位置,最後一個參數為Paint畫刷對象。


void  drawPoint(float x, float y, Paint paint) //畫點,參數一水平x軸,參數二垂直y軸,第三個參數為Paint對象


void drawText(String text, float x, float y, Paint paint)  //渲染文本,Canvas類除了上麵的還可以描繪文字,參數一是String類型的文本,參數二x軸,參數三y軸,參數四是Paint對象


void  drawTextOnPath(String text, Path path, float hOffset, float vOffset, Paint paint) //在路徑上繪製文本,相對於上麵第二個參數是Path路徑對象

從上麵來看我們可以看出Canvas繪製類比較簡單同時很靈活,實現一般的方法通常沒有問題,同時可以疊加的處理設計出一些效果,不過我們發現最後一個參數均為Paint對象。如果我們把Canvas當做繪畫師來看,那麼Paint就是我們繪畫的工具,比如畫筆、畫刷、顏料等等


Paint:

常見的函數:

 void  setARGB(int a, int r, int g, int b)  設置Paint對象顏色,參數一為alpha透明通道

       void  setAlpha(int a) 設置alpha不透明度,範圍為0~255

 void  setAntiAlias(boolean aa)  //是否抗鋸齒

 void  setColor(int color)  //設置顏色,這裏Android內部定義的有Color類包含了一些常見顏色定義 

 void  setFakeBoldText(boolean fakeBoldText)  //設置偽粗體文本
  
       void  setLinearText(boolean linearText)  //設置線性文本
 
       PathEffect  setPathEffect(PathEffect effect)  //設置路徑效果
 
       Rasterizer  setRasterizer(Rasterizer rasterizer) //設置光柵化
 
       Shader  setShader(Shader shader)  //設置陰影 

 void  setTextAlign(Paint.Align align)  //設置文本對齊

       void  setTextScaleX(float scaleX)  //設置文本縮放倍數,1.0f為原始
  
       void  setTextSize(float textSize)  //設置字體大小
 
       Typeface  setTypeface(Typeface typeface)  //設置字體,Typeface包含了字體的類型,粗細,還有傾斜、顏色等。

 void  setUnderlineText(boolean underlineText)  //設置下劃線



Path:

常見的函數:


void  addArc(RectF oval, float startAngle, float sweepAngle)  //為路徑添加一個多邊形
 
        void  addCircle(float x, float y, float radius, Path.Direction dir)  //給path添加圓圈
 
void  addOval(RectF oval, Path.Direction dir)  //添加橢圓形

void  addRect(RectF rect, Path.Direction dir)  //添加一個區域
  
void  addRoundRect(RectF rect, float[] radii, Path.Direction dir)  //添加一個圓角區域
 
boolean  isEmpty()  //判斷路徑是否為空
  
void  transform(Matrix matrix)  //應用矩陣變換
 
void  transform(Matrix matrix, Path dst)  //應用矩陣變換並將結果放到新的路徑中,即第二個參數。



一段簡單的代碼講canvas,paint和path都用上:

protected void onDraw(Canvas canvas) {  
    Paint paintPath=new Paint();  
    Paint paintText=new Paint();  
    paintPath.setColor(Color.Red); //路徑的畫刷為紅色  
    paintText.setColor(Color.Blue); //路徑上的文字為藍色  
    Path pathCWJ=new Path();  
    pathCWJ.addCircle(10,10,50,Direction.CW); // 半徑為50px,繪製的方向CW為順時針  
    canvas.drawPath(pathCWJ,paintPath);  
   canvas.drawTextOnPath("Android123 - CWJ",pathCWJ,0,15,paintText); //在路徑上繪製文字  
  }


13rd Apr

其實上周五的時候就對如何改變calendar的界麵單個日期的邊框有了初步想法,今天晚上經過1個晚上的實驗,終於搞定了,哈哈!將原來的代碼重新劃線,改成了圓形。

裏麵發現了一些新的知識點:

首先是在找width和height的長度時,發現用的是 surface.density = getResources().getDisplayMetrics().density; 這個函數,後來在網上找了下,發現density指的是屏幕的分辨率,這樣就可以根據不同的終端屏幕類型自動適配邊框大小了!

然後順手記一下關於分辨率,像素之間的關係吧:

px   :是屏幕的像素點
in    :英寸
mm :毫米
pt    :磅,1/72 英寸
dp   :一個基於density的抽象單位,如果一個160dpi的屏幕,1dp=1px
dip  :等同於dp
sp   :同dp相似,但還會根據用戶的字體大小偏好來縮放。
建議使用sp作為文本的單位,其它用dip

針對dip和px 的關係,做以下概述:

QVGA屏density=120;          QVGA(240*320)

HVGA屏density=160;          HVGA(320*480)

WVGA屏density=240;         WVGA(480*800)

WQVGA屏density=120          WQVGA(240*400)

density值表示每英寸有多少個顯示點,與分辨率是兩個概念。

後來我debug了一下,發現當我使用的VCD是WVGA的時候,實際抓到的density的值確實是240,然後就可以按照density來設置各種元素的長度了:

surface.width = (int) (200 * surface.density);
surface.height = (int) (215 * surface.density);
surface.paddingwidth = (int) (12*surface.density/2);
surface.gap = (int) (7*surface.density/2);
surface.linespace = (int)(20*surface.density/2);
surface.radiu = (int) (surface.width-surface.paddingwidth*2-surface.gap*6)/7;


同時再記錄下關於

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)函數

1、什麼時候調用onMeasure方法? 
當控件的父元素正要放置該控件時調用.父元素會問子控件一個問題,“你想要用多大地方啊?”,然後傳入兩個參數——widthMeasureSpec和heightMeasureSpec.
這兩個參數指明控件可獲得的空間以及關於這個空間描述的元數據.
更好的方法是你傳遞View的高度和寬度到setMeasuredDimension方法裏,這樣可以直接告訴父控件,需要多大地方放置子控件.
  接下來的代碼片段給出了如何重寫onMeasure.注意,調用的本地空方法是來計算高度和寬度的.它們會譯解widthHeightSpec和heightMeasureSpec值,並計算出合適的高度和寬度值.
java代碼:

  1. <font size="4">@Override
  2. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  3. int measuredHeight = measureHeight(heightMeasureSpec);
  4. int measuredWidth = measureWidth(widthMeasureSpec);
  5. setMeasuredDimension(measuredHeight, measuredWidth);
  6. }
  7. private int measureHeight(int measureSpec) {
  8. // Return measured widget height.
  9. }
  10. private int measureWidth(int measureSpec) {
  11. // Return measured widget width.
  12. }</font>
複製代碼
邊界參數——widthMeasureSpec和heightMeasureSpec ,效率的原因以整數的方式傳入。在它們使用之前,首先要做的是使用MeasureSpec類的靜態方法getMode和getSize來譯解,如下麵的片段所示:
java代碼:
  1. <font size="4">int specMode = MeasureSpec.getMode(measureSpec);
  2. int specSize = MeasureSpec.getSize(measureSpec);</font>
複製代碼
依據specMode的值,(MeasureSpec有3種模式分別是UNSPECIFIED, EXACTLY和AT_MOST)
如果是AT_MOST,specSize 代表的是最大可獲得的空間; 
如果是EXACTLY,specSize 代表的是精確的尺寸; 
如果是UNSPECIFIED,對於控件尺寸來說,沒有任何參考意義。

2、那麼這些模式和我們平時設置的layout參數fill_parent, wrap_content有什麼關係呢?
經過代碼測試就知道,當我們設置width或height為fill_parent時,容器在布局時調用子 view的measure方法傳入的模式是EXACTLY,因為子view會占據剩餘容器的空間,所以它大小是確定的。
而當設置為 wrap_content時,容器傳進去的是AT_MOST, 表示子view的大小最多是多少,這樣子view會根據這個上限來設置自己的尺寸。當子view的大小設置為精確值時,容器傳入的是EXACTLY, 而MeasureSpec的UNSPECIFIED模式目前還沒有發現在什麼情況下使用。 
   View的onMeasure方法默認行為是當模式為UNSPECIFIED時,設置尺寸為mMinWidth(通常為0)或者背景drawable的最小尺寸,當模式為EXACTLY或者AT_MOST時,尺寸設置為傳入的MeasureSpec的大小。 
   有個觀念需要糾正的是,fill_parent應該是子view會占據剩下容器的空間,而不會覆蓋前麵已布局好的其他view空間,當然後麵布局子 view就沒有空間給分配了,所以fill_parent屬性對布局順序很重要。以前所想的是把所有容器的空間都占滿了,難怪google在2.2版本裏把fill_parent的名字改為match_parent.
  在兩種情況下,你必須絕對的處理這些限製。在一些情況下,它可能會返回超出這些限製的尺寸,在這種情況下,你可以讓父元素選擇如何對待超出的View,使用裁剪還是滾動等技術。
  接下來的框架代碼給出了處理View測量的典型實現:
java代碼:
  1. <font size="4">@Override
  2. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  3. int measuredHeight = measureHeight(heightMeasureSpec);
  4. int measuredWidth = measureWidth(widthMeasureSpec);
  5. setMeasuredDimension(measuredHeight, measuredWidth);
  6. }
  7. private int measureHeight(int measureSpec) {
  8. int specMode = MeasureSpec.getMode(measureSpec);
  9. int specSize = MeasureSpec.getSize(measureSpec);
  10. // Default size if no limits are specified.
  11. int result = 500;
  12. if (specMode == MeasureSpec.AT_MOST){
  13. // Calculate the ideal size of your
  14. // control within this maximum size.
  15. // If your control fills the available
  16. // space return the outer bound.
  17. result = specSize;
  18. }
  19. else if (specMode == MeasureSpec.EXACTLY){
  20. // If your control can fit within these bounds return that value.
  21. result = specSize;
  22. }
  23. return result;
  24. }
  25. private int measureWidth(int measureSpec) {
  26. int specMode = MeasureSpec.getMode(measureSpec);
  27. int specSize = MeasureSpec.getSize(measureSpec);
  28. // Default size if no limits are specified.
  29. int result = 500;
  30. if (specMode == MeasureSpec.AT_MOST){
  31. // Calculate the ideal size of your control
  32. // within this maximum size.
  33. // If your control fills the available space
  34. // return the outer bound.
  35. result = specSize;
  36. }
  37. else if (specMode == MeasureSpec.EXACTLY){
  38. // If your control can fit within these bounds return that value.
  39. result = specSize;
  40. }
  41. return result;
  42. }</font>
複製代碼
一個MeasureSpec封裝了父布局傳遞給子布局的布局要求,每個MeasureSpec代表了一組寬度和高度的要求。一個MeasureSpec由大小和模式組成。它有三種模式:UNSPECIFIED(未指定),父元素部隊自元素施加任何束縛,子元素可以得到任意想要的大小;EXACTLY(完全),父元素決定自元素的確切大小,子元素將被限定在給定的邊界裏而忽略它本身大小;AT_MOST(至多),子元素至多達到指定大小的值。
  它常用的三個函數:
  1.static int getMode(int measureSpec):根據提供的測量值(格式)提取模式(上述三個模式之一)
  2.static int getSize(int measureSpec):根據提供的測量值(格式)提取大小值(這個大小也就是我們通常所說的大小)
  3.static int makeMeasureSpec(int size,int mode):根據提供的大小值和模式創建一個測量值(格式)

  這個類的使用呢,通常在view組件的onMeasure方法裏麵調用但也有少數例外,看看幾個例子:
  a.首先一個我們常用到的一個有用的函數,View.resolveSize(int size,int measureSpec)
  1. <font size="4">public static int resolveSize(int size, int measureSpec) {
  2.          int result = size;
  3.          int specMode = MeasureSpec.getMode(measureSpec);
  4.          int specSize =  MeasureSpec.getSize(measureSpec);
  5.          switch (specMode) {
  6.          case MeasureSpec.UNSPECIFIED:
  7.              result = size;
  8.              break;
  9.          case MeasureSpec.AT_MOST:
  10.              result = Math.min(size, specSize);
  11.              break;
  12.          case MeasureSpec.EXACTLY:
  13.              result = specSize;
  14.              break;
  15.          }
  16.          return result;
  17.      }</font>
複製代碼
上麵既然要用到measureSpec值,那自然表示這個函數通常是在onMeasure方法裏麵調用的。簡單說一下,這個方法的主要作用就是根據你提供的大小和模式,返回你想要的大小值,這個裏麵根據傳入模式的不同來做相應的處理。
  再看看MeasureSpec.makeMeasureSpec方法,實際上這個方法很簡單:
  1. <font size="4"> public static int makeMeasureSpec(int size, int mode) {
  2.             return size + mode;
  3.         }</font>
複製代碼
這樣大家不難理解size跟measureSpec區別了。看看它的使用吧,ListView.measureItem(View child)
  1. <font size="4">private void measureItem(View child) {
  2.          ViewGroup.LayoutParams p = child.getLayoutParams();
  3.          if (p == null) {
  4.              p = new ViewGroup.LayoutParams(
  5.                      ViewGroup.LayoutParams.MATCH_PARENT,
  6.                      ViewGroup.LayoutParams.WRAP_CONTENT);
  7.          }
  8.          int childWidthSpec = ViewGroup.getChildMeasureSpec(mWidthMeasureSpec,
  9.                  mListPadding.left + mListPadding.right, p.width);
  10.          int lpHeight = p.height;
  11.          int childHeightSpec;
  12.          if (lpHeight > 0) {
  13.              childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight, MeasureSpec.EXACTLY);
  14.          } else {
  15.              childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
  16.          }
  17.          child.measure(childWidthSpec, childHeightSpec);
  18.      }</font>
複製代碼
measureSpec方法通常在ViewGroup中用到,它可以根據模式(MeasureSpec裏麵的三個)可以調節子元素的大小。
  注意,使用EXACTLY和AT_MOST通常是一樣的效果,如果你要區別他們,那麼你就要使用上麵的函數View.resolveSize(int size,int measureSpec)返回一個size值,然後使用你的view調用setMeasuredDimension(int,int)函數。
  1. <font size="4">protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {
  2.         mMeasuredWidth = measuredWidth;
  3.         mMeasuredHeight = measuredHeight;
  4.         mPrivateFlags |= MEASURED_DIMENSION_SET;
  5.     }</font>
複製代碼
然後你調用view.getMeasuredWidth,view.getMeasuredHeigth 返回的就是上麵函數裏的mMeasuredWidth,mMeasuredHeight的值。
mode共有三種情況,取值分別為MeasureSpec.UNSPECIFIED, MeasureSpec.EXACTLY, MeasureSpec.AT_MOST。
MeasureSpec.EXACTLY是精確尺寸,當我們將控件的layout_width或layout_height指定為具體數值時如andorid:layout_width="50dip",或者為FILL_PARENT是,都是控件
大小已經確定的情況,都是精確尺寸。
MeasureSpec.AT_MOST 是最大尺寸,當控件的layout_width或layout_height指定為WRAP_CONTENT時,控件大小一般隨著控件的子空間或內容進行 變化,此時控件尺寸
隻要不超過父控件允許的最大尺寸即可。因此,此時的mode是AT_MOST,size給出了父控件允許的最大尺寸。
MeasureSpec.UNSPECIFIED是未指定尺寸,這種情況不多,一般都是父控件是AdapterView,通過measure方法傳入的模式。





















最後更新:2017-04-03 12:56:06

  上一篇:go Cocos2dx 3.0 過渡篇(一) 初體驗
  下一篇:go C++ Virtual詳解