一起擼個(gè)微信圖片瀏覽的BaseActivity吧(上)——初步思考與基礎(chǔ)結(jié)構(gòu)

本項(xiàng)目git: https://github.com/razerdp/ZoomViewActivity

【下篇】一起擼個(gè)微信圖片瀏覽的BaseActivity吧(下)——過渡動畫的實(shí)現(xiàn)

項(xiàng)目預(yù)覽圖:

preview.gif

距離上次更新博客有兩三個(gè)月了。。。。太懶了orz...

在微信的日常使用中,我們點(diǎn)擊圖片放大的時(shí)候都有一個(gè)動畫效果,這個(gè)動畫效果過渡看起來很自然,在5.0之后,擁有ShareElement之后做到這個(gè)還是比較好做的,然而目前在我們的日常開發(fā)中,大多數(shù)app兼容都是下限為4.0而不是5.0,所以要實(shí)現(xiàn)這個(gè)動畫效果就需要我們花費(fèi)一點(diǎn)心思了。

事實(shí)上,在朋友圈項(xiàng)目中,我們就實(shí)現(xiàn)過這樣的一個(gè)圖片瀏覽動畫,詳情點(diǎn)我→《一起擼個(gè)朋友圈吧 - 圖片瀏覽(中)【圖片瀏覽器】》

然而在這里的實(shí)現(xiàn)按照我目前的看法,是不太完美的,原因有二:

  • 圖片瀏覽視圖跟時(shí)間線(timeline)處于同一Activity,即便我將它移到一個(gè)代理類里面,但還是顯得依賴性很大。
  • 基于第一點(diǎn),不便于其他Activity使用

總的來說,就是一個(gè)定制性的類,不太符合我們的“通用性”思想。

于是,再稍微整理和封裝之后,我們就有了今天的這個(gè)項(xiàng)目。

在說明之前,先聲明一下目前仍然有的不足:

  • 對于圖片的scaleType支持不好
  • 暫時(shí)沒有針對多圖瀏覽(ViewPager)做優(yōu)化

暫且算是幾個(gè)issue吧,有空再處理一下。

廢話說完,那么就正式開始我們的項(xiàng)目吧。


【Step 1】思考

在朋友圈項(xiàng)目中,最難的那一部分——即如何做到圖片放大縮小已經(jīng)是解決了(感謝官方代碼-V-),那么現(xiàn)在我們遇到的難題有兩個(gè):

  • 如何做到順利的過渡到新的Activity中
  • 圖片縮小的時(shí)候如何正確的回歸到前一個(gè)Activity的小圖中

在朋友圈項(xiàng)目里,我們知道做到這種圖片的由小到大的過渡實(shí)際上是一個(gè)障眼法,就是大圖一開始不可見并且以小圖的大小開始顯示,并做放大和位移動畫達(dá)到一種視覺上看起來像是從小圖放大的感覺。

而這兩者的實(shí)際核心在于得到View的繪制區(qū)域,也就是getGlobalVisibleRect()方法,在朋友圈項(xiàng)目里,我把大圖和時(shí)間線放到同一個(gè)Activity的原因就是因?yàn)榧词筕iew不可見,但只要執(zhí)行到resume后,就可以拿到繪制區(qū)域。

但如果放到一個(gè)新的Activity里,我們就沒法這么做了,因?yàn)樵?strong>onCreate()里面,我們并無法拿到View的屬性信息,也就拿不到繪制區(qū)域了。

然而當(dāng)初做點(diǎn)擊展開控件的時(shí)候(鏈接→)《一起擼個(gè)朋友圈吧(step5) - 控件篇【點(diǎn)擊展開】》 我講述過TextView的onPreDraw()方法,那時(shí)候我留意到TextView實(shí)現(xiàn)了OnPreDrawListener,便以為只有某些View實(shí)現(xiàn)了這個(gè)方法,其他View使用的話是無效的,直到最近查閱了View的資料之后,才發(fā)現(xiàn)其實(shí)無論是什么View,都會在ViewRootImpl的performTraversals()方法里檢測onPreDraw(cancelDraw),而在執(zhí)行這個(gè)方法之前,實(shí)際上已經(jīng)是measure過了,所以這個(gè)方法對于任何View都是有效的。

