美文网首页优秀案例
BottomNavigationView 控件去除放大缩小动画

BottomNavigationView 控件去除放大缩小动画

作者: 头发稀疏的程序猿 | 来源:发表于2019-12-03 18:35 被阅读0次

    BottomNavigationView 控件去除放大缩小动画

    最近项目中有用到底部导航栏,最初的底部导航栏是使用的是‘com.android.support:design’包android.support.design.widget.BottomNavigationView 进行设置;按钮点击后的放大效果可以反射 BottomNavigationMenuView 下的mShiftingMode 属性进行取消。即可做到取消放大缩小动画效果。代码如下:

     @SuppressLint("RestrictedApi")
     public static void disableShiftMode(BottomNavigationView navigationView) {
            BottomNavigationMenuView menuView = (BottomNavigationMenuView) navigationView.getChildAt(0);
            try {
                Field shiftingMode = menuView.getClass().getDeclaredField("mShiftingMode");
                shiftingMode.setAccessible(true);
                shiftingMode.setBoolean(menuView, false);
                shiftingMode.setAccessible(false);
                for (int i = 0; i < menuView.getChildCount(); i++) {
                    BottomNavigationItemView itemView = (BottomNavigationItemView) menuView.getChildAt(i);
                    itemView.setShiftingMode(false);
                    itemView.setChecked(itemView.getItemData().isChecked());
                }
            } catch (NoSuchFieldException | IllegalAccessException e) {
                e.printStackTrace();
            }
      }
    

    但是在新版本中,当你引入design 依赖。当在布局文件中写入Button 后不再有android.support.design.widget.BottomNavigationView的提示。


    image

    出现的是com.google.android.material.bottomnavigation.BottomNavigationView
    既然官方 推荐使用BottomNavigationView 那就抱着试试的心态去使用了 , 属性基本和design 包下的BottomNavigationView 使用一致。

    使用如下:

    <com.google.android.material.bottomnavigation.BottomNavigationView
                android:id="@+id/bottom_navigation_view"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_alignParentBottom="true"
                android:layout_gravity="bottom"
                app:itemBackground="@null"
                app:itemHorizontalTranslationEnabled="false"
                app:itemIconTint="@drawable/color_state_menu_navi"
                app:itemTextColor="@drawable/color_state_menu_navi"
                app:menu="@menu/bottom_navigation_main" />
    

    运行后的效果却还是带有放大动画效果,这不是产品想要的效果。度娘了一番后部分博客给出了添加属性的方法。

    app:labelVisibilityMode="labeled"
    
    运行后动画效果,却还是存在。 运行后效果.gif

    查看BottomNavigationView 源码:

    image.png
    package com.google.android.material.bottomnavigation;
    
    public class BottomNavigationView extends FrameLayout {
        private static final int MENU_PRESENTER_ID = 1;
        private final MenuBuilder menu;
        private final BottomNavigationMenuView menuView;
        private final BottomNavigationPresenter presenter;
        private MenuInflater menuInflater;
    

    在BottomNavigationView 类中搜索,没有找到mShiftingMode 延伸阅读 BottomNavigationMenuView 看看这个菜单类。
    希望在 BottomNavigationMenuView源码中或许有 mShiftingMode 属性的调用。
    直接搜索。。
    还是没有!
    无奈,就读下源码。
    在BottomNavigationMenuView 中发现以下代码。

    private final Pool<BottomNavigationItemView> itemPool;
    

    由对象池管理BottomNavigation 每一个Item 的View。
    点击 BottomNavigationItemView 查看源码。

    public BottomNavigationItemView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            this.itemPosition = -1;
            Resources res = this.getResources();
            LayoutInflater.from(context).inflate(layout.design_bottom_navigation_item, this, true);
            this.setBackgroundResource(drawable.design_bottom_navigation_item_background);
            this.defaultMargin = res.getDimensionPixelSize(dimen.design_bottom_navigation_margin);
            this.icon = (ImageView)this.findViewById(id.icon);
            this.smallLabel = (TextView)this.findViewById(id.smallLabel);
            this.largeLabel = (TextView)this.findViewById(id.largeLabel);
            ViewCompat.setImportantForAccessibility(this.smallLabel, 2);
            ViewCompat.setImportantForAccessibility(this.largeLabel, 2);
            this.setFocusable(true);
            this.calculateTextScaleFactors(this.smallLabel.getTextSize(), this.largeLabel.getTextSize());
        }
    

    在构造方法中有icon 和 文本标签,这就是每一个Item的实现类了。
    开始梳理调用逻辑。--------------->>>>>>>>>
    在构造方法中,有两个TextView smallLabel 和 largeLabel 的初始化。一个item 按钮有2个文本的显示,一个小标签,一个大标签,icon 忽略。并且调用了calculateTextScaleFactors 方法。

     private void calculateTextScaleFactors(float smallLabelSize, float largeLabelSize) {
            this.shiftAmount = smallLabelSize - largeLabelSize;
            this.scaleUpFactor = 1.0F * largeLabelSize / smallLabelSize;
            this.scaleDownFactor = 1.0F * smallLabelSize / largeLabelSize;
        }
    

    搜索 shiftAmount 、scaleUpFactor、scaleDownFactor 调用位置。

             case -1:
                if (this.isShifting) {
                    if (checked) {
                        this.setViewLayoutParams(this.icon, this.defaultMargin, 49);
                        this.setViewValues(this.largeLabel, 1.0F, 1.0F, 0);
                    } else {
                        this.setViewLayoutParams(this.icon, this.defaultMargin, 17);
                        this.setViewValues(this.largeLabel, 0.5F, 0.5F, 4);
                    }
    
                    this.smallLabel.setVisibility(4);
                } else if (checked) {
                    this.setViewLayoutParams(this.icon, (int)((float)this.defaultMargin + this.shiftAmount), 49);
                    this.setViewValues(this.largeLabel, 1.0F, 1.0F, 0);
                    this.setViewValues(this.smallLabel, this.scaleUpFactor, this.scaleUpFactor, 4);
                } else {
                    this.setViewLayoutParams(this.icon, this.defaultMargin, 49);
                    this.setViewValues(this.largeLabel, this.scaleDownFactor, this.scaleDownFactor, 4);
                    this.setViewValues(this.smallLabel, 1.0F, 1.0F, 0);
                }
                break;
    

    在上述代码 else if (checked) 及 else 处有对三个属性进行调用。

    private void setViewLayoutParams(@NonNull View view, int topMargin, int gravity) {
            LayoutParams viewParams = (LayoutParams)view.getLayoutParams();
            viewParams.topMargin = topMargin;
            viewParams.gravity = gravity;
            view.setLayoutParams(viewParams);
        }
    
      private void setViewValues(@NonNull View view, float scaleX, float scaleY, int visibility) {
            view.setScaleX(scaleX);
            view.setScaleY(scaleY);
            view.setVisibility(visibility);
        }
    

    通过上述的两个方法,可以得知
    shiftAmount 为icon设置上边距的偏移量
    scaleUpFactor 为 largeLabel 的缩放值,默认为1.0F
    scaleDownFactor 为smallLabel 的缩放值 ,默认为 1.0F

    找到了调用逻辑,接下来就对shiftAmount,scaleUpFactor,scaleDownFactor 属性进行反射处理,让点击和非点击状态的大小一致。
    设置 shiftAmount 的偏移量为0,使icon 不上下移动。
    设置 scaleUpFactor scaleDownFactor 为默认状态下的值 1。
    使BottomNavigationItemView 处于默认状态,不发生位置偏移。

    代码如下:

     @SuppressLint("RestrictedApi")
     public void closeAnimation(BottomNavigationView view) {
            BottomNavigationMenuView mMenuView = (BottomNavigationMenuView) view.getChildAt(0);
            for (int i = 0; i < mMenuView.getChildCount(); i++) {
                BottomNavigationItemView button = (BottomNavigationItemView) mMenuView.getChildAt(i);
                TextView mLargeLabel = getField(button.getClass(), button, "largeLabel");
                TextView mSmallLabel = getField(button.getClass(), button, "smallLabel");
                float mSmallLabelSize = mSmallLabel.getTextSize();
                setField(button.getClass(), button, "shiftAmount", 0F);
                setField(button.getClass(), button, "scaleUpFactor", 1F);
                setField(button.getClass(), button, "scaleDownFactor", 1F);
                mLargeLabel.setTextSize(TypedValue.COMPLEX_UNIT_PX, mSmallLabelSize);
            }
            mMenuView.updateMenuView();
        }
    
    
     private <T> T getField(Class targetClass, Object instance, String fieldName) {
            try {
                Field field = targetClass.getDeclaredField(fieldName);
                field.setAccessible(true);
                return (T) field.get(instance);
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
            return null;
        }
    
    
        private void setField(Class targetClass, Object instance, String fieldName, Object value) {
            try {
                Field field = targetClass.getDeclaredField(fieldName);
                field.setAccessible(true);
                field.set(instance, value);
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    

    运行后得到想要的效果。


    运行后效果.gif

    延伸阅读layout布局
    layout.design_bottom_navigation_item

    <?xml version="1.0" encoding="utf-8"?>
    <merge xmlns:android="http://schemas.android.com/apk/res/android">
      <ImageView
          android:id="@+id/icon"
          android:layout_width="24dp"
          android:layout_height="24dp"
          android:layout_marginTop="@dimen/design_bottom_navigation_margin"
          android:layout_marginBottom="@dimen/design_bottom_navigation_margin"
          android:layout_gravity="center_horizontal"
          android:contentDescription="@null"
          android:duplicateParentState="true"/>
      <com.google.android.material.internal.BaselineLayout
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:layout_gravity="bottom|center_horizontal"
          android:paddingBottom="10dp"
          android:clipToPadding="false"
          android:duplicateParentState="true">
        <TextView
            android:id="@+id/smallLabel"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:duplicateParentState="true"
            android:maxLines="1"
            android:textSize="@dimen/design_bottom_navigation_text_size"/>
        <TextView
            android:id="@+id/largeLabel"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:paddingLeft="8dp"
            android:paddingRight="8dp"
            android:duplicateParentState="true"
            android:maxLines="1"
            android:textSize="@dimen/design_bottom_navigation_active_text_size"
            android:visibility="invisible"/>
      </com.google.android.material.internal.BaselineLayout>
    </merge>
    
    
    <dimen name="design_bottom_navigation_text_size">12sp</dimen>
    <dimen name="design_bottom_navigation_active_text_size">14sp</dimen>
    

    如有理解有误的地方,希望指正。
    转载请注明来源,谢谢!

    相关文章

      网友评论

        本文标题:BottomNavigationView 控件去除放大缩小动画

        本文链接:https://www.haomeiwen.com/subject/falqbctx.html