私人訂制Android本地圖片選擇器

效果圖

效果圖

需求分析

  • 網(wǎng)格布局顯示本地圖片
  • 支持圖片多選
  • 支持選中的圖片預(yù)覽
  • 未選擇圖片時(shí)不可預(yù)覽
  • 由已選多圖變?yōu)闊o圖時(shí)可退出圖片選擇頁面
  • 圖片已選達(dá)到上限后依然可以跳轉(zhuǎn)圖片選擇頁面

第三方框架使用

史上最強(qiáng)的安卓圖片選擇器——GalleryFinal;
簡(jiǎn)書博客地址——http://m.itdecent.cn/p/48ddd6756b7a

實(shí)現(xiàn)目標(biāo)

應(yīng)用并修改第三方框架GalleryFinal源碼,實(shí)現(xiàn)效果圖的樣式。

具體實(shí)現(xiàn)

  • 導(dǎo)入GalleryFinal源碼
    從GalleryFinal的Github倉庫中拷貝倉庫地址,使用git指令cloneGalleryFinal源代碼到本地:
git clone https://github.com/pengjianbo/GalleryFinal.git

選擇Android Studio菜單欄File->New->import Module...,導(dǎo)入GalleryFinal源代碼到Android Studio:

導(dǎo)入GalleryFinal源碼

詳情參考:
【Mac流派】程序猿修煉之道(6)-技能篇之git指令
Android Studio導(dǎo)入第三方庫的三種方法

  • 寫一個(gè)GridView
    這里聲明一下,選擇圖片的Activity是GalleryFinal自帶的,所以我們這里要寫的GridView是用來顯示選中并返回的圖片,這里的代碼就不放出來了,效果圖如下:

    未選擇時(shí)

    選擇圖片時(shí)

    本人實(shí)現(xiàn)的可支持最大圖片數(shù)量是5,在圖片選滿的時(shí)候依然顯示“+”,用以跳轉(zhuǎn)圖片選擇頁面選擇其他圖片。

  • 初始化GalleryFinal配置

    • 通過代碼設(shè)置圖片選擇器的標(biāo)題欄背景顏色,標(biāo)題文本顏色,浮動(dòng)按鈕顏色;
    • 通過監(jiān)聽事件,達(dá)到滾動(dòng)時(shí)不加載圖片,停下來時(shí)加載圖片,實(shí)現(xiàn)優(yōu)化;
    • 初始化功能配置;

    為防止代碼分開查看導(dǎo)致邏輯的混亂,將上述配置代碼一齊貼上,下面的代碼可放在跳轉(zhuǎn)圖片選擇界面的按鈕點(diǎn)擊事件中。這里強(qiáng)調(diào)一下:mThemeConfig = new ThemeConfig.Builder() .setTitleBarBgColor等方法傳參是整型,但是其傳入的是顏色值而非資源文件的id。
    代碼注釋較詳細(xì),其他不做過多講解:

