Android Browser学习六 多窗口: NavScreen 切换窗口

本文详细解析了AndroidBrowser中实现多标签切换功能的UI结构及动画效果,包括显示和隐藏多窗口列表的动画、左右滑动删除标签的动画等,并介绍了NavScreen和AnimScreen等关键组件的作用。

想要实现浏览器的多标签切换功能, 需要一个用户交互界面, 这个界面在Android Browser中就是NavScreen了:

这里我们介绍一下下面这个UI的实现, 主要代码在NavScreen.java中.

我们知道, 在Android Browser中 用以和用户打交道的功能基本都被限制在了BaseUI中, 在手机上它的实现就是PhoneUI:

显示多窗口列表当然也是不例外的:PhoneUI::showNavScreen:


01//点击按钮显示多窗口列表
02  void showNavScreen() {
03      mUiController.setBlockEvents(true); //拦截多窗口外的其他操作
04      if (mNavScreen == null) {
05          mNavScreen = new NavScreen(mActivity, mUiController, this);
06          mCustomViewContainer.addView(mNavScreen, COVER_SCREEN_PARAMS);
07      } else {
08          mNavScreen.setVisibility(View.VISIBLE);
09          mNavScreen.setAlpha(1f);
10          mNavScreen.refreshAdapter();
11      }
12      mActiveTab.capture();
13      if (mAnimScreen == null) {
14          //这是动画的视图 ,多标签窗口切换的动画师phoneui来实现的而不是 navscreen ,也就是说我点击一个tab 剩下的看到的其实是
15          //真正的web窗口
16          mAnimScreen = new AnimScreen(mActivity);
17      } else {
18          mAnimScreen.mMain.setAlpha(1f);
19          mAnimScreen.mTitle.setAlpha(1f);
20          mAnimScreen.setScaleFactor(1f);
21      }
22      //设置动画需要截图的view
23      mAnimScreen.set(getTitleBar(), getWebView());
24      if (mAnimScreen.mMain.getParent() == null) {
25          //如果animscreen 的main没有父亲, 说明是执行了 全屏模式
26          mCustomViewContainer.addView(mAnimScreen.mMain, COVER_SCREEN_PARAMS);
27      }
28      mCustomViewContainer.setVisibility(View.VISIBLE);
29      mCustomViewContainer.bringToFront();//把这个view放到顶层
30      mAnimScreen.mMain.layout(0, 0, mContentView.getWidth(),
31              mContentView.getHeight()); //动画的宽度和contentview一样大
32      int fromLeft = 0;
33      int fromTop = getTitleBar().getHeight();
34      int fromRight = mContentView.getWidth();
35      int fromBottom = mContentView.getHeight();
36      int width = mActivity.getResources().getDimensionPixelSize(R.dimen.nav_tab_width);
37      int height = mActivity.getResources().getDimensionPixelSize(R.dimen.nav_tab_height);
38      int ntth = mActivity.getResources().getDimensionPixelSize(R.dimen.nav_tab_titleheight);
39      int toLeft = (mContentView.getWidth() - width) / 2;
40      int toTop = ((fromBottom - (ntth + height)) / 2 + ntth);
41      int toRight = toLeft + width;
42      int toBottom = toTop + height;
43      float scaleFactor = width / (float) mContentView.getWidth();
44      detachTab(mActiveTab);
45      mContentView.setVisibility(View.GONE);
46      AnimatorSet set1 = new AnimatorSet();
47      AnimatorSet inanim = new AnimatorSet();
48      //使用上下左右的位置 使得  tab的运动轨迹 从整个屏幕 位置缩小到tab,无论当前tab在哪里
49      ObjectAnimator tx = ObjectAnimator.ofInt(mAnimScreen.mContent, "left",
50              fromLeft, toLeft);
51      ObjectAnimator ty = ObjectAnimator.ofInt(mAnimScreen.mContent, "top",
52              fromTop, toTop);
53      ObjectAnimator tr = ObjectAnimator.ofInt(mAnimScreen.mContent, "right",
54              fromRight, toRight);
55      ObjectAnimator tb = ObjectAnimator.ofInt(mAnimScreen.mContent, "bottom",
56              fromBottom, toBottom);
57      ObjectAnimator title = ObjectAnimator.ofFloat(mAnimScreen.mTitle, "alpha",
58              1f, 0f);
59      ObjectAnimator sx = ObjectAnimator.ofFloat(mAnimScreen, "scaleFactor",
60              1f, scaleFactor);
61      
62      ObjectAnimator blend1 = ObjectAnimator.ofFloat(mAnimScreen.mMain,
63              "alpha", 1f, 0f);
64      blend1.setDuration(100);
65 
66      inanim.playTogether(tx, ty, tr, tb, sx, title);
67      inanim.setDuration(200);
68      set1.addListener(new AnimatorListenerAdapter() {
69          @Override
70          public void onAnimationEnd(Animator anim) {
71              mCustomViewContainer.removeView(mAnimScreen.mMain);
72              finishAnimationIn();
73              mUiController.setBlockEvents(false);
74          }
75      });
76      set1.playSequentially(inanim, blend1);//inanim播放ok后播放 blend1 也就是先缩放然后在透明
77      set1.start();
78  }



