編輯:關於Android編程
目前市場上的應用在啟動時基本上都會先啟動一個SplashActivity,作為一個歡迎界面,為什麼這樣設計呢?
個人總結有三個優點:
比如:可以由後台動態的改變歡迎的圖片,或者顯歡迎xxx回來,新浪微博的就是這種交互。
由上一篇博文中知道app啟動的耗時主要是在Application初始化中和MainActivity的界面繪制前,由於MainActivity的業務和布局復雜度肯定比只顯示一張圖片的界面高,所以,加入一個顯示一張圖片的Splash頁可以優化應用的啟動。
一般來說SplashActivity一般會設計成停留2到4s不等,或者根據數據的加載程度來動態的設置Splash界面的停留時間,既然停留那麼久,那麼當然可以在這個界面背後做一些事以備MainActivity的快速顯示,比如:數據的預加載、sp的初始化、網絡請求等。
當然你可能有些疑問,那這樣初始化放在Application中也可以啊?也用異步操作數據也是一樣啊?
答案是不一樣!正如上篇所說的,Application初始化時並不會加載界面,而是在它創建完和初始化完成後,開始創建Activity時才開始繪制Theme中的background和繪制布局,所以用一個輕量的Splash頁給它設置一張背景歡迎圖,這樣就立馬能顯示界面了,而在這個界面中還可以做其它的初始化操作,這樣在視覺上即達到了app的快速啟動,又添加了體驗和做數據的初始化。
相反如果過多的放在Application中,則在點擊app圖標啟動時會感覺延遲,必須要把Application中的東西都做完才進入Activity的配置和繪制中。
目前大多應用的Splash頁設計都是利用一個Activity,取名叫SplashActivity,然後在這個SplashActivity中加入一個背景圖,然後再new Handler().postDelayed()幾秒中,再startActivity跳入主界面,這樣設計看起來非常不錯,既可以在SplashActivity初始化、預加載數據,還可以提高應用的啟動速度。
不過這確實提高了應用的啟動速度,畢竟我們比較快的看到了第一幀——SplashActivity,不過在SplashActivity之後,還需要調到MainActivity啊,雖然MainActivity中的一些數據可以在SplashActivity做預取,不過這中間需要有Intent的傳遞過程,而且MainActivity中布局還沒加載進來,所以還是需要再加載和繪制布局界面,然後才能填入數據,所以這樣看來,在跳轉到MainActivity中,還是需要做界面的繪制和數據的加載(包括Intent的數據傳遞)。
這樣看來上面這個設計流程可以這樣表示:
從上面這個設計圖來看,其中有些操作能不能去除呢?既能達到app啟動速度的提高,也能對數據的預加載還能減去Splash和MainActivity之間不必要的數據傳遞和View的分開繪制。
答案是能的,既然SplashActivity和MainActivity分開進行操作還是不完美,那麼可以考慮把它們合為一起,即:一開始還是顯示MainActivity,SplashActivity變為SplashFragment,然後放一個FrameLayout作為根布局去顯示SplashFragment界面,這樣在SplashFragment顯示時候利用顯示的2~4s間的空隙時間做網絡請求去加載數據,這樣待SplashFragment顯示完後再remove,這樣將看到的是有內容的MainActivity,就不必再去等待網絡請求去返回數據了。
當然,這種方式是把load Splash View和ContentView合二為一了一起加載,這可能會影響應用的啟動時間,這時我們可以用ViewStub延遲加載MainActivity中某些View從而減去這個影響。
如下設計:
這裡為了測試,我把Splash頁的delay時間都設為2.5s。
優化前:
優化後:
優化後其實是把SplashActivity用Fragment顯示,顯示完後再remove,這樣在顯示的時候,MainActivity中還可以直接加載網絡數據,這樣在顯示完SplashFragment後則直接顯示主頁了,而省去了ProgressBar進度條的網絡加載過程。
代碼:<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwcmUgY2xhc3M9"brush:java;">
private Handler mHandler = new Handler();
//...
final SplashFragment splashFragment = new SplashFragment();
final FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.replace(R.id.frame, splashFragment);
transaction.commit();
//...
mHandler.postDelayed(new DelayRunnable(this, splashFragment, mProgressBar), 2500);
//...
static class DelayRunnable implements Runnable {
private WeakReference
其中FrameLayout作為MainActivity的根布局用作SplashFragment的全屏顯示。
為了更優則可以考慮ViewStub,在SplashFragment顯示時再進行加載額外的View。
關於耦合性,其實很低,Splash頁面有專門一個SplashFragment去配置,而MainActivity只是控制它的加載與remove。
1、View.postDelayed();
2、Handler.postDelayed();
3、getWindow().getDecorView().post(){Handler.postDelay()}
而前兩種方式雖然都有Delay效果,但並不是真正Delay了我們設置的時間,而第三種方式才是正確的Delay了我們想要的時間,原因如下:
關於Activity界面的啟動,首先是在onCreate()方法中會對contentView、DecorView和ActionBar等進行初始化,比如:contentView進行inflate,我們便可以在這個方法中通過findViewById來找到對應的控件了,但是此時我們只是把那些對應的控件創建了一個對象而已,它們並沒有add在界面上。而真正把contentView 添加到界面上的操作是在OnResume()方法執行時候,包括ViewRootImpl的初始化。而contentView的繪制會在onResume()方法執行完後的二次performTraversals()方法進行繪制。
所以要想達到DelayLoad懶加載效果,是在所有的View繪制完成後進行Delay效果,而上面的第一第二種方式都是在第一次performTraversals()後執行,該次只是為第二次調用performTraversals()方法做一些准備工作,這樣就達不到准確的Delay效果了,因為第二次的performTraversals()就是真正的進行View的測量布局和繪制了,這肯定是需要時間的,所以第一和第二種方式Delay的時間是需要再減去這個View繪制的時間的。
如果是想在界面剛繪制完成後做一些事情,或者有些事情必須在UI繪制完成顯示後做的話,那可以通過這個方法:
getWindow().getDecorView().post(new Runnable() {
@Override
public void run() {
mHandler.post(new Runnable() {
@Override
public void run() {
//do something
}
});
}
});
如果想Delay的話,可以這樣:
getWindow().getDecorView().post(new Runnable() {
@Override
public void run() {
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
//do something
}
},3000);
}
});
先上效果圖: 驗證碼采用PHP返回的imageview來實現,xml如下:
Android Studio 大家應該都很熟悉了,但是可能很多人都僅限基本的功能使用,而 Android Studio 非常強大,有很多非常實用卻又鮮為人知的小技巧,熟練
ViewPager引入示例首先讓大家有個全局的認識,直接上個項目,看看僅僅通過這幾行代碼,竟然就能完成如此強悍的功能。效果圖:實現了三個view間的相互滑動。第一個VIE
在上一篇文章中介紹了介紹了觀察者模式的定義和一些基本概念,觀察者模式在 android開發中應用還是非常廣泛的,例如android按鈕事件的監聽、廣播等等,在