// ------- 聲明 -------
// 主題配置
private ThemeConfig mThemeConfig;
// 圖片加載器
private cn.finalteam.galleryfinal.ImageLoader mGlidImgLoader;
// 滾動(dòng)監(jiān)聽事件
private PauseOnScrollListener mPauseOnScrollListener;
// 功能配置
private FunctionConfig mFunctionConfig;
// 核心配置
private CoreConfig mCoreConfig;
// ------- 實(shí)現(xiàn) -------
// 獲取標(biāo)題欄背景顏色
int colorTitleBarBg = ContextCompat.getColor(Activity.this, R.color.titleBarBgColor);
// 標(biāo)題欄文字顏色
int colorTitleBarText = ContextCompat.getColor(Activity.this, R.color.titleBarTextColor);
// 浮動(dòng)按鈕常規(guī)顏色
int colorFabNormal = ContextCompat.getColor(Activity.this, R.color.color_ffaa2a);
// 浮動(dòng)按鈕點(diǎn)擊顏色
int colorFabPressed = ContextCompat.getColor(Activity.this, R.color.color_e29428);
// 標(biāo)題欄按鈕顏色
int colorTitleBarIcon = ContextCompat.getColor(MainActivity.this, R.color.colorTitleBarIcon);
// 設(shè)置主題
mThemeConfig = new ThemeConfig.Builder()
        .setTitleBarBgColor(colorTitleBarBg) // 設(shè)置標(biāo)題欄背景顏色
        .setTitleBarTextColor(colorTitleBarText)    // 設(shè)置標(biāo)題欄文字顏色
        .setFabNormalColor(colorFabNormal)  // 設(shè)置浮動(dòng)按鈕常規(guī)顏色
        .setFabPressedColor(colorFabPressed)    // 設(shè)置浮動(dòng)按鈕點(diǎn)擊顏色
        .setCheckSelectedColor(colorFabNormal)  // 設(shè)置選中標(biāo)記(對(duì)勾)的顏色和按鈕的顏色相同
        .setTitleBarIconColor(colorTitleBarIcon) // 設(shè)置標(biāo)題欄按鈕顏色
        .setIconBack(R.drawable.ic_back) // 設(shè)置返回按鈕
        .build();
// 初始化圖片加載器
mGlidImgLoader = new GlideImageLoader();
// 初始化監(jiān)聽事件
mPauseOnScrollListener = new GlidePauseOnScrollListener(false, true);
// 初始化功能配置
FunctionConfig.Builder funConBuilder = new FunctionConfig.Builder();
// 設(shè)置最多可選擇5張照?qǐng)D片
funConBuilder.setMutiSelectMaxSize(5);
// 設(shè)置圖片不可編輯
funConBuilder.setEnableEdit(false);
// 設(shè)置圖片不可旋轉(zhuǎn)
funConBuilder.setEnableRotate(false);
// 設(shè)置圖片不可裁剪
funConBuilder.setEnableCrop(false);
// 設(shè)置不可通過照相選擇照片
funConBuilder.setEnableCamera(false);
// 設(shè)置添加過濾集合,過濾掉之前選中的圖片
//        funConBuilder.setFilter(mPhotoList);
// 不過濾圖片,而是將之前選中的圖片設(shè)置為選中狀態(tài)
funConBuilder.setSelected(mPhotoList);
// 設(shè)置可預(yù)覽
funConBuilder.setEnablePreview(true);
// 功能配置
mFunctionConfig = funConBuilder.build();
// 初始化核心配置
mCoreConfig = new CoreConfig.Builder(ReportActivity.this, mGlidImgLoader, mThemeConfig)
        .setFunctionConfig(mFunctionConfig) // 添加功能配置
        .setPauseOnScrollListener(mPauseOnScrollListener) // 滑動(dòng)停止加載事件
        .setNoAnimcation(true) // 無特效動(dòng)畫
        .build();
// 實(shí)例化
GalleryFinalGalleryFinal.init(mCoreConfig);
// 多圖片選擇打開相冊(cè)
GalleryFinal.openGalleryMuti(Constants.REQUEST_CODE_GALLERY, mFunctionConfig,
        mOnHandlerResultCallback);
// 初始化圖片加載器
initImageLoader(ReportActivity.this);
/**
 * 初始化圖片加載器
 *
 * @param context
 */
private void initImageLoader(Context context) {
    // 圖片加載器配置
    ImageLoaderConfiguration.Builder config = new ImageLoaderConfiguration.Builder(this);
    // 設(shè)置線程優(yōu)先級(jí)
    config.threadPriority(Thread.NORM_PRIORITY - 2);
    // 禁止內(nèi)存緩存
    config.denyCacheImageMultipleSizesInMemory();
    // 設(shè)置磁盤緩存文件名生成器
    config.diskCacheFileNameGenerator(new Md5FileNameGenerator());
    // 設(shè)置磁盤緩存大小
    config.diskCacheSize(20 * 1024 * 1024);
    // 設(shè)置任務(wù)進(jìn)程執(zhí)行順序:先進(jìn)后出
    config.tasksProcessingOrder(QueueProcessingType.LIFO);
    // 調(diào)試使用,若是發(fā)布版,需要移除代碼
    config.writeDebugLogs();
    // 初始化圖片加載器
    ImageLoader.getInstance().init(config.build());
}
  • 選擇圖片返回的回調(diào)實(shí)現(xiàn)