这里还实现了一个打开多窗口的动画, 我们暂时先不去考虑, 先看NavScreen的数据结构:


他的结构也不是很复杂, 拿到了Activity 和Controller的引用, 然后有一个NavTabScroller (继承自NavTabScroller )和 一个TabAdapter (继承自 BaseAdapter)的成员, 他们是多窗口列表view的具体实现和数据来源了. NavScreen有一些Tab的操作, 他们基本都需要通知到Controller, 因为NavScreen只不过是UI 真正的操作是Controller来做的. 

看一下NavTabScroller 是一个ScrollView, 多窗口之所以可以滑动就全靠他了, 他还实现了横向竖向滑动, 载入adapter的数据等功能:


01public class NavTabScroller extends ScrollerView {
02    static final int INVALID_POSITION = -1;
03    static final float[] PULL_FACTOR = { 2.5f, 0.9f };
04 
05    interface OnRemoveListener {
06        public void onRemovePosition(int position);
07    }
08 
09    interface OnLayoutListener {
10        public void onLayout(int l, int t, int r, int b);
11    }
12 
13    private ContentLayout mContentView; //实际上是一个linearlayout
14    private BaseAdapter mAdapter;
15    private OnRemoveListener mRemoveListener;
16    private OnLayoutListener mLayoutListener;
17    private int mGap;
18    private int mGapPosition;
19    private ObjectAnimator mGapAnimator;
20 
21    // after drag animation velocity in pixels/sec
22    private static final float MIN_VELOCITY = 1500; //最小的滑动
23    private AnimatorSet mAnimator;
24 
25    private float mFlingVelocity;
26    private boolean mNeedsScroll;
27    private int mScrollPosition;
28 
29    DecelerateInterpolator mCubic;
30    int mPullValue;




他装载数据的操作是setAdapter函数调用handleDataChanged函数实现的:


01//装载多窗口数据
02  void handleDataChanged(int newscroll) {
03      int scroll = getScrollValue(); //是x方向scroll 还是y
04      if (mGapAnimator != null) {
05          mGapAnimator.cancel();//取消动画
06      }
07      mContentView.removeAllViews();
08      for (int i = 0; i < mAdapter.getCount(); i++) {
09          View v = mAdapter.getView(i, null, mContentView);//从adapter中拿到view 添加到linearlayout上listview等其实也是这样实现的
10          LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
11                  LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
12          lp.gravity = (mHorizontal ? Gravity.CENTER_VERTICAL : Gravity.CENTER_HORIZONTAL);
13          mContentView.addView(v, lp);//添加tabview到那个mContentView . 居中显示
14          if (mGapPosition > INVALID_POSITION){
15              adjustViewGap(v, i);
16          }
17      }
18      if (newscroll > INVALID_POSITION) {
19          newscroll = Math.min(mAdapter.getCount() - 1, newscroll);//newscroll 是从0 开始到 adapter.count的
20          mNeedsScroll = true;
21          mScrollPosition = newscroll;
22          requestLayout();
23      } else {
24          setScrollValue(scroll); //滑动到顶部/左边
25      }
26  }




好 大体的UI就差不多这些了, 下面是其动画的实现:

其动画分为以下几个:

1.点击多窗口按钮 的时候, 整个浏览器窗口会缩小到多窗口列表, 然后显示出其他的窗口标签供作者选择

2.点击多窗口列表任何一个窗口    其他的多窗口标签会消失 , 

整个窗口会扩到到整个屏幕

3.在多窗口列表中左右滑动任何一个窗口, 整个窗口会渐变和移动 直到删除


4.其实这个"listview"还有回弹功能, 效果是使小窗口的间距缩小,不过效果不是很明显, 应该有点小bug


那就从第一个动画开始分析:

这个动画是在PhoneUI::showNavScreen()函数实现的, 其实就是一个animator动画:

01//点击按钮显示多窗口列表
02  void showNavScreen() {
03      mUiController.setBlockEvents(true); //拦截多窗口外的其他操作
04      if (mNavScreen == null) {
05          mNavScreen = new NavScreen(mActivity, mUiController, this);
06          mCustomViewContainer.addView(mNavScreen, COVER_SCREEN_PARAMS);
07      } else {
08          mNavScreen.setVisibility(View.VISIBLE);
09          mNavScreen.setAlpha(1f);
10          mNavScreen.refreshAdapter();
11      }
12      mActiveTab.capture();
13      if (mAnimScreen == null) {
14          //这是动画的视图 ,多标签窗口切换的动画师phoneui来实现的而不是 navscreen ,也就是说我点击一个tab 剩下的看到的其实是
15          //真正的web窗口
16          mAnimScreen = new AnimScreen(mActivity);
17      } else {
18          mAnimScreen.mMain.setAlpha(1f);
19          mAnimScreen.mTitle.setAlpha(1f);
20          mAnimScreen.setScaleFactor(1f);
21      }
22      //设置动画需要截图的view
23      mAnimScreen.set(getTitleBar(), getWebView());
24      if (mAnimScreen.mMain.getParent() == null) {
25          //如果animscreen 的main没有父亲, 说明是执行了 全屏模式
26          mCustomViewContainer.addView(mAnimScreen.mMain, COVER_SCREEN_PARAMS); //把需要做动画的view添加到整个布局的上层
27      }
28      mCustomViewContainer.setVisibility(View.VISIBLE);
29      mCustomViewContainer.bringToFront();//把这个view放到顶层
30      mAnimScreen.mMain.layout(0, 0, mContentView.getWidth(),
31              mContentView.getHeight()); //动画的宽度和contentview一样大
32      int fromLeft = 0;
33      int fromTop = getTitleBar().getHeight();
34      int fromRight = mContentView.getWidth();
35      int fromBottom = mContentView.getHeight();
36      int width = mActivity.getResources().getDimensionPixelSize(R.dimen.nav_tab_width);
37      int height = mActivity.getResources().getDimensionPixelSize(R.dimen.nav_tab_height);
38      int ntth = mActivity.getResources().getDimensionPixelSize(R.dimen.nav_tab_titleheight);
39      int toLeft = (mContentView.getWidth() - width) / 2;
40      int toTop = ((fromBottom - (ntth + height)) / 2 + ntth);
41      int toRight = toLeft + width;
42      int toBottom = toTop + height;
43      float scaleFactor = width / (float) mContentView.getWidth();
44      detachTab(mActiveTab);
45      mContentView.setVisibility(View.GONE);
46      AnimatorSet set1 = new AnimatorSet();
47      AnimatorSet inanim = new AnimatorSet();
48      //使用上下左右的位置 使得  tab的运动轨迹 从整个屏幕 位置缩小到屏幕的中心 ,无论当前tab在哪里, 不过轨迹是一样的
49      ObjectAnimator tx = ObjectAnimator.ofInt(mAnimScreen.mContent, "left",
50              fromLeft, toLeft);
51      ObjectAnimator ty = ObjectAnimator.ofInt(mAnimScreen.mContent, "top",
52              fromTop, toTop);
53      ObjectAnimator tr = ObjectAnimator.ofInt(mAnimScreen.mContent, "right",
54              fromRight, toRight);
55      ObjectAnimator tb = ObjectAnimator.ofInt(mAnimScreen.mContent, "bottom",
56              fromBottom, toBottom);
57      ObjectAnimator title = ObjectAnimator.ofFloat(mAnimScreen.mTitle, "alpha",
58              1f, 0f);
59      ObjectAnimator sx = ObjectAnimator.ofFloat(mAnimScreen, "scaleFactor",
60              1f, scaleFactor);
61      
62      ObjectAnimator blend1 = ObjectAnimator.ofFloat(mAnimScreen.mMain,
63              "alpha", 1f, 0f);
64      blend1.setDuration(100);
65 
66      inanim.playTogether(tx, ty, tr, tb, sx, title);
67      inanim.setDuration(200);
68      set1.addListener(new AnimatorListenerAdapter() {
69          @Override
70          public void onAnimationEnd(Animator anim) {
71              mCustomViewContainer.removeView(mAnimScreen.mMain);//把做动画的view删除
72              finishAnimationIn();
73              mUiController.setBlockEvents(false);
74          }
75      });
76      set1.playSequentially(inanim, blend1);//inanim播放ok后播放 blend1 也就是先缩放然后在透明
77      set1.start();
78  }



这里我们可能要问了AnimScreen这个东西是什么呢?原来,为了提高动画的效率,其实是通过把webview的内容绘制到 Imageview 上, 需要切换的时候就把这个imageview添加到webview上面,然后做动画. 看看AnimScreen代码就明白了:



01/*
02    *其实动画是使用两个imageview在做, 这两个imageview 分别绘制了titlebar和webview
03    */
04   static class AnimScreen {
05 
06       private View mMain;
07       private ImageView mTitle;
08       private ImageView mContent;
09       private float mScale;
10       private Bitmap mTitleBarBitmap;
11       private Bitmap mContentBitmap;
12 
13       public AnimScreen(Context ctx) {
14           mMain = LayoutInflater.from(ctx).inflate(R.layout.anim_screen,
15                   null);
16           mTitle = (ImageView) mMain.findViewById(R.id.title);
17           mContent = (ImageView) mMain.findViewById(R.id.content);
18           mContent.setScaleType(ImageView.ScaleType.MATRIX);
19           mContent.setImageMatrix(new Matrix());
20           mScale = 1.0f;
21           setScaleFactor(getScaleFactor());
22       }
23 
24       /**
25        * 包titilebar和webview的截图画到动画的view上
26        * @param tbar
27        * @param web
28        */
29       public void set(TitleBar tbar, WebView web) {
30           if (tbar == null || web == null) {
31               return;
32           }
33           if (tbar.getWidth() > 0 && tbar.getEmbeddedHeight() > 0) {
34               if (mTitleBarBitmap == null
35                       || mTitleBarBitmap.getWidth() != tbar.getWidth()
36                       || mTitleBarBitmap.getHeight() != tbar.getEmbeddedHeight()) {
37                   mTitleBarBitmap = safeCreateBitmap(tbar.getWidth(),
38                           tbar.getEmbeddedHeight());
39               }
40               if (mTitleBarBitmap != null) {
41                   Canvas c = new Canvas(mTitleBarBitmap);
42                   tbar.draw(c);
43                   c.setBitmap(null);
44               }
45           } else {
46               mTitleBarBitmap = null;
47           }
48           mTitle.setImageBitmap(mTitleBarBitmap);
49            
50           mTitle.setVisibility(View.VISIBLE);
51           int h = web.getHeight() - tbar.getEmbeddedHeight();
52           if (mContentBitmap == null
53                   || mContentBitmap.getWidth() != web.getWidth()
54                   || mContentBitmap.getHeight() != h) {
55               mContentBitmap = safeCreateBitmap(web.getWidth(), h);
56           }
57           if (mContentBitmap != null) {
58               Canvas c = new Canvas(mContentBitmap);
59               int tx = web.getScrollX();
60               int ty = web.getScrollY();
61               c.translate(-tx, -ty - tbar.getEmbeddedHeight());
62               web.draw(c);
63               c.setBitmap(null);
64           }
65           mContent.setImageBitmap(mContentBitmap);
66       }
67 
68       private Bitmap safeCreateBitmap(int width, int height) {
69           if (width <= 0 || height <= 0) {
70               Log.w(LOGTAG, "safeCreateBitmap failed! width: " + width
71                       + ", height: " + height);
72               return null;
73           }
74           return Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
75       }
76 
77       /*
78        * 这个版本至显示content
79        */
80       public void set(Bitmap image) {
81           mTitle.setVisibility(View.GONE);
82           mContent.setImageBitmap(image);
83       }
84 
85       private void setScaleFactor(float sf) {
86           mScale = sf;
87           Matrix m = new Matrix();
88           m.postScale(sf,sf);
89           mContent.setImageMatrix(m);
90       }
91 
92       private float getScaleFactor() {
93           return mScale;
94       }
95 
96   }


知道了第一个动画如何实现, 第二个动画就好理解了, 正好是第一个动画的反过来, 不过这次动画的轨迹可能不一样, 因为用户可能点击的是上面或者最底下的tab:当然, 通过navScreen就可以拿到选择tab的位置:整个操作调用的地方还是比较多的比如选择tab 新建tab等都会调用到这个和动画.


01//隐藏多窗口切换 动画基本同上面显示多标签
02    void hideNavScreen(int position, boolean animate) {
03        if (!showingNavScreen()) return;
04        final Tab tab = mUiController.getTabControl().getTab(position);
05        if ((tab == null) || !animate) {//似乎还有别的可以打开tab的方式但是还不是很清楚在哪里
06            if (tab != null) {
07                setActiveTab(tab);
08            } else if (mTabControl.getTabCount() > 0) {
09                // use a fallback tab
10                setActiveTab(mTabControl.getCurrentTab());
11            }
12            mContentView.setVisibility(View.VISIBLE);
13            finishAnimateOut();
14            return;
15        }
16        NavTabView tabview = (NavTabView) mNavScreen.getTabView(position);
17        if (tabview == null) {
18            if (mTabControl.getTabCount() > 0) {
19                // use a fallback tab
20                setActiveTab(mTabControl.getCurrentTab());
21            }
22            mContentView.setVisibility(View.VISIBLE);
23            finishAnimateOut();
24            return;
25        }
26        mUiController.setBlockEvents(true);
27        mUiController.setActiveTab(tab);
28        mContentView.setVisibility(View.VISIBLE);
29        if (mAnimScreen == null) {
30            mAnimScreen = new AnimScreen(mActivity);
31        }
32        mAnimScreen.set(tab.getScreenshot());
33        mCustomViewContainer.addView(mAnimScreen.mMain, COVER_SCREEN_PARAMS); //全屏模式
34        mAnimScreen.mMain.layout(0, 0, mContentView.getWidth(),
35                mContentView.getHeight());
36        mNavScreen.mScroller.finishScroller();
37        ImageView target = tabview.mImage;
38        int toLeft = 0;
39        int toTop = getTitleBar().getHeight();
40        int toRight = mContentView.getWidth();
41        int width = target.getDrawable().getIntrinsicWidth();
42        int height = target.getDrawable().getIntrinsicHeight();
43        int fromLeft = tabview.getLeft() + target.getLeft() - mNavScreen.mScroller.getScrollX();
44        int fromTop = tabview.getTop() + target.getTop() - mNavScreen.mScroller.getScrollY();//target就是选择的tab tab的顶部位置 为了给人以 从原来位置扩大到整个屏幕的感觉
45        int fromRight = fromLeft + width;
46        int fromBottom = fromTop + height;
47        float scaleFactor = mContentView.getWidth() / (float) width;
48        int toBottom = toTop + (int) (height * scaleFactor);
49        mAnimScreen.mContent.setLeft(fromLeft);
50        mAnimScreen.mContent.setTop(fromTop);
51        mAnimScreen.mContent.setRight(fromRight);
52        mAnimScreen.mContent.setBottom(fromBottom);
53        mAnimScreen.setScaleFactor(1f);
54        AnimatorSet set1 = new AnimatorSet();
55        ObjectAnimator fade2 = ObjectAnimator.ofFloat(mAnimScreen.mMain, "alpha", 0f, 1f);
56        ObjectAnimator fade1 = ObjectAnimator.ofFloat(mNavScreen, "alpha", 1f, 0f);
57        set1.playTogether(fade1, fade2);
58        set1.setDuration(100);
59        //使用上下左右的位置 使得  tab的运动轨迹 从原来位置扩展到整个屏幕,无论整个tab在哪里
60        AnimatorSet set2 = new AnimatorSet();
61        ObjectAnimator l = ObjectAnimator.ofInt(mAnimScreen.mContent, "left",
62                fromLeft, toLeft);
63        ObjectAnimator t = ObjectAnimator.ofInt(mAnimScreen.mContent, "top",
64                fromTop, toTop);
65        ObjectAnimator r = ObjectAnimator.ofInt(mAnimScreen.mContent, "right",
66                fromRight, toRight);
67        ObjectAnimator b = ObjectAnimator.ofInt(mAnimScreen.mContent, "bottom",
68                fromBottom, toBottom);
69        ObjectAnimator scale = ObjectAnimator.ofFloat(mAnimScreen, "scaleFactor",
70                1f, scaleFactor);
71        ObjectAnimator otheralpha = ObjectAnimator.ofFloat(mCustomViewContainer, "alpha", 1f, 0f);
72        otheralpha.setDuration(100);
73        set2.playTogether(l, t, r, b, scale);
74        set2.setDuration(200);
75        AnimatorSet combo = new AnimatorSet();
76        combo.playSequentially(set1, set2, otheralpha);
77        combo.addListener(new AnimatorListenerAdapter() {
78            @Override
79            public void onAnimationEnd(Animator anim) {
80                mCustomViewContainer.removeView(mAnimScreen.mMain);//动画结束的时候把动画view 隐藏
81                finishAnimateOut();//让当前窗口渐变消失
82                mUiController.setBlockEvents(false);
83            }
84        });
85        combo.start();
86    }


对于第三个动画, 左右滑动删除的动画, 其入口有二

a.  Scrollview的onTouchEvent事件中调用的NavTabScroller::onOrthoDragFinished()函数, 其实最后还是调用到animateOut函数

01/*这是scrollview回调的一个函数,作用是 在用户左右滑动 tab之后 判断是否删除这个tab*/
02    @Override
03    protected void onOrthoDragFinished(View downView) {
04        if (mAnimator != null) return;
05        if (mIsOrthoDragged && downView != null) {
06            // offset
07            float diff = mHorizontal ? downView.getTranslationY() : downView.getTranslationX();
08            if (Math.abs(diff) > (mHorizontal ? downView.getHeight() : downView.getWidth()) / 2) {
09                // remove it 达到了删除tab的调节,开始删除
10                animateOut(downView, Math.signum(diff) * mFlingVelocity, diff);
11            } else {
12                // snap back 没有达到条件,就让view回来
13                offsetView(downView, 0);
14            }
15        }
16    }

在用户按住小tab移动的时候会执行offsetView函数:


1private void offsetView(View v, float distance) {
2      v.setAlpha(getAlpha(v, distance));
3      //setTranslationY 这个功能应该只有3.0以后才支持 让view左右滑动
4      if (mHorizontal) {
5          v.setTranslationY(distance);
6      } else {
7          v.setTranslationX(distance);
8      }
9  }





b另一种调用动画的方式比较简单了,其实就是直接调用animateOut函数:


看一下这个函数到底做了什么吧:

   1.需要删除窗口的平移和alpha渐变

   2.删除窗口后,其他窗口的上移,这是比较复杂的一个逻辑 ,大体是通过改变mGap这个参数实现,动画也是使用了animator:


01/*删除 tab 动画 (左右滑动删除 )的显示*/
02   private void animateOut(final View v, float velocity, float start) {
03       if ((v == null) || (mAnimator != null)) return; //有其他动画就不要执行这个动画
04       final int position = mContentView.indexOfChild(v);
05       int target = 0;
06       if (velocity < 0) {//动画结束的位置
07           target = mHorizontal ? -getHeight() :  -getWidth();
08       } else {
09           target = mHorizontal ? getHeight() : getWidth();
10       }
11       int distance = target - (mHorizontal ? v.getTop() : v.getLeft());
12       long duration = (long) (Math.abs(distance) * 1000 / Math.abs(velocity));//动画持续时间
13       int scroll = 0;
14       int translate = 0;
15       int gap = mHorizontal ? v.getWidth() : v.getHeight();
16       int centerView = getViewCenter(v);//获取view的中心
17       int centerScreen = getScreenCenter();//获取屏幕的中心
18       int newpos = INVALID_POSITION;
19       if (centerView < centerScreen - gap / 2) {
20           // top view删除的是上面的view
21           scroll = - (centerScreen - centerView - gap);
22           translate = (position > 0) ? gap : 0;
23           newpos = position;
24       } else if (centerView > centerScreen + gap / 2) {
25           // bottom view 删除的是底部的view
26           scroll = - (centerScreen + gap - centerView);
27           if (position < mAdapter.getCount() - 1) {
28               translate = -gap;
29           }
30       } else {
31           // center view 删除的是中间的view
32           scroll = - (centerScreen - centerView);
33           if (position < mAdapter.getCount() - 1) {
34               translate = -gap;
35           } else {
36               scroll -= gap;
37           }
38       }
39       mGapPosition = position;
40       final int pos = newpos;
41       ObjectAnimator trans = ObjectAnimator.ofFloat(v,
42               (mHorizontal ? TRANSLATION_Y : TRANSLATION_X), start, target); //控制待删除view的水平 移动
43       ObjectAnimator alpha = ObjectAnimator.ofFloat(v, ALPHA, getAlpha(v,start),//控制待删除view的透明变化
44               getAlpha(v,target));
45       AnimatorSet set1 = new AnimatorSet();
46       set1.playTogether(trans, alpha);
47       set1.setDuration(duration);
48       mAnimator = new AnimatorSet();
49       ObjectAnimator trans2 = null;
50       ObjectAnimator scroll1 = null;
51       if (scroll != 0) {
52           if (mHorizontal) {//调整scrollview的scroll位置
53               scroll1 = ObjectAnimator.ofInt(this, "scrollX", getScrollX(), getScrollX() + scroll);
54           } else {
55               scroll1 = ObjectAnimator.ofInt(this, "scrollY", getScrollY(), getScrollY() + scroll);
56           }
57       }
58       if (translate != 0) {
59           trans2 = ObjectAnimator.ofInt(this, "gap", 0, translate); //删除view会留下一个空白,需要让上面的view补充上 这里gap是 负值,因为view少了,坐标也就小了
60       }
61       final int duration2 = 200;
62       if (scroll1 != null) {
63           if (trans2 != null) {
64               AnimatorSet set2 = new AnimatorSet();
65               set2.playTogether(scroll1, trans2);
66               set2.setDuration(duration2);
67               mAnimator.playSequentially(set1, set2);
68           } else {
69               scroll1.setDuration(duration2);
70               mAnimator.playSequentially(set1, scroll1);
71           }
72       } else {
73           if (trans2 != null) {
74               trans2.setDuration(duration2);
75               mAnimator.playSequentially(set1, trans2);
76           }
77       }
78       mAnimator.addListener(new AnimatorListenerAdapter() {
79           public void onAnimationEnd(Animator a) {
80               if (mRemoveListener !=  null) {
81                   mRemoveListener.onRemovePosition(position);//通知移除tab
82                   mAnimator = null;
83                   mGapPosition = INVALID_POSITION;
84                   mGap = 0;
85                   handleDataChanged(pos);
86               }
87           }
88       });
89       mAnimator.start();
90   }

至于切换就简单了,是在controller::setActiveTab()函数进行处理.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值