<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Android實(shí)現(xiàn)炫酷的ViewPager3D組件

          共 3240字,需瀏覽 7分鐘

           ·

          2022-02-20 09:20

          不知道還有沒有人記得風(fēng)靡于一時(shí)的gallery3d組件?


          類似于這樣的效果:



          為什么忽然會(huì)提到gallery3d?是因?yàn)閯偤糜行枨笠鲱愃菩Ч捻?yè)面。


          剛把視覺拿到手的時(shí)候,首先想到的便是gallery3d組件,將gallery3d組件挪過來后發(fā)現(xiàn)并不是很合意。原因嘛,一個(gè)是效果差別較大,另外就是gallery這個(gè)組件也早被ViewPager代替而過時(shí)棄用了。于是就打算用ViewPager自己手寫一個(gè)。


          做需求之前當(dāng)然要先分析設(shè)計(jì)一波。

          我們先來拆分一下它的特性:
          1、中間的Page保持大小不變兩邊逐層遞減。
          2、中間的Page保持角度不變兩邊以Y軸向中間方向內(nèi)旋,角度遞增
          3、每一頁(yè)P(yáng)age都有一個(gè)倒影,倒影半透明,透明度從中間往兩邊逐層遞減
          4、每一頁(yè)都深入部分到前一頁(yè)(以中間為第一頁(yè)),但重疊區(qū)域只顯示上層Page


          1、逐層遞減

          利用View的scaleX、scaleY的特性,對(duì)Page進(jìn)行縮放。

          設(shè)定縮減因子為scaleFactor,
          那么每一層的遞減公式為:

            1-scaleFactor+scaleFactor*(1 - Math.abs(pagePosition))


          2、逐層旋轉(zhuǎn)

          利用View的setRotationY特性,對(duì)Page進(jìn)行內(nèi)旋。

          設(shè)定旋轉(zhuǎn)因子為rotationFactor,
          那么每一層的內(nèi)旋角度公式為:

          float rotationY = - position * rotation//控制一下最大旋轉(zhuǎn)角if( rotationY > MAX_ROTATION ) {   rotationY = MAX_ROTATION ;} else if ( rotationY < - MAX_ROTATION ) {   rotationY = - MAX_ROTATION ;}return rotationY;


          3、倒影

          利用View的buildDrawingCache方法生成View的cache圖片,然后對(duì)圖片處理為倒影,顯示到對(duì)應(yīng)的View的下方。透明度的變化,直接使用View.setAlpha()方法,倒影生成方式大概是這樣:

          //生成cache圖片view.buildDrawingCache();Bitmap bm = paramView.getDrawingCache();Bitmap tmp = Bitmap.createBitmap(bm.getWidth(), bm.getHeight(), Config.ARGB_4444);new Canvas(tmp).drawBitmap(bm, 0, 0, new Paint());view.destroyDrawingCache();view.setDrawingCacheEnabled(false);//返回倒影圖片return createReflectedImage(tmp,0.5);


          4、裁切重疊區(qū)域

          要讓Page之間重疊,我們利用View的translationX屬性,讓View移動(dòng)到上一頁(yè)的區(qū)域內(nèi)。


          裁切則利用canvas的clipRect方法,對(duì)View的顯示區(qū)域進(jìn)行裁切。但是前提是要能準(zhǔn)確計(jì)算重疊的部分。


          要準(zhǔn)確計(jì)算重疊部分~,我們先看一張圖吧。



          上圖中的灰色半透明部分(0)為中間的Page,紅色半透明部分(1)為右邊第一頁(yè),依次類推,黑色線框是在第一頁(yè)執(zhí)行完 scale、translationX后的位置、紅色半透明以及重疊的部分則是執(zhí)行完 scale、translationX后再執(zhí)行rotationY后的最終位置。


          由圖我們知道,想要準(zhǔn)確的裁切掉(0)和(1)的重疊區(qū)域,必須要準(zhǔn)確計(jì)算出非重疊區(qū)域/(1)的大小的比例。


          計(jì)算View旋轉(zhuǎn)后的寬度


          怎么計(jì)算呢?
          這要用到立體幾何的知識(shí),下圖是View內(nèi)旋后的中心線上的平面圖,至于怎么畫出來的,自己腦補(bǔ)吧,囧~。



          其中:
          Ao 是視距距離;
          θ  是旋轉(zhuǎn)角度;
          ao 是原始寬度;
          a’o 是旋轉(zhuǎn)后映射到平面坐標(biāo)系上的寬度。


          A點(diǎn)是眼睛的視角位置;
          ab是View的旋轉(zhuǎn)中心線,也是View的初始寬度;
          穿過ab線的藍(lán)色線條是View的繪制平面;
          xy是ab沿o點(diǎn)進(jìn)行Y軸旋轉(zhuǎn)后的位置;
          紅色線條是眼睛看向旋轉(zhuǎn)后x、y點(diǎn)經(jīng)過或延長(zhǎng)經(jīng)過繪制平面的最大邊界;
          也就是說,經(jīng)過o點(diǎn)的θ角度旋轉(zhuǎn)后,View在繪制面板上看到的大小將由ab位置變成a'b'的位置。


          我們解題的目標(biāo)是,求出a'o,以及b'o的長(zhǎng)度,這樣給定我們?cè)紝挾纫约靶D(zhuǎn)中心點(diǎn),我們就能計(jì)算出最終的寬度。


          求a'o的步驟


          ab是原始坐標(biāo),o是原點(diǎn)(0,0)
          0、ao=xo,bo=yo
          ∵ cosθ = xx' ÷ xo

          1、xx' = cosθ ? xo
          2、x’o = sinθ ? xo


          ∵ ∠Axx' = ∠Aa'o
          ∴ Ao / a'o = Ax' / xx'
          ∴ ( Ao + x'o ) ÷ xx' = Ao / a'o
          a’o = (Ao * xx') / (Ao + x'o)
          根據(jù)0、1、2得
          a’o = (Ao * cosθ * ao) / ( Ao + sinθ * ao )


          同時(shí)可以逆推出:
          Ao = a'o*sinθ *ao / (a'o-cosθ * ao);
          ao = (a'o * Ao / (Ao *cosθ - a'o * sinθ));


          查了半天沒查到Android View的初始視距是多少,所以程序中使用的Ao的大小其實(shí)是通過給定 θ 以及 ao、a'o計(jì)算出來的,至于這3個(gè)數(shù)據(jù)的采集方式,可以去參考sample內(nèi)的AoTestActivity類。


          求b'o的步驟


          yy’ / b’o = Ay’ /Ao
          b’o = yy’ * Ao / Ay’
          Ay’ = Ao - y’o
          yy’ =  sin ( 90 - θ ) * yo
          y’o = cos ( 90 - θ ) * yo


          b’o = sin ( 90 - θ ) * yo * Ao / ( Ao - cos ( 90 - θ ) * yo)


          根據(jù)公式計(jì)算第(1)頁(yè)的最終大小(偽代碼):

          float ao = page.getWidth()*scale;float width = (Ao * cosθ * ao) / ( Ao + sinθ * ao )


          計(jì)算出(1)-b的寬度(偽代碼)

          float keepWidth = page.getWidth*(1 - (scaleFactor - (1 -translationXFactor) ) / scaleFactor))根據(jù)keepWidth反求出旋轉(zhuǎn)之前的寬度keepWidth = keepWidth * Ao / (Ao * cosθ - keepWidth * sinθ )clipStart = 0;//clip操作要計(jì)算View的最初始的大小,所以這里要用page.getWidth()clipRight = page.getWidth() - keepWidth;//TODO 上面的只是第一頁(yè)的算法,其他頁(yè)數(shù)會(huì)有不同,都寫下來就太多了,所以,大家有興趣直接去翻源碼吧。最難的是中間的兩頁(yè)。


          至此重疊區(qū)域的計(jì)算方式有了,然后根據(jù)上面的步驟以及計(jì)算方式最終搗鼓出這么一個(gè)效果的組件:



          當(dāng)然不僅僅是這么簡(jiǎn)單?。?br style="box-sizing: border-box;color: rgb(64, 64, 64);font-family: -apple-system, BlinkMacSystemFont, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Segoe UI", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Helvetica Neue", Helvetica, Arial, sans-serif;font-size: 16px;text-align: start;white-space: normal;background-color: rgb(255, 255, 255);">這個(gè)組件內(nèi)置了translation、scale、rotation、alpha4個(gè)轉(zhuǎn)換器,可以自定義這4個(gè)轉(zhuǎn)換器實(shí)現(xiàn)各種不同效果的翻頁(yè)。圖1就是自定義后做出的新效果。


          具體如何使用以及源碼請(qǐng)移步:
          https://github.com/bambootang/ViewPager3D


          到這里就結(jié)束啦。

          瀏覽 103
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲乱伦A片| 午夜干干| 特黄特色一级特黄大片 | 操一操鲁一鲁 | 中文字幕av久久久久久欧洲尺码 |