/**
 * 回調(diào)處理
 */
private GalleryFinal.OnHanlderResultCallback mOnHandlerResultCallback = new GalleryFinal.OnHanlderResultCallback() {
    @Override
    public void onHanlderSuccess(int reqeustCode, List<PhotoInfo> resultList) {
        // 清除原來列表中的圖片
        mPhotoList.clear();
        // 返回圖片列表
        mPhotoList.addAll(resultList);
        // 刷新頁面
        mPhotoAdapter.notifyDataSetChanged();
    }
    @Override
    public void onHanlderFailure(int requestCode, String errorMsg) {
        // 錯(cuò)誤提示
        Toast.makeText(Activity.this, errorMsg, Toast.LENGTH_SHORT).show();
    }
};

基于上述代碼,可得到效果圖如下:

初步效果圖

修改源碼

通過運(yùn)行調(diào)試,發(fā)現(xiàn)框架中有些功能與需求不一致,因此我產(chǎn)生了修改源碼的想法,總結(jié)需要更改的原功能點(diǎn)如下:

  • 選擇完達(dá)到上限數(shù)量的圖片后,無法重新回到圖片選擇頁面


    選擇圖片數(shù)量達(dá)到上限時(shí)無法進(jìn)入圖片選擇頁面
  • 無圖片選擇時(shí),無法點(diǎn)擊浮動(dòng)按鈕進(jìn)行返回


    無圖片時(shí)無法返回
  • 無圖片選擇時(shí),預(yù)覽按鈕依然存在


    無圖片選擇時(shí)預(yù)覽按鈕依然存在并可以點(diǎn)擊

針對(duì)以上需要修改的功能,源碼修改如下:

  • 選擇圖片到達(dá)上限依然可以返回圖片選擇頁面
    因圖片選擇頁面的跳轉(zhuǎn)在openGalleryMuti方法里實(shí)現(xiàn):
// 多圖片選擇打開相冊(cè)
GalleryFinal.openGalleryMuti(Constants.REQUEST_CODE_GALLERY, mFunctionConfig, 
       mOnHandlerResultCallback);

所以我們來看看openGalleryMuti方法的源碼:


openGalleryMuti方法源碼

可以發(fā)現(xiàn),源碼中有這么一個(gè)判斷邏輯:

if (config.getSelectedList() != null && config.getSelectedList().size() > config.getMaxSize()) {
    if(callback != null){
        callback.onHanlderFailure(requestCode, mCoreConfig.getContext().getString(R.string.select_max_tips));
    }
    return;
}

其作用是當(dāng)選中的圖片數(shù)量超過最大值時(shí),返回打開本地圖片選擇器失敗的提示信息。之前我們提到,需求中我們實(shí)際多顯示了一張圖片:


“添加”圖片

且在配置的時(shí)候傳入的是添加了一張圖片以后的圖片列表:

// 不過濾圖片,而是將之前選中的圖片設(shè)置為選中狀態(tài)
funConBuilder.setSelected(mPhotoList);

