JAVA 反射笔记 Actionbar设置样式

来源:互联网 发布:sqlserver over函数 编辑:程序博客网 时间:2024/05/22 13:05

[JAVA 反射笔记 Actionbar设置样式]

当我们调用java命令运行某个java程序时, 将会启动一个java虚拟机进程, 不管该程序多复杂, 启动了多少个线程, 都处于该java虚拟机进程里.
同一个JVM的所有线程,所有变量都处于同一个进程里, 都使用该JVM进程的内存区, 当系统出现以下情况,JVM进程才会被终止
- 程序运行到正常结束
- 运行到使用System.exit()或Runtime.getRuntime().exit()
- 程序执行过程中遇到未捕获的异常或错误而结束
- 程序所在的平台强制结束JVM进程

类的加载

程序主动使用某个类时, 需要经过 加载,连接,初始化3个步骤

加载 指的是将类的class文件读入内存, 并创建一个 java.lang.Class对象,代表的是类本身
可以从不同的来源加载类的二进制数据
- 本地问价系统加载class文件
- jar包加载class
- 网络加载class
- java源文件动态编译并加载

类的连接

被加载后系统生成了一个对应的Class对象,连接阶段负责吧类的二进制数据合并到JRE中, 包括验证, 检验被加载的类是否有正确的内部结构并和其他类协调一致;准备 , 为类的静态成员(static 标示的)分配内存,并设置默认值;解析, 将类的二进制数据中的符号引用替换成直接引用

类的初始化

类的初始化,虚拟机负责对静态成员Field进行初始化 , JVM初始化一个类步骤
- 假如这个类还没有被加载和连接,则程序先加载并连接该类
- 假如该类的直接父类还没有被初始化,则先初始化其直接父类
- 假如类中有初始化语句,则系统依次执行这些初始化语句

类的初始化时机:
- 创建类的实例. 使用new 的方式 ; 使用反射的方式; 使用反序列化的方式
- 调用某个类的静态方法
- 访问某个类或接口的静态static成员变量Field,或为该静态成员变量赋值 (注: 如果是final static 就等同于宏变量, 编译时就会被替换成常量了,是不会导致初始化的, 除非这个宏变量的值时运行时才能被确定的)
- 使用反射类强制创建某个类或接口对应的java.lang.Class对象.如: Class.forName(“Person”); 如果系统还没有初始化Person类,则此时会初始化并返回Person类对应的java.lang.Class对象
- 初始化某个类的子类,该子类的所有父类都会被初始化
- 直接使用java.exe命令来运行某个主类,程序会先初始化该主类

系统为所有被载入内存中的类生成一个java.lang.Class实例, 一旦一个类被载入JVM, 同一个类就不会再次被载入
每个类被加载后系统就会为该类生成一个对应的Class对象, 通过该Class对象就可以访问到JVM中的这个类.
获取Class对象通常有如下3种方式
- 使用Class类的forName(String clazzName) , 静态方法, 参数是某个类的全限定类名,必须添加完整包名
- 调用某个类的class属性来获取该类对应的Class对象, 如:Person.class, 返回的就是Person类的Class对象
- 调用某个对象的getClass方法, 获得该对象所属的类的Class对象, 该方法是java.lang.Object类中的一个方法, 所以所有的java对象都可以调用该方法获得所属类对应的Class对象

大部分时候使用 类名.class 也就是第二种方式, 但有时, 只知道一个类的字符串则只能使用第一种方式了

获取类信息

一旦获得某个类的Class对象后, 就能调用该Class对象的方法, 来获得该类的对象和其他信息
Class类提供了大量的实例方法来获取该Class对象所对应类的详细信, 如:
file:///C:/WORKSPACE/adt-bundle-windows-x86/sdk/docs/reference/java/lang/Class.html

获取构造方法:

Constructor<> getConstructors() 返回Class对象对应的所有的public构造方法
Constructor<> getDeclaredConstructors() 返回所有的构造方法, 包括private

