655
技術社區[雲棲]
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
↳ |
||
|
↳ |
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代碼:
-
<font size="4">@Override
-
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-
int measuredHeight = measureHeight(heightMeasureSpec);
-
int measuredWidth = measureWidth(widthMeasureSpec);
-
setMeasuredDimension(measuredHeight, measuredWidth);
-
}
-
private int measureHeight(int measureSpec) {
-
// Return measured widget height.
-
}
-
private int measureWidth(int measureSpec) {
-
// Return measured widget width.
- }</font>
java代碼:
-
<font size="4">int specMode = MeasureSpec.getMode(measureSpec);
- int specSize = MeasureSpec.getSize(measureSpec);</font>
如果是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代碼:
-
<font size="4">@Override
-
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-
int measuredHeight = measureHeight(heightMeasureSpec);
-
int measuredWidth = measureWidth(widthMeasureSpec);
-
setMeasuredDimension(measuredHeight, measuredWidth);
-
}
-
private int measureHeight(int measureSpec) {
-
int specMode = MeasureSpec.getMode(measureSpec);
-
int specSize = MeasureSpec.getSize(measureSpec);
-
// Default size if no limits are specified.
-
int result = 500;
-
if (specMode == MeasureSpec.AT_MOST){
-
// Calculate the ideal size of your
-
// control within this maximum size.
-
// If your control fills the available
-
// space return the outer bound.
-
result = specSize;
-
}
-
else if (specMode == MeasureSpec.EXACTLY){
-
// If your control can fit within these bounds return that value.
-
result = specSize;
-
}
-
return result;
-
}
-
private int measureWidth(int measureSpec) {
-
int specMode = MeasureSpec.getMode(measureSpec);
-
int specSize = MeasureSpec.getSize(measureSpec);
-
// Default size if no limits are specified.
-
int result = 500;
-
if (specMode == MeasureSpec.AT_MOST){
-
// Calculate the ideal size of your control
-
// within this maximum size.
-
// If your control fills the available space
-
// return the outer bound.
-
result = specSize;
-
}
-
else if (specMode == MeasureSpec.EXACTLY){
-
// If your control can fit within these bounds return that value.
-
result = specSize;
-
}
-
return result;
- }</font>
它常用的三個函數:
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)
-
<font size="4">public static int resolveSize(int size, int measureSpec) {
-
int result = size;
-
int specMode = MeasureSpec.getMode(measureSpec);
-
int specSize = MeasureSpec.getSize(measureSpec);
-
switch (specMode) {
-
case MeasureSpec.UNSPECIFIED:
-
result = size;
-
break;
-
case MeasureSpec.AT_MOST:
-
result = Math.min(size, specSize);
-
break;
-
case MeasureSpec.EXACTLY:
-
result = specSize;
-
break;
-
}
-
return result;
- }</font>
再看看MeasureSpec.makeMeasureSpec方法,實際上這個方法很簡單:
-
<font size="4"> public static int makeMeasureSpec(int size, int mode) {
-
return size + mode;
- }</font>
-
<font size="4">private void measureItem(View child) {
-
ViewGroup.LayoutParams p = child.getLayoutParams();
-
if (p == null) {
-
p = new ViewGroup.LayoutParams(
-
ViewGroup.LayoutParams.MATCH_PARENT,
-
ViewGroup.LayoutParams.WRAP_CONTENT);
-
}
-
int childWidthSpec = ViewGroup.getChildMeasureSpec(mWidthMeasureSpec,
-
mListPadding.left + mListPadding.right, p.width);
-
int lpHeight = p.height;
-
int childHeightSpec;
-
if (lpHeight > 0) {
-
childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight, MeasureSpec.EXACTLY);
-
} else {
-
childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
-
}
-
child.measure(childWidthSpec, childHeightSpec);
- }</font>
注意,使用EXACTLY和AT_MOST通常是一樣的效果,如果你要區別他們,那麼你就要使用上麵的函數View.resolveSize(int size,int measureSpec)返回一個size值,然後使用你的view調用setMeasuredDimension(int,int)函數。
-
<font size="4">protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {
-
mMeasuredWidth = measuredWidth;
-
mMeasuredHeight = measuredHeight;
-
mPrivateFlags |= MEASURED_DIMENSION_SET;
- }</font>
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