有了這一點(diǎn),我們就可以解決上面的問題了,在onPreDraw里面得到繪制區(qū)域,然后計(jì)算比率之后進(jìn)行動畫的展開就可以實(shí)現(xiàn)進(jìn)入Activity的時(shí)候開展動畫。

回到我們的第一個(gè)問題,解決了動畫播放之后,我們還要解決的是如何打開窗口的時(shí)候不進(jìn)行動畫,關(guān)于這一點(diǎn)是在再簡單不過了,我們只需要startActivity后執(zhí)行overridePendingTransition(0, 0);就可以禁用窗口切換動畫了。

至此,我們第一個(gè)問題解決的思路如下:

  • 目標(biāo)ImageView實(shí)現(xiàn)onPreDrawListener,并在里面獲取getGlobalVisibleRect。
  • startActivity禁用動畫(特指位移動畫,實(shí)際上Alpha動畫還是可以接受的),使用戶的焦點(diǎn)集中在圖片中而不集中在Activity過場動畫中。

然后第二個(gè)問題,在朋友圈項(xiàng)目中也講解過,在view點(diǎn)擊的時(shí)候就把view的rect傳過來,最后執(zhí)行退出動畫時(shí)回歸原來的位置即可。


【Step 2】封裝

如題,我們的標(biāo)題名字叫做BaseActivity,因此我們的目的很簡單,就是讓子類輕松實(shí)現(xiàn)這個(gè)效果,并且可以更好的拓展,而不要說只能是固定的一個(gè)Activity。

因此我們的BaseActivity需要實(shí)現(xiàn)以下幾個(gè)功能:

  • 得到目標(biāo)View,即最終放大的View
  • 播放進(jìn)場動畫/退場動畫
  • 實(shí)現(xiàn)核心算法,并保證私有,對內(nèi)保護(hù)
  • 判斷是否進(jìn)行動畫

綜上所述,我們暫時(shí)可以寫出如下的代碼結(jié)構(gòu):

public abstract class BaseScaleElementAnimaActivity<V extends ImageView> extends AppCompatActivity {

    @Override protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //數(shù)據(jù)初始化
        initData();
    }

    @Override public void setContentView(@LayoutRes int layoutResID) {
        super.setContentView(layoutResID);
        //針對目標(biāo)View的初始化
        initImageView();
    }

    private void initData() {

    }

    private void initImageView() {
        
    }

    //進(jìn)場過渡動畫(放大)
    private void playEnterAnima() {
      
    }

    //退場過渡動畫(縮?。?    private void playExitAnima() {
      
    }

    @Override public void finish() {
        super.finish();
        overridePendingTransition(0, android.R.anim.fade_out);
    }

    //子類限制
    protected abstract V getAnimaedImageView();
    //子類限制(此處用的Glide)
    protected abstract void onLoadingPicture(SimpleTarget targetImageView, String url);
    //放大/縮小比例計(jì)算
    private float[] calculateRatios(Rect startBounds, Rect finalBounds) {
      
    }
    //startActivity方法
    public static void startWithScaleElementActivity(Activity from,
                                                     @Nullable String picUrl,
                                                     @Nullable Rect fromRect,
                                                     Class<? extends BaseScaleElementAnimaActivity> clazz) {
        Intent intent = new Intent(from, clazz);
        intent.putExtra("url", picUrl);
        intent.putExtra("fromRect", fromRect);
        from.startActivity(intent);
        //禁用過渡動畫
        from.overridePendingTransition(0, 0);
    }
}

對于子類而言,它并不需要知道如何實(shí)現(xiàn)放大/縮小動畫,它只需要提供最終展示的View和在什么時(shí)候載入圖片的時(shí)機(jī)(本項(xiàng)目采用Glide,其他圖片框架請自行替換設(shè)計(jì))。

所以在父類的onCreate中,我們需要拿到前一個(gè)Activity點(diǎn)擊的View的繪制區(qū)域以及圖片url,在setContentView中,我們需要子類提供目標(biāo)View,其余操作都放在父類執(zhí)行。

在使用該功能的Activity時(shí)必須采用對應(yīng)的靜態(tài)方法,畢竟咱們有點(diǎn)特殊是吧。。。

【第一章節(jié)完,下一章開始實(shí)現(xiàn)動畫】

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

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

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