获取成员方法:

Method getMethod(String name, Class...<?> parameterTypes) Returns a Method object which represents the public method with the specified name and parameter types.

Method[] getMethods()
Returns an array containing Method objects for all public methods for the class C represented by this Class.

public Method getDeclaredMethod (String name, Class...<?> parameterTypes)
Returns a Method object which represents the method matching the specified name and parameter types that is declared by the class represented by this Class.

getDeclaredMethod , 返回该Class对应的类的指定的方法, 而且与方法的权限无关, 忽视private
还有其他 等等…

获取成员变量

Field getField(String name)
Returns a Field object which represents the public field with the given name.

Field[] getFields()
Returns an array containing Field objects for all public fields for the class C represented by this Class.

public Field getDeclaredField (String name)
Returns a Field object for the field with the given name which is declared in the class represented by this Class.

public Field[] getDeclaredFields ()
Returns an array containing Field objects for all fields declared in the class represented by this Class. If there are no fields or if this Class represents an array class, a primitive type or void then an empty array is returned.

使用反射生成并操作对象

有了Class对象就能获取Method 对象, Constructor对象, Field对象, 这3个类都位于java.lang.reflect包下, 并实现了java.lang.reflect.Member接口
程序可以通过Method对象来执行对应的方法, Constructor对象来调用对应的构造方法来创建对象, 通过Field对象来访问修改对象的属性值

创建对象

两种方式:
- 使用Class对象的newInstsance()方法来调用默认构造函数来创建该类的实例, 如

Class<?> clazz = Class.forName("完整类名"); //根据字符串获取对应的Class对象clazz.newInstance();            //使用clazz对应的默认构造函数创建实例
  • 先通过Class对象获得Constructor对象, 再调用Constructor对象的newInstance()来创建该Class的对象, 这种方式可以选择使用指定的构造函数, 如
Class<?> jframeClazz = Class.forName("javax.swing.JFrame");Constructor ctor = jframeClazz.getConstructor(String.class);Object obj = ctor.newInstance("测试窗口");

调用方法

每个Method对象对应一个成员方法, 获得Method对象后, 程序就可以通过invoke()来调用

Class<?> targetClass = target.getClass(); //从已经创建的对象获取Class对象Method mtd = targetClass.getMethod("setTitle",String.class);mtd.invoke(target,"hello world");

上面target是一个已经创建了的对象, 通过getClass获取其所属的Class对象, 然后获取setTitle方法, 最后调用

当通过Method的invoke方法调用对应的方法时, java会检查权限, 如果需要调用某个对象的private方法, 则要先调用Method对象父类的setAccessible方法
setAccessible(true); 则取消java语言的权限检查, 可以调用private的方法.

不仅Method对象可以使用setAccessible方法来取消权限检查, Constructor , Field都可以调用此方法来取消权限检查, 因为他们都继承AccessibleObject

Fieldextends AccessibleObjectimplements Memberjava.lang.Objectjava.lang.reflect.AccessibleObjectjava.lang.reflect.Field

访问属性值

Field 提供了如下两组方法来读取或设置Field值
- getXxx(Object obj) : 获取obj对象Field的属性值, Xxx表示8个基本类型, 如果该属性的类型是引用类型,则取消后面的Xxx

  • setXxx(Object obj, Xxx val) : 将obj对象的Field设置成val , Xxx表示8个基本类型 如果该属性的类型是引用类型,则取消后面的Xxx
Person p = new Person();Class<Person> personClazz = Person.class;Field nameField = personClazz.getDeclaredField("name"); //使用getDeclaredField 表示可以获取各种访问控制的fieldnameField.setAccessible(true);   //取消权限检查nameField.set(p,"new Name");   //String 不是基本数据类型, 因此调用set方法设置name FieldField ageField = personClazz.getDeclaredField("age");ageField.setAccessible(true);ageField.setInt(p,30);  //基本数据类型, 调用setXxx()