所以會(huì)導(dǎo)致界面無法跳轉(zhuǎn),我們有三個(gè)策略:
1.將多添加的圖片放到adapter里面處理,adapter外部保持選中圖片數(shù)量與選擇頁面?zhèn)魅雸D片的數(shù)量一致
2.setSelected傳入圖片列表之前將mPhotoList移除多出的圖片
3.注釋掉源碼中對(duì)圖片數(shù)量上限的判斷

  • 無圖選擇時(shí),點(diǎn)擊浮動(dòng)按鈕可以返回
    可能有人不解,為何不點(diǎn)擊標(biāo)題欄的返回按鈕返回而要點(diǎn)擊浮動(dòng)按鈕返回?其原因是,若之前我選擇好圖片,但是想想,現(xiàn)在我不想要選擇的圖片了,這時(shí)候我們想把圖片清空掉,這時(shí)候需要點(diǎn)擊浮動(dòng)按鈕,來更新選中圖片的列表。
    想到這是浮動(dòng)按鈕的點(diǎn)擊事件,所以我們到源碼的GallerySelectActivity中浮動(dòng)按鈕的事件回調(diào)方法中:
    浮動(dòng)按鈕點(diǎn)擊事件

    這段代碼僅僅在選中圖片的數(shù)量大于0的時(shí)候才執(zhí)行操作,所以我們添加一個(gè)條件,修改后的代碼如下:
if (mSelectPhotoList.size() > 0) {
    if (!GalleryFinal.getFunctionConfig().isEditPhoto()) {
        resultData(mSelectPhotoList);
    } else {
        toPhotoEdit();
    }
} else {
    // 添加的代碼,使未選中圖片時(shí)也可返回
    resultData(mSelectPhotoList);
}
mSelectPhotoList初始化

考慮到選中圖片的列表在聲明時(shí)已經(jīng)初始化,所以不用擔(dān)心圖片返回的回調(diào)事件傳入空指針對(duì)象。

  • 未選擇圖片時(shí)不顯示預(yù)覽按鈕
    通過布局的id——iv_preview在PhotoSelectActivity中查找,在refreshSelectCount方法里找到了對(duì)預(yù)覽按鈕可見性的設(shè)置:
    refreshSelectCount方法源碼

    從源碼中可以看到,預(yù)覽按鈕的可見性判斷邏輯僅僅與isEnablePreview有關(guān),而沒有和選中的圖片數(shù)量進(jìn)行關(guān)聯(lián),所以我們修改代碼如下:
public void refreshSelectCount() {
    mTvChooseCount.setText(getString(R.string.selected, mSelectPhotoList.size(), GalleryFinal.getFunctionConfig().getMaxSize()));
    if (mSelectPhotoList.size() > 0 && GalleryFinal.getFunctionConfig().isMutiSelect()) {
        mIvClear.setVisibility(View.VISIBLE);
        if (GalleryFinal.getFunctionConfig().isEnablePreview()) {
            mIvPreView.setVisibility(View.VISIBLE);
        }
    } else {
        mIvClear.setVisibility(View.GONE);
        mIvPreView.setVisibility(View.GONE);
    }
}

當(dāng)選中圖片列表大小為0的時(shí)候,隱藏預(yù)覽按鈕;大于0的時(shí)候再根據(jù)isEnablePreview()來判斷是否顯示預(yù)覽按鈕。

修改布局和代碼邏輯

布局和代碼邏輯的修改,其思路與上一節(jié)修改源碼一樣,因需求的效果圖功能與GalleryFinal的功能基本一致,邏輯上并不需要做很多的修改,而布局的修改僅涉及到ImageButton變成Button,ImageView變成TextView以及控件位置的調(diào)整,在關(guān)聯(lián)控件上和點(diǎn)擊事件根據(jù)id來判斷事件處理上做相應(yīng)修改即可,在此不做贅述。

總結(jié)

使用GalleryFinal訂制屬于自己的圖片選擇器并不難,只需要循著需求的功能點(diǎn),按照代碼的邏輯一點(diǎn)點(diǎn)追蹤源碼并進(jìn)行修改訂制即可。誠(chéng)懇地說,GalleryFinal框架的可移植性確實(shí)很強(qiáng),在此推薦大家了解一下!

本篇文章的Demo已上傳Github,歡迎訪問指導(dǎo)!
原創(chuàng)不易,轉(zhuǎn)載請(qǐng)注明鏈接:http://m.itdecent.cn/p/fd5ebfc4725e,謝謝!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容