Android反射的简单应用之BottomNavigationView
反射
反射(Reflection)在java和android开发过程中都非常有用,但是在android中的使用往往会影响app的性能,因此使用反射时要在适当的情况下使用。
什么时候使用呢?
反射耗时的多少与被反射类的大小有关系,它本质上是对类的成员列表进行遍历。如果这个类的成员越多,遍历的时间越长,整个反射的时间也就越多,如果类的成员较少,影响也是较小的。例如在以Activity作为测试对象,Activity是个大类,因此反射耗时比较正常。反射是把双刃剑,在适当的情况下使用非常利于程序的架构,如果使用不当会造成性能损耗。
如何使用呢?
我们不妨先看看这篇文章—公共技术点之 Java 反射 Reflection,相信你可以使用里面的方法来实现自己所需的功能。
这里就让我们来总结下反射的一些方法:
- 获取class对象:
正常来获取class时我们都是直接用 object.class 或者 通过该类的对象(Object o = new Object(),)来获取class( o.getclass()),下面则是通过class所在的包名来获取对象。
// 加载指定的 Class 对象,参数为要加载的类的完整路径public static Class<?> forName (String className)// 加载指定的 Class 对象,参数 1 为要加载的类的完整路径,例如"com.loften.sample.test";// 参数 2 为是否要初始化该 Class 对象,参数 3 为指定加载该类的 ClassLoader.public static Class<?> forName (String className, boolean shouldInitialize, ClassLoader classLoader)//在调用 Class.forName()方法时,没有在编译路径下(classpath)找到对应的类,那么将会抛出 ClassNotFoundException
- 获取构造函数:
// 获取一个公有的构造函数,参数为可变参数,如果构造函数有参数,那么需要将参数的类型传递给 getConstructor 方法public Constructor<T> getConstructor (Class...<?> parameterTypes)// 获取目标类所有的公有构造函数public Constructor[]<?> getConstructors ()
注意,当你通过反射获取到 Constructor、Method、Field 后,在反射调用之前将此对象的 accessible 标志设置为 true,以此来提升反射速度。值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。值为 false 则指示反射的对象应该实施 Java 语言访问检查。
Constructor<?> constructor = clz.getConstructor(String.class);// 设置 Constructor 的 Accessibleconstructor.setAccessible(true);// 设置 Methohd 的 AccessibleMethod learnMethod = Student.class.getMethod("learn", String.class);learnMethod.setAccessible(true);
- 获取类中函数
// 获取 Class 对象中指定函数名和参数的函数,参数一为函数名,参数 2 为参数类型列表public Method getDeclaredMethod (String name, Class...<?> parameterTypes)// 获取该 Class 对象中的所有函数( 不包含从父类继承的函数 )public Method[] getDeclaredMethods ()// 获取指定的 Class 对象中的**公有**函数,参数一为函数名,参数 2 为参数类型列表public Method getMethod (String name, Class...<?> parameterTypes)// 获取该 Class 对象中的所有**公有**函数 ( 包含从父类和接口类集成下来的函数 )public Method[] getMethods ()//这里需要注意的是 getDeclaredMethod 和 getDeclaredMethods 包含 private、protected、default、public 的函数,并且通过这两个函数获取到的只是在自身中定义的函数,从父类中集成的函数不能够获取到。而 getMethod 和 getMethods 只包含 public 函数,父类中的公有函数也能够获取到。
- 获取类中属性:
// 获取 Class 对象中指定属性名的属性,参数一为属性名public Method getDeclaredField (String name)// 获取该 Class 对象中的所有属性( 不包含从父类继承的属性 )public Method[] getDeclaredFields ()// 获取指定的 Class 对象中的**公有**属性,参数一为属性名public Method getField (String name)// 获取该 Class 对象中的所有**公有**属性 ( 包含从父类和接口类集成下来的公有属性 )public Method[] getFields ()//这里需要注意的是 getDeclaredField 和 getDeclaredFields 包含 private、protected、default、public 的属性,并且通过这两个函数获取到的只是在自身中定义的属性,从父类中集成的属性不能够获取到。而 getField 和 getFields 只包含 public 属性,父类中的公有属性也能够获取到。
- 获取父类和接口:
/*** 获取父类:只需 getSuperclass(),后续获取属性方法同上*/private static void getParentFields(){ TestImp testImp = new TestImp("anni", 20, "45kg", "女", 180); Class<?> superClass = testImp.getClass().getSuperclass(); while (superClass != null) { System.out.println("TestImp's super class is : " + superClass.getName()); // 再获取父类的上一层父类,直到最后的 Object 类,Object 的父类为 null superClass = superClass.getSuperclass(); }}/*** 获取接口*/private static void showInterfaces() { TestImp testImp = new TestImp("anni", 20, "45kg", "女", 180); Class<?>[] interfaceses = testImp.getClass().getInterfaces(); for (Class<?> class1 : interfaceses) { System.out.println("TestImp's interface is : " + class1.getName()); }}
- 获取注解:
// 获取指定类型的注解public A getAnnotation(Class annotationClass) ;// 获取 Class 对象中的所有注解public Annotation[] getAnnotations() ;
实际应用:
Material Design中的 BottomNavigationView 给出的样式跟我们正常使用的demo不一样,主要是我们的产品还未采用这样样式,不废话,先给图:
上述第一幅是当选项大于3个时的样式(选项等于3个时则与第二幅图一直),往往与我们的设计图不一致,官方也暂未提供接口让我们去调用修改,因此我们可以通过反射,也就可以实现第二幅图的效果,自定义的BottomNavigationView代码如下:
public class DivBottomNavigationView extends BottomNavigationView { public DivBottomNavigationView(Context context) { super(context); } public DivBottomNavigationView(Context context, AttributeSet attrs) { super(context, attrs); } public DivBottomNavigationView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } public void SetNormalBottomNavigation() { BottomNavigationMenuView mMenuView = getField(getClass().getSuperclass(), this, "mMenuView", BottomNavigationMenuView.class); BottomNavigationItemView[] mButtons = getField(mMenuView.getClass(), mMenuView, "mButtons", BottomNavigationItemView[].class); for (BottomNavigationItemView button : mButtons) { TextView mLargeLabel = getField(button.getClass(), button, "mLargeLabel", TextView.class); TextView mSmallLabel = getField(button.getClass(), button, "mSmallLabel", TextView.class); setField(button.getClass(), button, "mShiftAmount", 0); setField(button.getClass(), button, "mScaleUpFactor", 1); setField(button.getClass(), button, "mScaleDownFactor", 1); mLargeLabel.setTextSize(TypedValue.COMPLEX_UNIT_PX, mLargeLabel.getTextSize()); setField(button.getClass(), button, "mShiftingMode", false); } setField(mMenuView.getClass(), mMenuView, "mShiftingMode", false); mMenuView.updateMenuView(); } public int getCurrentItem() { BottomNavigationMenuView mMenuView = getField(getClass().getSuperclass(), this, "mMenuView", BottomNavigationMenuView.class); BottomNavigationItemView[] mButtons = getField(mMenuView.getClass(), mMenuView, "mButtons", BottomNavigationItemView[].class); Menu menu = getMenu(); for (int i = 0; i < mButtons.length; i++) { if (menu.getItem(i).isChecked()) { return i; } } return 0; } public void setCurrentItem(int item) { if (item < 0 || item >= getMaxItemCount()) { throw new ArrayIndexOutOfBoundsException("item is out of bounds, we expected 0 - " + (getMaxItemCount() - 1) + ". Actually " + item); } BottomNavigationMenuView mMenuView = getField(getClass().getSuperclass(), this, "mMenuView", BottomNavigationMenuView.class); BottomNavigationItemView[] mButtons = getField(mMenuView.getClass(), mMenuView, "mButtons", BottomNavigationItemView[].class); View.OnClickListener mOnClickListener = getField(mMenuView.getClass(), mMenuView, "mOnClickListener", View.OnClickListener.class); mOnClickListener.onClick(mButtons[item]); } /** * get private filed in this specific object * * @param targetClass * @param instance the filed owner * @param fieldName * @param fieldType the field type * @param * @return field if success, null otherwise. */ private T getField(Class targetClass, Object instance, String fieldName, Class fieldType) { 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; } /** * change the field value * * @param targetClass * @param instance the filed owner * @param fieldName * @param value */ 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(); } }}
这里附上我的源码地址:https://github.com/myloften/BottomNavigationViewSample
如果想要有更多自定义的BottomNavigationView的用法,可以参考下面链接:
https://github.com/ittianyu/BottomNavigationViewEx
更多相关文章
- focusable ,focusableInTouchMode,控件焦点属性;Android 如何让Edit
- android中反射的应用
- android android:exported属性的使用
- [置顶] 关于android:lineSpacingExtra属性 在android5.0与之前版
- Android -----listView的属性大全
- android 测试读取LEB数据的函数
- Android 控件TextView的属性
- android开发:View中android:fadingEdge属性的作用