自定义actionBar

新建项目, 注意创建时,eclipse中可选主题, None, Holo Dark, Holo Light, Holo Light with Dark Action Bar , 只有最后的这个主题才默认显示ActionBar..

//menu.xml

<menu xmlns:android="http://schemas.android.com/apk/res/android" >    <item        android:id="@+id/action_search"        android:actionViewClass="android.widget.SearchView"        android:icon="@drawable/actionbar_search_icon"        android:showAsAction="ifRoom|collapseActionView"        android:title="@string/action_search"/>    <item        android:id="@+id/action_group_chat"        android:icon="@drawable/menu_group_chat_icon"        android:title="@string/menu_group_chat"/>    <item        android:id="@+id/action_add_friend"        android:icon="@drawable/menu_add_icon"        android:title="@string/menu_addfriend"/>    <item        android:id="@+id/action_scan"        android:icon="@drawable/men_scan_icon"        android:title="@string/menu_scan"/>    <item        android:id="@+id/action_feedback"        android:icon="@drawable/menu_feedback_icon"        android:title="@string/menu_feedback"/></menu>

自定义属性更改overflowButton默认图片,Manifest文件中指定android:theme=”@style/AppTheme” , 其指向是values/styles.xml , 然后按住ctrl 再跟踪AppBaseTheme , 发现是对应不同的版本使用的不同style文件, 当前版本19, 对应的是values-v14/styles.xml 这个文件中自定义属性更改图片

//values-v14/styles.xml

    <style name="AppBaseTheme" parent="android:Theme.Holo.Light.DarkActionBar">        <!-- API 14 theme customizations can go here. -->        <item name="android:actionOverflowButtonStyle">@style/weixinActionOverflowButtonStyle</item>    </style>    <style name="weixinActionOverflowButtonStyle">        <item name="android:src">@drawable/actionbar_add_icon</item>    </style>

//MainActivity.java

public class MainActivity extends Activity {    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        setOverflowButtonAlways(); //调用自定义方法显示OverflowButton        getActionBar().setDisplayShowHomeEnabled(false); // 设置不显示应用主页图标...    }    /***     * 使用反射来修改ViewConfiguration类对象,      * 把物理按键设置为false,     * 则actionBar就一定显示OverflowButton,      * 并且当点击属性按钮时, 弹出的菜单从OverflowButton处弹出     */    public void setOverflowButtonAlways() {        try {            ViewConfiguration config = ViewConfiguration.get(this);            Field menuKey = ViewConfiguration.class                    .getDeclaredField("sHasPermanentMenuKey");            menuKey.setAccessible(true);            menuKey.setBoolean(config, false);        } catch (Exception e) {            e.printStackTrace();        }    }    /***     * 子菜单的图标没有显示 使用反射调用menu对象的方法来开启图标显示     *  menu是个接口 , F4, Ctrl + t 查看实现接口的方法,     *  menu.getClass().getSimpleName() 就是我们需要的类名(MenuBuilder),     *  找到类后, 还是要仔细看源码才能找出方法     *  反射调用menu对象的 setOptionalIconsVisible(boolean );     */    @Override    public boolean onMenuOpened(int featureId, Menu menu) {        // 先判断如果当前打开的是actionBar的menu的话        if (featureId == Window.FEATURE_ACTION_BAR && menu != null) {            if (menu.getClass().getSimpleName().equals("MenuBuilder")) {//                try {                    Method m = menu.getClass().getDeclaredMethod(                            "setOptionalIconsVisible", Boolean.TYPE);                    m.setAccessible(true);                    m.invoke(menu, true);                } catch (Exception e) {                    e.printStackTrace();                }             }        }        return super.onMenuOpened(featureId, menu);    }    /**     * 显示菜单     */    @Override    public boolean onCreateOptionsMenu(Menu menu) {        getMenuInflater().inflate(R.menu.main, menu);        return true;    }}
0 0
原创粉丝点击