<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>

          這效果炸了!自定義 仿BiliBili 圖片3D切換效果

          共 5803字,需瀏覽 12分鐘

           ·

          2022-01-13 05:53

          ?BATcoder技術(shù)群,讓一部分人先進大廠

          大家好,我是劉望舒,騰訊最具價值專家,著有三本業(yè)內(nèi)知名暢銷書,連續(xù)五年蟬聯(lián)電子工業(yè)出版社年度優(yōu)秀作者,百度百科收錄的資深技術(shù)專家。

          前華為面試官、獨角獸公司技術(shù)總監(jiān)。


          想要加入?BATcoder技術(shù)群,公號回復(fù)BAT?即可。


          作者:小白彡 鏈接:https://www.jianshu.com/p/aa6770d29376

          最近刷B站看到一個比較有意思的圖片切換效果,在查看一個用戶發(fā)的圖片的時候是平滑過渡,如果下一張圖片是另一個用戶發(fā)的,則會觸發(fā)一個3D翻轉(zhuǎn)的效果,不止是圖片翻轉(zhuǎn),連帶里面的布局也會一起翻轉(zhuǎn)。

          話不多說、先上效果圖:

          先分析一下這個效果由哪幾部分組成:

          • 1.左右平移。
          • 2.上下翻轉(zhuǎn)。

          咦。。就這,就這

          真就這么簡單,先看第一個左右平移,這是一個很普通的ViewPager可以做到的效果,直接使用ViewPager就可以了,下面是使用ViewPager的代碼:


          ????????ViewPager?vPage?=?findViewById(R.id.vpage);

          ????????List?mFragments?=?new?ArrayList<>();
          ????????mFragments.add(R.mipmap.one);
          ????????mFragments.add(R.mipmap.two);
          ????????mFragments.add(R.mipmap.three);
          ????????mFragments.add(R.mipmap.four);
          ????????mFragments.add(R.mipmap.five);

          ????????BliPageAdapter?pageAdapter?=?new?BliPageAdapter(this,?mFragments);
          ????????vPage.setAdapter(pageAdapter);

          這樣就可以實現(xiàn),左右平移的效果了(ViewPager的具體使用參考別的文章或官方文檔)

          那第二個翻轉(zhuǎn)該怎么實現(xiàn)呢,這才是最主要的,在效果圖中可以明顯看到3D的效果,那么肯定會用到Camera(非相機)來做, 那么3D效果是作用在哪個控件上呢?如果作用到ViewPager上,那么看到的畫面就全部是傾斜的,如果作用到圖片上,那么只有】圖片傾斜,里面的內(nèi)容不會傾斜,所以判斷3D效果是作用在ViewPager的每一個子View上,這樣的話,看到的畫面是正的,但里面的內(nèi)容可傾斜可不傾斜,有了這個依據(jù),就可以開始實現(xiàn)了:

          1.給ViewPager設(shè)置PageTransformer用來監(jiān)聽滑動的位置,因為3D效果會根據(jù)滑動的位置來決定傾斜的角度。
          ?vPage.setPageTransformer(false,?new?BliPageTransformer());
          BliPageTransformer?實現(xiàn)了ViewPager.PageTransformer接口:

          public?class?BliPageTransformer?implements?ViewPager.PageTransformer?{

          ????@Override
          ????public?void?transformPage(@NonNull?View?page,?float?position)?{
          ???????
          ????}
          }
          2.自定義ViewPager子View,因為要在子View的dispatchDraw中對顯示的內(nèi)容做3D變換效果,

          我使用的是ConstraintLayout為最外層的容器,所以我自定義BliConstraintLayout繼承自ConstraintLayout, 重 寫dispatchDraw方法:

          public?class?BliConstraintLayout?extends?ConstraintLayout?
          ???@Override
          ????protected?void?dispatchDraw(Canvas?canvas)?
          {
          ????????//camera保存狀態(tài)
          ????????camera.save();
          ????????//camera設(shè)置繞Y軸旋轉(zhuǎn)角度
          ????????camera.rotateY(rotateY);
          ????????//將變換應(yīng)用到canvas上
          ????????camera.applyToCanvas(canvas);
          ????????camera.getMatrix(matrix);
          ????????if?(!isLeftRotate){
          ????????????//設(shè)置靠左進行旋轉(zhuǎn)
          ????????????matrix.preTranslate(-?getWidth(),?-?getHeight()?>>?1);
          ????????????matrix.postTranslate(getWidth(),?getHeight()?>>?1);
          ????????}?else?{
          ????????????//設(shè)置靠右進行旋轉(zhuǎn)
          ????????????matrix.preTranslate(0,?-?getHeight()?>>?1);
          ????????????matrix.postTranslate(0,?getHeight()?>>?1);
          ????????}
          ????????canvas.setMatrix(matrix);
          ????????//camera恢復(fù)狀態(tài)
          ????????camera.restore();
          ????????super.dispatchDraw(canvas);
          ????}

          ViewPager子View的xml文件外層容器替換成自定義的BliConstraintLayout

          核心代碼就在這里,Camera的用法可以參考其他博客或官方文檔,

          這里值得注意的是:

          • 1.camera需要保存和恢復(fù)狀態(tài),不然下一次繪制會拿到上一次改變過的狀態(tài)。
          • 2.使用matrix對變換矩陣進行平移,因為默認變換都是針對坐標點(0,0)的,而上面的3D效果,需要的是x軸 靠左和靠右, y軸居中.
          • 3.這些設(shè)置都要放在 super.dispatchDraw(canvas)之前,因為在super.dispatchDraw(canvas)調(diào)用后表示界面已經(jīng)開始繪制的,所以要在繪制之前給它設(shè)置成我們需要變換的樣子。

          在后頭看看BliPageTransformer中該怎么設(shè)置根據(jù)滑動位置設(shè)置3D傾斜:

          ?@Override
          ????public?void?transformPage(@NonNull?View?page,?float?position)?{
          ????????BliConstraintLayout?bliConstraintLayout?=?(BliConstraintLayout)?page;
          ????????/**
          ?????????*?傾斜度
          ?????????*/

          ????????int?tiltDegree?=?34;
          ????????float?v?=?position?*?tiltDegree;
          ????????bliConstraintLayout.setRotateY(v);
          ????????if?(position?>?0){
          ????????????bliConstraintLayout.setIsLeftRotate(true);
          ????????}else{
          ????????????bliConstraintLayout.setIsLeftRotate(false);
          ????????}
          ????}

          先看看方法里的參數(shù)

          • page : 當(dāng)前子View
          • position:先簡單了解一下position:position = -1 當(dāng)前顯示頁的上一頁。position = 0 當(dāng)前顯示頁。position = 1當(dāng)前顯示頁的下一頁。

          那么在滑動到下一頁的過程中position的變化就是:

          0 -> -1 : 對應(yīng)page為當(dāng)前顯示頁

          1 -> 0 :對應(yīng)page為當(dāng)前顯示頁的下一頁

          獲取到對應(yīng)View之后,再根據(jù)position計算對應(yīng)值:

          例如我設(shè)置的最大角度為34度, 那么在1->0的過程中,角度會由34 -> 0

          再判斷當(dāng)前View是當(dāng)前頁,還是下一頁,如果是當(dāng)前頁那么是相對布局的右邊傾斜,如果是當(dāng)前頁的下一頁那么應(yīng)該相對布局的左邊傾斜。

          再將計算出的值設(shè)置到我們自定義的BliConstraintLayout 中,重繪畫布。

          下面就可以運行看看效果了:


          完美復(fù)刻,一鍵三連。

          下面貼一下關(guān)鍵點完整代碼:

          public?class?BliPageTransformer?implements?ViewPager.PageTransformer?{

          ????@Override
          ????public?void?transformPage(@NonNull?View?page,?float?position)?{
          ????????BliConstraintLayout?bliConstraintLayout?=?(BliConstraintLayout)?page;
          ????????/**
          ?????????*?傾斜度
          ?????????*/

          ????????int?tiltDegree?=?34;
          ????????float?v?=?position?*?tiltDegree;
          ????????bliConstraintLayout.setRotateY(v);
          ????????if?(position?>?0){
          ????????????bliConstraintLayout.setIsLeftRotate(true);
          ????????}else{
          ????????????bliConstraintLayout.setIsLeftRotate(false);
          ????????}
          ????}
          }
          public?class?BliConstraintLayout?extends?ConstraintLayout?{

          ????private?float?rotateY?=?0;

          ????private?boolean?isLeftRotate?=?true;

          ????private?Matrix?matrix?=?new?Matrix();

          ????private?Camera?camera?=?new?Camera();

          ????public?BliConstraintLayout(@NonNull?Context?context)?{
          ????????super(context);
          ????}

          ????public?BliConstraintLayout(@NonNull?Context?context,?@Nullable?AttributeSet?attrs)?{
          ????????super(context,?attrs);
          ????}

          ????public?BliConstraintLayout(@NonNull?Context?context,?@Nullable?AttributeSet?attrs,?int?defStyleAttr)?{
          ????????super(context,?attrs,?defStyleAttr);
          ????}

          ????@Override
          ????protected?void?dispatchDraw(Canvas?canvas)?{
          ????????//camera保存狀態(tài)
          ????????camera.save();
          ????????//camera設(shè)置繞Y軸旋轉(zhuǎn)角度
          ????????camera.rotateY(rotateY);
          ????????//將變換應(yīng)用到canvas上
          ????????camera.applyToCanvas(canvas);
          ????????camera.getMatrix(matrix);
          ????????if?(!isLeftRotate){
          ????????????//設(shè)置靠左進行旋轉(zhuǎn)
          ????????????matrix.preTranslate(-?getWidth(),?-?getHeight()?>>?1);
          ????????????matrix.postTranslate(getWidth(),?getHeight()?>>?1);
          ????????}?else?{
          ????????????//設(shè)置靠右進行旋轉(zhuǎn)
          ????????????matrix.preTranslate(0,?-?getHeight()?>>?1);
          ????????????matrix.postTranslate(0,?getHeight()?>>?1);
          ????????}
          ????????canvas.setMatrix(matrix);
          ????????//camera恢復(fù)狀態(tài)
          ????????camera.restore();
          ????????super.dispatchDraw(canvas);
          ????}

          ????public?void?setRotateY(float?rotateY){
          ????????this.rotateY?=?rotateY;
          ????????invalidate();
          ????}

          ????public?void?setIsLeftRotate(boolean?isLeft){
          ????????this.isLeftRotate?=?isLeft;
          ????}
          }

          收工。




          ? 耗時2年,Android進階三部曲第三部《Android進階指北》出版!

          ? 『BATcoder』做了多年安卓還沒編譯過源碼?一個視頻帶你玩轉(zhuǎn)!

          ? 『BATcoder』我去!安裝Ubuntu還有坑?

          ? 重生!進階三部曲第一部《Android進階之光》第2版 出版!

          為了防止失聯(lián),歡迎關(guān)注我的小號


          ??微信改了推送機制,真愛請星標本公號??
          瀏覽 65
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <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>
                  天天爽夜夜爽人人爽 | 淫秽三级片中文字幕在线免费观看 | 性爱视频网站 | 黄色国产网站 | 无码一区二区三区四区五区在线看 |