編輯:關於Android編程
零、選擇Glide
為什麼圖片加載我首先推薦Glide?
圖片加載框架用了不少,從afinal框架的afinalBitmap,Xutils的BitmapUtils,老牌框架universalImageLoader,著名開源組織square的picasso,google推薦的glide到FaceBook推出的fresco。這些我前前後後都體驗過,那麼面對這麼多的框架,該如何選擇呢?下面簡單分析下我的看法。
afinal和Xuils在github上作者已經停止維護了,開源社區最新的框架要屬KJFramework,不過這種快速開發框架看似很好用,功能也應有盡有,小型項目也罷,大型項目我不是很推薦,這樣做項目的耦合度太高,一旦出現停止維護,而新的問題不斷增加,沒人處理就麻煩了。
在glide和fresco還未出來的時候,當時最火的莫過於universalImageLoader和picasso了,當時覺得universalImageLoader配置相對picasso麻煩,雖然提供了各種配置,但是沒有實踐過,根本不知道如何配置,還不如都采用默認配置,就選擇了picasso作為圖片加載框架,用了近一年的時間,沒有太大的問題,且使用簡單,或許是因為之前的項目太過於簡單,周期也並不是很長,還有使用eclipse開發,一個很大的問題一直都沒有暴露出來,換上了最新的Android Studio可以清晰的看到各種性能相關的監控,如cpu還有內存監控,終於知道了之前做的項目都那麼的卡頓的罪魁禍首,picasso加載稍微大一點的圖片就特別耗內存,通常一個listView或者頂部滑動廣告欄都含有多張圖片,這使得做出的頁面只要含圖片較多就異常卡頓(之前的時候還把它歸結為測試機不好),知道這一點後我就有點想把picasso給替換掉,但這一次我不能那麼粗心。
測試了picasso,glide,universalImageLoader,fresco這四個框架,測試內容大概有以下幾項,內存測試,大圖片測試,小圖片測試,本地圖片,網絡圖片當然還結合官方文檔體驗其特色功能,內存測試中,glide,universalImageLoader,fresco表現都非常優秀,picasso這一點上實在是太糟糕了,小圖片差別也不是很大,稍微大點圖片內存消耗就要比其他高出幾倍,這一點上證明了我的猜想,picasso不能再用了,下面一項項分析其他框架,在高於2M左右大圖測試中fresco的表現則和picasso一樣直接神馬都不顯示,項目中要實現大圖預覽功能,這點上是不行的,接著看universalImageLoader和glide在這幾項測試中成績都很好,到底該如何選擇呢?
因為我項目之前用的picasso,glide從用法上幾乎就是另一個picasso,從picasso轉移到glide相對改動較少,還有一點就是這個項目是google在維護,我也能給它更多的信任,相比較universalImageLoader,glide可以支持gif和短視頻,後期也需要用到,這裡不得不談一下glide優秀的緩存機制了,glide圖片緩存默認使用RGB565相當於ARGB8888可以節省不少的空間,支持與activity,fragment,application生命周期的聯動,更智能管理圖片請求當然還有其他的擴展更多可以看?glide介紹?當然,glide的方法數量比universalImageLoader多了1000多個,遇到64k問題的會比較關注這個。
剛才只是掠過fresco,其實我對他的期待還是蠻大的,因為剛出來還有居多不穩定的地方,裡面存在著大量吸引著我的功能,支持webps格式(和jpg一樣都是有損壓縮格式,webps相同質量圖片更節省空間),支持漸進式jpeg,可以輕松的定制image的各種屬性,支持多圖請求和圖片復用,並支持手勢縮放和旋轉等等,更多介紹?fresco,當然,實際用的時候並沒有那麼好,很多功能都有待完善。
還有一點細節的地方要注意的,最好不要直接拿來用,至少經過自己簡單的封裝,而不是直接在項目中使用,一個簡單的例子,後期圖片過多,可能需要另外配置一台機器單獨存放圖片,主機地址做成可配置,可不要因為一個簡單的需求又要加班了
更多。
一、Glide3.0以來的新特性
1.動態的GIF圖片加載:
Glide.with(context).load(...).asBitmap() //顯示gif靜態圖片 Glide.with(context).load(...).asGif() //顯示gif動態圖片
2.本地視頻快照:
Glide現在還可以把視頻解碼為一張圖片:
Glide.with(context).load(“視頻路徑“)
3.對縮略圖的支持:
//加載yourView1/10尺寸的縮略圖,然後加載全圖 Glide.with(yourFragment).load(yourUrl).thumbnail(0.1f).into(yourView)
4.生命周期集成
同時將Activity/Fragment作為with()參數的好處是:圖片加載會和Activity/Fragment的生命周期保持一致,
請求會在onStop的時候自動暫停,
在onStart的時候重新啟動,gif的動畫也會在onStop的時候停止,以免在後台消耗電量。
5.轉碼
Glide的.toBytes()和.transcode()方法允許在後台獲取、解碼和轉換一個圖片,你可以將一張圖片轉換成更多有用的圖片格式,比如,上傳一張250*250的圖片
Glide.with(context) .load(“/user/profile/photo/path”) .asBitmap() .toBytes() .centerCrop() .into(new SimpleTarget<byte[]>(250, 250) { @Override public void onResourceReady(byte[] data, GlideAnimation anim) { // Post your bytes to a background thread and upload them here. } });
6.動畫:3.x加入了cross fades和View的屬性動畫的支持
比如
(.animate(ViewPropertyAnimation.Animator))
7. 網絡模塊可以選擇OkHttp或者Volley的支持
You can now choose to use either OkHttp, or Volley, or Glide's HttpUrlConnection default as your network stack.
Volley和OkHttp可以在gradle文件當中添加依賴,注冊相應的ModelLoaderFactory
二、圖片的緩存和緩存的時效機制
1.圖片緩存的鍵值
圖片緩存的鍵值主要用於DiskCacheStrategy.RESULT,Glide當中的鍵值主要包含三個部分:
通過DataFetcher.getId()方法返回的String數據作為鍵值。一般的DataFetchers會簡單返回數據模型data model的toString()結果,如果是URL/File會返回相應的路徑
圖片的尺寸,主要是通過override(width,height)或者通過Target's getSize()方法確定的尺寸信息
包含一個可選的簽名所有的這些東西會通過一種散列算法生成一個獨有、安全的文件名,通過此文件名將圖片緩存在disk中
2.緩存失效
因為Glide當中圖片緩存key的生成是通過一個散列算法來實現的,所以很難手動去確定哪些文件可以從緩存當中進行刪除
2.1 當內容(url,file path)改變的時候,改變相應的標識符就可以了,Glide當中也提供了signature()方法,將一個附加的數據加入到緩存key當中
多媒體存儲數據,可用MediaStoreSignature類作為標識符,會將文件的修改時間、mimeType等信息作為cacheKey的一部分
文件,使用StringSignature
Urls ,使用StringSignature
Glide.with(yourFragment) .load(yourFileDataModel) .signature(new StringSignature(yourVersionMetadata)) .into(yourImageView); Glide.with(fragment) .load(mediaStoreUri) .signature(new MediaStoreSignature(mimeType, dateModified, orientation)) .into(view);
自定義標識符:
public class IntegerVersionSignature implements Key { private int currentVersion; public IntegerVersionSignature(int currentVersion) { this.currentVersion = currentVersion; } @Override public boolean equals(Object o) { if (o instanceof IntegerVersionSignature) { IntegerVersionSignature other = (IntegerVersionSignature) o; return currentVersion = other.currentVersion; } return false; } @Override public int hashCode() { return currentVersion; } @Override public void updateDiskCacheKey(MessageDigest md) { messageDigest.update(ByteBuffer.allocate(Integer.SIZE) .putInt(signature).array()); } }
2.2、不緩存可以通過diskCacheStrategy(DiskCacheStrategy.NONE.)實現
三、配置GlideModules
可以通過GlideModule接口來配置Glide的配置文件,並且像ModelLoaders一樣注冊相關組件。
包含一個GlideMode :
第一步、To use and register a GlideModule, first implement the interface with your configuration and components:
public class MyGlideModule implements GlideModule { @Override public void applyOptions(Context context, GlideBuilder builder) { // Apply options to the builder here. } @Override public void registerComponents(Context context, Glide glide) { // register ModelLoaders here. } }
第二步、然後將上面的實現了加入到proguard.cfg當中:
-keepnames class * com.mypackage.MyGlideModule
第三步、在AndroidManifest.xml文件中添加meta-data,以便Glide能夠找到你的Module
<meta-data android:name="com.bumptech.glide.samples.flickr.FlickrGlideModule" android:value="GlideModule" />
四、Library項目
一個Library項目可能會定義一個或者多個GlideModules,如果一個Library項目添加一個Module到Library項目的manifest當中,依賴於此Library的應用就會自動加載依賴庫(Library項目)當中的Module。
當然,如果manifest的合並不正確,那麼Library裡面Module就必須手動地在應用當中添加進去。
五、GlideModules沖突
雖然Glide允許一個應用當中存在多個GlideModules,Glide並不會按照一個特殊的順序去調用已注冊的GlideModules,如果一個應用的多個依賴工程當中有多個相同的Modules,就有可能會產生沖突。
如果一個沖突是不可避免的,應用應該默認去定義一個自己的Module,用來手動地處理這個沖突,在進行Manifest合並的時候,可以用下面的標簽排除沖突的module。
<meta-data android:name=”com.mypackage.MyGlideModule” tools:node=”remove”/>
六、通過GlideBuilder配置全局配置文件
Glide允許開發者配置自定義的全局操作應用於所有的請求,這個部分可以通過GlideModule接口中的applyOptions方法的GlideBuilder參數實現 :
1.DiskCache
1.1、硬盤緩存是在一個後台線程當中,通過一個DiskCache.Factory接口進行緩存的。
開發者能夠通過GlideBuilder的setDiskCache(DiskCache.Factory df)方法設置存儲的位置和大小
通過傳入DiskCacheAdapter來完全禁用緩存
自定義一個DiskCache來完全禁用緩存,
Glide默認是用InternalCacheDiskCacheFactory類來創建硬盤緩存的,這個類會在應用的內部緩存目錄下面創建一個最大容量250MB的緩存文件夾,使用這個緩存目錄而不用sd卡,意味著除了本應用之外,其他應用是不能訪問緩存的圖片文件的。
1.2.設置disk緩存的大小 : InternalCacheDiskCacheFactory
builder.setDiskCache(new InternalCacheDiskCacheFactory(context, yourSizeInBytes));
1.3.設置緩存的路徑
可以通過實現DiskCache.Factory,然後使用DiskLruCacheWrapper創建一個新的緩存目錄,比如,可以通過如下方式在外存當中創建緩存目錄:
builder .setDiskCache(new DiskCache.Factory() { @Override public DiskCache build() { // Careful: the external cache directory doesn't enforce permissions File cacheLocation = new File(context.getExternalCacheDir(), "cache_dir_name"); cacheLocation.mkdirs(); return DiskLruCacheWrapper.get(cacheLocation, yourSizeInBytes); } });
2.內存當中的緩存和POOLS
GlideBuilder當中,允許開發者去設置內存當中圖片緩存區的大小,主要涉及到的類包括MemoryCache和BitmapPool
2.1 大小的設置
默認內存緩存的大小是用過MemorySizeCalculator來實現的,這個類會根據設備屏幕的大小,計算出一個合適的size,開發者可以獲取到相關的默認設置信息:
MemorySizeCalculator calculator = new MemorySizeCalculator(context); int defaultMemoryCacheSize = calculator.getMemoryCacheSize(); int defaultBitmapPoolSize = calculator.getBitmapPoolSize();
如果在應用當中想要調整內存緩存的大小,開發者可以通過如下方式:
Glide.get(context).setMemoryCategory(MemoryCategory.HIGH);
2.2 Memory Cache
Glide內存緩存的目的是減少I/O,提高效率
可以通過GlideBuidler的setMemoryCache(MemoryCache memoryCache)去設置緩存的大小,開發者可以通過LruResourceCache類去設置緩存區的大小
builder.setMemoryCache(new LruResourceCache(yourSizeInBytes));
2.3 Bitmap Pool
可以通過GlideBuilder的setBitmapPool()方法設置池子的大小,LruBitmapPool是Glide的默認實現,使用如下:
builder.setBitmapPool(new LruBitmapPool(sizeInBytes));
.圖片格式
GlideBuilder允許開發者設置一個全局的默認圖片格式,
在默認情況下,Glide使用RGB_565格式加載圖片,如果想要使用高質量的圖片,可以通過如下方式設置系統的圖片格式:
builder.setDecodeFormat(DecodeFormat.ALWAYS_ARGB_8888);
七、自定義顯示控件
除了可以將圖片、視頻快照和GIFS顯示在View上面之外,開發者也可以在自定義的Target上面顯示這些媒體文件
1.SimpleTarget
重點內容
如果你想簡單地加載一個Bitmap,你可以通過以下簡單的方式而不是直接地顯示給用戶,可能是顯示一個notification,或者上傳一個頭像,Glide都能很好地實現
SimpleTarget提供了對Target的簡單實現,並且讓你專注於對加載結果的處理
為了使用SimpleTarget,開發者需要提供一個寬和高的像素值,用來加載你的資源文件,並且你需要去實現
onResourceReady(R resource,GlideAnimation<? super R> glideAnimation) int myWidth = 512; int myHeight = 384; Glide.with(yourApplicationContext)) .load(youUrl) .asBitmap() .into(new SimpleTarget<Bitmap>(myWidth, myHeight) { @Override public void onResourceReady(Bitmap bitmap, GlideAnimation anim) { // Do something with bitmap here. } };
說明:
通常你去加載資源的時候,是將他們加載到一個view當中,當fragment或者activity失去焦點或者distroyed的時候,Glide會自動停止加載相關資源,確保資源不會被浪費
在大多數SimpleTarget的實現當中,如果需要資源的加載不受組件生命周期的影響,Glide.width(context)當中的context是application context而不是fragment或者activity
另外,由於一些long running operations可能會導致內存洩露,如果你打算使用一個這樣的操作,可以考慮使用一個靜態的內部類而不是一個動態的內部類。
2.ViewTarget
如果你想加載一張圖片到一個view當中,但是又想改變或者監聽Glide默認的部分設置,就可以通過重寫ViewTarget或者他的子類來實現
如果你想Gidle加載圖片的時候可以自定義圖片的大小,或者想要設置一個自定義的顯示動畫,就可以通過ViewTarget來實現,可以通過一個靜態的ViewTarget或者動態的內部類來實現相關的功能
Glide.with(yourFragment) .load(yourUrl) .into(new ViewTarget<YourViewClass, GlideDrawable>(yourViewObject) { @Override public void onResourceReady(GlideDrawable resource, GlideAnimation anim) { YourViewClass myView = this.view; // Set your resource on myView and/or start your animation here. } });
說明:
加載一張靜態的圖片或者一張GIF動態圖,可以在load後面加上asBitmap()/asGif()
.Load(url)會通過asXXX()替換ViewTarget當中的GlideDrawable參數,也可以通過實現LifecycleLisener,給target設置一個回調。
3.覆蓋默認的相關設置
如果只是想使用Glide的默認配置,可以使用Glide當中ImageViewTargets的兩個子類:
GlideDrawableImageViewTarget 默認的實現,可以通過asGif()加載動態圖片
BitmapImageViewTarget 可以通過asBitmap()加載靜態圖片
如果想要使用Glide默認實現,可以在他們的子類方法當中使用super.xx()即可,例如:
Glide.with(yourFragment) .load(yourUrl) .asBitmap() .into(new BitmapImageViewTarget(yourImageView)) { @Override public void onResourceReady(Bitmap bitmap, GlideAnimation anim) { super.onResourceReady(bitmap, anim); Palette.generateAsync(bitmap, new Palette.PaletteAsyncListener() { @Override public void onGenerated(Palette palette) { // Here's your generated palette } }); } });
八、使用Glide下載自定義尺寸的圖片
Glide的ModelLoader接口為開發者提供了裝載圖片的view的尺寸,並且允許開發者使用這些尺寸信息去選擇合適的URL去下載圖片。選用適當的尺寸可以節省寬帶和設備的空間開銷,提高app的性能
2014年googleI/o大會發表了一篇文章,闡述了他們如何使用ModelLoader接口去適配圖片的尺寸,見下面的連接:https://github.com/google/iosched/blob/master/doc/IMAGES.md
1、通過http/https下載圖片,可以通過繼承BaseGlideUtlLoader來實現:
public interface MyDataModel { public String buildUrl(int width, int height); } public class MyUrlLoader extends BaseGlideUrlLoader<MyDataModel> { @Override protected String getUrl(MyDataModel model, int width, int height) { // Construct the url for the correct size here. return model.buildUrl(width, height); } }
2、可以使用你自定義的ModelLoader去加載圖片了
Glide.with(yourFragment) .using(new MyUrlLoader()) .load(yourModel) .into(yourView);
如果你想避免每次加載圖片都要使用.using(new MyUrlLoader()) ,可以實現是一個
ModelLoaderFactory然後使用Glide將它注冊到GlideModule當中 public class MyGlideModule implements GlideModule { ... @Override public void registerComponents(Context context, Glide glide) { glide.register(MyDataModel.class, InputStream.class, new MyUrlLoader.Factory()); } }
這樣你就可以跳過.using()了
Glide.with(yourFragment) .load(yourModel) .into(yourView);
九、集成庫
1.什麼是集成庫
Glide包含一些小的、可選的集成庫,目前Glide集成庫當中包含了訪問網絡操作的Volley和OkHttp
2.為什麼要包含集成庫
這些集成庫,和Glide的ModelLoader系統允許開發者使用一致地框架去進行網絡相關的操作
3.如何將一個庫集成到Glide當中,
將一個庫集成到Glide當中需要兩步操作,
包含正確的dependency,
確保創建了該集成庫的GlideModule,比如,
將Volley集成到Glide當中
第一步、添加依賴
dependencies { compile 'com.github.bumptech.glide:volley-integration:1.2.2' compile 'com.mcxiaoke.volley:library:1.0.5' }
第二步、創建Volley集成庫的GlideModule
<meta-data android:name="com.bumptech.glide.integration.volley.VolleyGlideModule" android:value="GlideModule" />
然後改變混淆文件:
-keep class com.bumptech.glide.integration.volley.VolleyGlideModule #or -keep public class * implements com.bumptech.glide.module.GlideModule
將OkHttp集成到Glide當中:
第一步、添加依賴
dependencies { compile 'com.github.bumptech.glide:okhttp-integration:1.2.2' compile 'com.squareup.okhttp:okhttp:2.0.0' }
第二步、創建OkHttp集成庫的GlideModule
<meta-data android:name="com.bumptech.glide.integration.okhttp.OkHttpGlideModule" android:value="GlideModule" />
-keep class com.bumptech.glide.integration.okhttp.OkHttpGlideModule #or -keep public class * implements com.bumptech.glide.module.GlideModule
十、在後台線程當中進行加載和緩存
為了保證Glide在後台線程當中加載資源文件更加容易,Glide除了Glide.with(fragment).load(url).into(view)之外還提供了
downloadOnly(int width, int height) downloadOnly(Y target)// Y extends Target<File> into(int width, int height)
1.downloadOnly
Glide的downloadOnly()允許開發者將Image的二進制文件下載到硬盤緩存當中,以便在後續使用,
在UI線程當中異步下載,在異步線程當中則是使用width和height
在異步線程當中同步調用下載,在同步線程當中,
downloadOnly使用一個target作為參數
(1)在後台線程當中下載圖片,可以通過如下的方式:
FutureTarget<File> future = Glide.with(applicationContext) .load(yourUrl) .downloadOnly(500, 500); File cacheFile = future.get();
當future返回的時候,image的二進制文件信息就存入了disk緩存了,值得注意的是downloadOnly API只是保證圖片個bytes數據在disk當中是有效的。
(2)下載完畢之後如果想要進行顯示,可以通過如下方式進行調用:
Glide.with(yourFragment) .load(yourUrl) .diskCacheStrategy(DiskCacheStrategy.ALL) .into(yourView);
通過DiskCacheStrategy.ALL或者DiskCacheStrategy.SOURCE,可以保證程序會去讀取緩存文件
2. 如果想要在後台線程當中獲取某個URL對應的Bitmap
不通過downloadOnly,可以使用into(),會返回一個FutureTarget對象,比如,想要得到一個URL對應的500*500的centerCrop裁剪圖片,可以通過如下方式實現:
Bitmap myBitmap = Glide.with(applicationContext) .load(yourUrl) .asBitmap() .centerCrop() .into(500, 500) .get()
注意:上面的調用只能在異步線程當中,如果在main Thread當中調用.get(),會阻塞主線
十一、轉換器
1.默認的轉換器
Glide兩個默認的轉換器,fitCenter和CenterCrop,其他的轉換器詳見https://github.com/wasabeef/glide-transformations,可以將圖片轉為各種形狀,例如圓形,圓角型等等
用法:
Glide.with(yourFragment) .load(yourUrl) .fitCenter() .into(yourView); Glide.with(yourFragment) .load(yourUrl) .centerCrop() .into(yourView); // For Bitmaps: Glide.with(yourFragment) .load(yourUrl) .asBitmap() .centerCrop() .into(yourView); // For gifs: Glide.with(yourFragment) .load(yourUrl) .asGif() .fitCenter() .into(yourView);
甚至可以在兩幅圖片進行類型轉換的時候進行transformed
Glide.with(yourFragment) .load(yourUrl) .asBitmap() .toBytes() .centerCrop() .into(new SimpleTarget<byte[]>(...) { ... });
2.自定義轉換器
方法:
第一步、編寫轉換器類 ,繼承BitmapTransformation:
private static class MyTransformation extends BitmapTransformation { public MyTransformation(Context context) { super(context); } @Override protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) { Bitmap myTransformedBitmap = ... // apply some transformation here. return myTransformedBitmap; } @Override public String getId() { // Return some id that uniquely identifies your transformation. return "com.example.myapp.MyTransformation"; } }
第二步、在Glide方法鏈當中用.transform(…)替換fitCenter()/centerCrop()
Glide.with(yourFragment) .load(yourUrl) .transform(new MyTransformation(context)) .into(yourView); // For Bitmaps: Glide.with(yourFragment) .load(yourUrl) .asBitmap() .transform(new MyTransformation(context)) .into(yourView); // For Gifs: Glide.with(yourFragment) .load(yourUrl) .asGif() .transform(new MyTransformation(context)) .into(yourView);
3.自定義轉換器的尺寸
在上面使用過程當中沒有設置尺寸值,那麼轉換器轉換的圖片尺寸怎麼確定呢,
Glide實際上已經足夠智能根據view的尺寸來確定轉換圖片的尺寸了
如果需要自定義尺寸,而不是用view和target當中的尺寸,那麼可以使用override(int,int)設置相關的寬和高
4. Bitmap 再利用
為了減少垃圾收集,可以通過BitmapPool接口去釋放不需要的Bitmaps,當然也可以對裡面的bitmap進行再利用。
例如在一次轉換中,
從pool當中得到一個bitmap
將Bitmap回設給Canvas
使用Matrix、Paint在Canvas上面繪制原始的Bitmap,或者通過一個Shader來轉換一個image
4.1 不要手動地去釋放一個轉換的bitmap資源,也不要將transform()之後的Bitmap重新放置到BitmapPool當中去
protected Bitmap transform(BitmapPool bitmapPool, Bitmap original, int width, int height) { Bitmap result = bitmapPool.get(width, height, Bitmap.Config.ARGB_8888); // If no matching Bitmap is in the pool, get will return null, so we should //allocate. if (result == null) { // Use ARGB_8888 since we're going to add alpha to the image. result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); } // Create a Canvas backed by the result Bitmap. Canvas canvas = new Canvas(result); Paint paint = new Paint(); paint.setAlpha(128); // Draw the original Bitmap onto the result Bitmap with a transformation. canvas.drawBitmap(original, 0, 0, paint); // Since we've replaced our original Bitmap, we return our new Bitmap here. Glide will // will take care of returning our original Bitmap to the BitmapPool for us. return result; }
大家都玩微信,避免不了都多多少少會有幾個微信群,尤其是人數較多的群如果只看頭像肯定分不清楚誰是誰,只要我們在微信裡設置一下就可以將群裡面的好友名字顯示出來,
在同組項目進行共享時,容易把本地的配置文件比如*.iml等文件上傳至共享服務器,這樣會對隊友造成巨大的麻煩,為了解決這個問題,可以使用下面方法解決,下面以上傳到服務器的a
本文我們將介紹一些關於Android矢量圖的相關知識點。最新的項目中要求以矢量圖替代傳統的.png資源文件,所以特意學習了一下Android中的矢量圖相關概念,不得不說矢
Android 日期和時間的使用日期和時間的使用;1:彈出框TimePickerDialog,DatePickerDialog2:組件TimePicker,DatePic