android學習之路(七)---- 用Fan-Image-Loader實現一個炫酷的相簿功能
FanGallery
一、簡介
在上一篇博文當中,我們提出了universal-image-loader的缺點,並進行了豐富和改寫,那麼,這期我們就以上篇博文封裝的Fan-Image-Loader為基礎,實現一個相簿, 一般來說,像這種相簿功能,都有openGL來實現載入過程,以達到快速渲染的目的,但是openGL有很大的學習成本,而且擴充套件性不高,這裡我們使用Fan-Image-Loader同樣可以達到這樣的目的
二、記憶體問題的解決
對於android應用來講,記憶體始終是一個永恆的話題,但對於一個相簿app而言,圖片的載入速度和記憶體都是讓人頭疼的問題,一般的解決辦法,就是用openGL提高渲染速度,記憶體上面尚沒有更好的解決辦法,那麼在本篇例子當中,我們使用Fan-Image-Loader載入圖片,同樣可以達到openGL的渲染速度。接下來就是記憶體!記憶體!記憶體!首先我們應該明白,在android生態圈,系統對應用執行時的堆記憶體分配沒有嚴格標準,從60M~140M都有,如果app本身就消耗記憶體,那麼留給相簿和影象處理的記憶體就很少了,這時候就很容易OOM,但是另一點,android系統對應用的記憶體分配是以程式為單位的,這就給我們提供了迂迴的解決辦法:
1.將工程分為兩個程式:app和pic,app程式就是系統預設程式,啟動時就會分配記憶體空間,pic程式的啟動由一個service啟動,相關的初始化放在service的onCreate方法當中。
2.程式之間的通訊有兩種方式:aidl和broadcastReceiver,但是對於我們這樣一個輕量的app而言,broadcastReceiver顯然太重了,所以用aidl通訊,當我們在app程式點選某個按鈕,準備進入相簿的時候,會通過aidl提供的方法判斷相簿當中是否有圖片,如果沒有圖片,那麼就應該啟動拍照介面而不是相簿介面。
3.由於是兩個程式,他們之間不會共用任何變數和資源,這就需要在各自的初始化入口處進行各自的初始化操作。
三、初始化
從上面的解析當中,我們知道了用雙程式來解決記憶體問題,如何啟動一個雙程式呢?
1.自定義一個service—–FanService.java
/**
* time: 15/12/6
* description:pic程式的啟動入口
*
* @author fandong
*/
public class FanService extends Service {
private class MixBinder extends IFanService.Stub {
@Override
public boolean isPhotoValidate() throws RemoteException {
return LocalPhotoManager.getInstance().isPhotoValidate();
}
@Override
public void putLocalPhoto(int code, String path) throws RemoteException {
LocalPhotoManager.getInstance().put2Gallery(code, path);
LocalPhotoManager.getInstance().put2Map(code, path);
}
}
/*退出的時候,幹掉mix程式*/
public static void stopFanService(Context context) {
//1.停掉LocalPhotoManager
LocalPhotoManager.destroy();
//2.停掉mix程式
Intent intent = new Intent(context, FanService.class);
context.stopService(intent);
}
/*啟動mix程式*/
public static void startFanService(Context context) {
Intent intent = new Intent(context, FanService.class);
context.startService(intent);
}
/*繫結mix程式*/
public static void bindFanService(Context context, ServiceConnection connection) {
Intent intent = new Intent(context, FanService.class);
context.bindService(intent, connection, Context.BIND_AUTO_CREATE);
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return new MixBinder();
}
@Override
public void onCreate() {
super.onCreate();
//1.掃描本地圖片到記憶體當中
LocalPhotoManager.getInstance().initialize();
//2.初始化ImageLoader
FanImageLoader.init(getApplicationContext(), FileUtil.getPathByType(FileUtil.DIR_TYPE_CACHE));
//3.初始化Pinguo-image-loader當中的日誌系統
L.writeDebugLogs(BuildConfig.DEBUG);
}
@Override
public boolean onUnbind(Intent intent) {
stopFanService(this);
return super.onUnbind(intent);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
}
2.在清單檔案當中註冊,並宣告程式,核心就是android:proccess這個屬性了:
<activity
android:name=".GalleryActivity"
android:process=":pic"
android:theme="@style/AppTheme.NoActionBar" />
<service
android:name=".FanService"
android:process=":pic" />
可以看到,這裡我們把FanService和GalleryActivity放到了同一個程式:pic當中了,接下來就啟動:pic程式了
3.啟動pic程式,在GalleryApplication.java當中:
@Override
public void onCreate() {
super.onCreate();
gContext = this;
//1.啟動pic程式
FanService.startFanService(this);
}
從上面的FanService.java的onCreate方法當中可以看出來,我們在此方法當中進行了本地圖片資源的初始化。
4.程式寫到這一步,完成了同一應用的雙程式執行,相當於整個app增加了一倍的堆記憶體,雖然不能根除OOM,但是也大幅降低了OOM的風險
四、實現酷炫的照片選擇效果
1.從上面的動畫效果可以看出,當手指觸碰到裁剪區域的時候,裁剪區域會隨著相簿整體向上滑動,整個互動流程如下圖所示:
2.上面的流程當中涉及到的關鍵技術如下所示:
2.1 裁剪介面覆蓋在RecyclerView
上面,所有RecyclerView
應該有一個headerView
,此headerView
的高度應該和裁剪區域一樣,這樣才能達到覆蓋的效果,但是recyclerView
並沒有想ListView
一樣新增headerView
的功能,所以只能通過如下兩步操作,達到這樣的效果:
第一步、給LayoutManager設定一行的item寬度:
mLayoutManager = new GridLayoutManager(this, 4, GridLayoutManager.VERTICAL, false);
mLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(int position) {
if (position == 0) {
return 4;//第一行寬度佔四列
}
return 1;
}
});
mRecyclerView.setLayoutManager(mLayoutManager);
第二步、在adapter當中進行判斷
@Override
public GalleryViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view;
if (0 == viewType) {
view = mInflater.inflate(R.layout.vw_gallery_header, parent, false);
} else if (1 == viewType) {
view = mInflater.inflate(R.layout.vw_gallery_camera_item, parent, false);
} else {
view = mInflater.inflate(R.layout.vw_gallery_item, parent, false);
}
view.setTag(viewType);
return new GalleryViewHolder(view, viewType);
}
@Override
public int getItemViewType(int position) {
return position;
}
2.2 好了,經過上面兩步,我們知道了裁剪區域是怎樣覆蓋在recyclerView
上面的,那麼這一步就是體現我們滑動recyclerView
時候ScrollLinearLayout
如何跟隨RecyclerView
進行滑動的:關鍵方法是給recyclerView
設定OnScrollListener
,讓我們來看看這個滑動監聽:
//2.初始化mRecyclerOnScrollListener
mRecyclerOnScrollListener = new RecyclerView.OnScrollListener() {
float limitY = ResHelper.getDimen(R.dimen.crop_image_operation_height);
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
if (RecyclerView.SCROLL_STATE_IDLE == newState) {
/*當recyclerview的第一個空白檢視的bottom大於 標題的bottom,那麼scrollLinearlayout應該滑動到下面*/
boolean scrollToBottom = false;
View targetView = mLayoutManager.getChildAt(0);
if (0 == (int) targetView.getTag()) {
if (limitY < targetView.getBottom()) {
scrollToBottom = true;
}
}
mScrollLinearLayout.clipToBound(mRecyclerView::smoothScrollBy, scrollToBottom);
System.gc();
} else {
if (mPopupWindow != null && mPopupWindow.isShowing()) {
mPopupWindow.dismiss();
mPopupWindow = null;
}
}
}
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
View view = mLayoutManager.getChildAt(0);
if (dy > 0) {
mScrollLinearLayout.scrollBy((float) mRecyclerView.getLastTouchY(), dx, dy);
} else {
if ((Integer) view.getTag() == 1) {
if (view.getTop() >= 0) {
mScrollLinearLayout.scrollBy(0.f, dx, dy);
}
}
if ((Integer) view.getTag() == 0) {
if (view.getBottom() > limitY) {
mScrollLinearLayout.scrollBy(0.f, dx, dy);
}
}
}
}
};
根據上面畫的流程圖再來看現在的這個滑動過程,應該不難理解,這裡就不做過多講解了
2.3 從效果圖的動態圖,我們可以看到,當選中某個圖片的時候,如果ScrollLinearLayout
處於頂部懸浮狀態的時候此時ScrollLinearLayout
會向上滑動,處於初始位置,而recyclerView
選中的item則會滑動到ScrollLinearLayout
的下面,這個功能是如何實現的呢?recyclerView
的item點選事件
//3.初始化recyclerView的點選事件
mOnRecyclerItemClickListener = (position, url, clickView) -> {
if (1 != position && position == mLastClickPosition) {
return;
}
mLastClickPosition = position;
//3.0 如果是第一個方框,則需要啟動拍照的介面
if (1 == position && System.currentTimeMillis() - mLastClickTime > 3000) {
mLastClickTime = System.currentTimeMillis();
Toast.makeText(GalleryActivity.this, "點選拍照按鈕", Toast.LENGTH_SHORT).show();
return;
}
int old = mGalleryAdapter.getSelectedPosition();
mGalleryAdapter.setSelectedPosition(position);
mGalleryAdapter.notifyItemChanged(old);
mGalleryAdapter.notifyItemChanged(position);
mImageCropView.setImageURI(url);
//3.1.如果linearLayout是懸浮在上面的,就下滑至原來位置
mScrollLinearLayout.scrollToBottom();
//3.2.滑動item到指定位置
mRecyclerView.smoothScrollBy(0, (int) (clickView.getTop() - mScrollLinearLayout.getFullViewHeight() + 0.5f));
};
以上就是做到此效果的關鍵方法,詳細可以參考原始碼功能,這裡不做過多介紹
2.4 當ScrollLinearLayout處於頂部懸浮狀態的時候,此時,我想ScrollLinearLayout
滑下來,只需要點選ScrollLinearLayout
露出來的部分就可以了,這是如何做到的呢?就是給三個編輯按鈕新增了攔截點選事件.
第一步、GalleryActivity.java
//1.初始化mOnClickInterceptListener
mOnClickInterceptListener = () -> {
if (mScrollLinearLayout.isTopState()) {
mScrollLinearLayout.scrollToBottom();
return true;
}
return false;
};
第二步、ImageCropView.java
@OnClick({R.id.image_crop_attach, R.id.image_crop_ratio,
R.id.image_crop_rotate})
public void onClick(View view) {
//1.確定是否攔截點選事件
if (mOnClickInterceptListener != null) {
if (mOnClickInterceptListener.clickIntercept()) {
return;
}
}
……
}
相關文章
- 10個非常炫酷的jQuery相簿動畫賞析jQuery動畫
- 用 Vue 做一個酷炫的 menuVue
- Android——Activity切換炫酷動畫實現Android動畫
- 七款酷炫的 Mac 屏保Mac
- HTML5+CSS3D酷炫相簿HTMLCSSS33D
- android炫酷的textviewAndroidTextView
- 一個iOS開發者學習Android之路iOSAndroid
- PHP建立一個炫酷的圖表PHP
- 7款純CSS3實現的炫酷動畫應用CSSS3動畫
- Flutter 實現酷炫的3D效果Flutter3D
- 如何實現炫酷的數字大屏
- vue寫一個炫酷的日曆元件Vue元件
- c++實現彩色炫酷(?)畫面C++
- CoordinatorLayout實現酷炫摺疊效果
- C# Winform實現炫酷的透明動畫介面C#ORM動畫
- 【Android珍藏】推薦10個炫酷的開源庫Android
- 寫一個酷炫的iOS進度條動畫iOS動畫
- 一些實用的開源炫酷框架—值得你去了解框架
- 8個最新炫酷的HTML5動畫應用HTML動畫
- 該為你的Android Studio 打造一個炫酷的個性化主題了Android
- 使用CSS background實現炫酷懸停效果CSS
- React 實現炫酷的可拖拽網格佈局React
- 利用CSS變數實現炫酷的懸浮效果CSS變數
- jquery實現在滑鼠點選處的炫酷效果jQuery
- three.js實現炫酷的3d影院JS3D
- Android自定義View之實現簡單炫酷的球體進度球AndroidView
- Android超炫酷煙花程式Android
- 自定義一個酷炫的提交完成按鈕
- 炫酷實用的HTML5應用和jQuery外掛HTMLjQuery
- Android 學習深入之路(應用層)Android
- android實現拍照、相簿選圖、裁剪功能,相容7.0以及小米Android
- 8個炫酷的HTML5動畫、應用和遊戲HTML動畫遊戲
- InteractiveGraph 實現酷炫關係圖譜之前瞻
- 動手做一個酷炫(並不)的計算器(一)
- 7款炫酷實用的jQuery/HTML5選單jQueryHTML
- canvas實現炫酷的黑客帝國數字雨特效Canvas黑客特效
- 【原始碼分析】Lottie 實現炫酷動畫背後的原理原始碼動畫
- 自定義View:實現炫酷的點贊特效(仿即刻)View特效