在Android中编辑AttributeSet吗?

时间:2019-05-08 17:11:39

标签: java android

定义自定义视图时,是否可以从AttributeSet中添加或删除键?

    public BFPlayer(Context context, AttributeSet attrs, int defStyle) {
        super(context, camelToSnake(attrs), defStyle);
        init(context, attrs, defStyle);
    }

    private static AttributeSet camelToSnake(AttributeSet attrs) {
        int num = attrs.getAttributeCount();
        for (int i = 0; i < num; i++)
        {
            String name = attrs.getAttributeName(i);
            String newName = CaseFormat.UPPER_CAMEL.to(CaseFormat.UPPER_SNAKE_CASE, name);

            // I'm looking for the two methods below:
            attrs.addAttribute(newName, attrs.getAttributeValue(i));
            attrs.removeAttribute(name);

        }
        return attrs;
    }

更新:我的尝试

我尝试创建以下类:

BFAttributes.java

public class BFAttributeSet implements AttributeSet {
    private final AttributeSet attrs;
    private static final List<String> playerViewAttributes = new ArrayList<>(Arrays.asList(
            "useArtwork",
            "defaultArtwork",
            "useController",
            "hideOnTouch",
            "autoShow",
            "hideDuringAds",
            "showBuffering",
            "resizeMode",
            "surfaceType"
    ));

    public BFAttributeSet(AttributeSet attrs)
    {
        this.attrs = attrs;
    }

    @Override
    public int getAttributeCount() {
        return attrs.getAttributeCount();
    }

    @Override
    public String getAttributeName(int index) {
        String name = attrs.getAttributeName(index);
        if (playerViewAttributes.contains(name))
        {
            name = CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, name);
        }
        return name;
    }

    @Override
    public String getAttributeValue(int index) {
        return attrs.getAttributeValue(index);
    }

    @Override
    public String getAttributeValue(String namespace, String name) {
        return attrs.getAttributeValue(namespace, name);
    }

    @Override
    public String getPositionDescription() {
        return attrs.getPositionDescription();
    }

    @Override
    public int getAttributeNameResource(int index) {
        return attrs.getAttributeNameResource(index);
    }

    @Override
    public int getAttributeListValue(String namespace, String attribute, String[] options, int defaultValue) {
        return attrs.getAttributeListValue(namespace, attribute, options, defaultValue);
    }

    @Override
    public boolean getAttributeBooleanValue(String namespace, String attribute, boolean defaultValue) {
        return attrs.getAttributeBooleanValue(namespace, attribute, defaultValue);
    }

    @Override
    public int getAttributeResourceValue(String namespace, String attribute, int defaultValue) {
        return attrs.getAttributeResourceValue(namespace, attribute, defaultValue);
    }

    @Override
    public int getAttributeIntValue(String namespace, String attribute, int defaultValue) {
        return attrs.getAttributeIntValue(namespace, attribute, defaultValue);
    }

    @Override
    public int getAttributeUnsignedIntValue(String namespace, String attribute, int defaultValue) {
        return attrs.getAttributeUnsignedIntValue(namespace, attribute, defaultValue);
    }

    @Override
    public float getAttributeFloatValue(String namespace, String attribute, float defaultValue) {
        return attrs.getAttributeFloatValue(namespace, attribute, defaultValue);
    }

    @Override
    public int getAttributeListValue(int index, String[] options, int defaultValue) {
        return attrs.getAttributeListValue(index, options, defaultValue);
    }

    @Override
    public boolean getAttributeBooleanValue(int index, boolean defaultValue) {
        return attrs.getAttributeBooleanValue(index, defaultValue);
    }

    @Override
    public int getAttributeResourceValue(int index, int defaultValue) {
        return attrs.getAttributeResourceValue(index, defaultValue);
    }

    @Override
    public int getAttributeIntValue(int index, int defaultValue) {
        return attrs.getAttributeIntValue(index, defaultValue);
    }

    @Override
    public int getAttributeUnsignedIntValue(int index, int defaultValue) {
        return attrs.getAttributeUnsignedIntValue(index, defaultValue);
    }

    @Override
    public float getAttributeFloatValue(int index, float defaultValue) {
        return attrs.getAttributeFloatValue(index, defaultValue);
    }

    @Override
    public String getIdAttribute() {
        return attrs.getIdAttribute();
    }

    @Override
    public String getClassAttribute() {
        return attrs.getClassAttribute();
    }

    @Override
    public int getIdAttributeResourceValue(int defaultValue) {
        return attrs.getIdAttributeResourceValue(defaultValue);
    }

    @Override
    public int getStyleAttribute() {
        return attrs.getStyleAttribute();
    }
}

然后在我的自定义视图中,执行以下操作:

    public BFPlayer(Context context, AttributeSet attrs, int defStyle) {
        super(context, new BFAttributeSet(attrs), defStyle);
        init(context, attrs, defStyle);
    }

启动我的应用时,这引发了以下错误:

2019-05-08 17:54:54.641 24456-24456/? E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.blueframetech.blueframesdk, PID: 24456
    java.lang.RuntimeException: Unable to start activity ComponentInfo{com.blueframetech.blueframesdk/com.blueframetech.blueframesdk.MainActivity}: android.view.InflateException: Binary XML file line #9: Binary XML file line #9: Error inflating class com.blueframetech.bfplayer.BFPlayer
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2913)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3048)
        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1808)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:193)
        at android.app.ActivityThread.main(ActivityThread.java:6669)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
     Caused by: android.view.InflateException: Binary XML file line #9: Binary XML file line #9: Error inflating class com.blueframetech.bfplayer.BFPlayer
     Caused by: android.view.InflateException: Binary XML file line #9: Error inflating class com.blueframetech.bfplayer.BFPlayer
     Caused by: java.lang.reflect.InvocationTargetException
        at java.lang.reflect.Constructor.newInstance0(Native Method)
        at java.lang.reflect.Constructor.newInstance(Constructor.java:343)
        at android.view.LayoutInflater.createView(LayoutInflater.java:647)
        at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:790)
        at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:730)
        at android.view.LayoutInflater.rInflate(LayoutInflater.java:863)
        at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:824)
        at android.view.LayoutInflater.inflate(LayoutInflater.java:515)
        at android.view.LayoutInflater.inflate(LayoutInflater.java:423)
        at android.view.LayoutInflater.inflate(LayoutInflater.java:374)
        at android.support.v7.app.AppCompatDelegateImpl.setContentView(AppCompatDelegateImpl.java:469)
        at android.support.v7.app.AppCompatActivity.setContentView(AppCompatActivity.java:140)
        at com.blueframetech.blueframesdk.MainActivity.onCreate(MainActivity.java:14)
        at android.app.Activity.performCreate(Activity.java:7136)
        at android.app.Activity.performCreate(Activity.java:7127)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1271)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2893)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3048)
        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1808)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:193)
        at android.app.ActivityThread.main(ActivityThread.java:6669)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
     Caused by: java.lang.ClassCastException: com.blueframetech.bfplayer.facades.BFAttributeSet cannot be cast to android.content.res.XmlBlock$Parser
        at android.content.res.ResourcesImpl$ThemeImpl.obtainStyledAttributes(ResourcesImpl.java:1336)
        at android.content.res.Resources$Theme.obtainStyledAttributes(Resources.java:1537)
        at android.content.Context.obtainStyledAttributes(Context.java:712)
2019-05-08 17:54:54.642 24456-24456/? E/AndroidRuntime:     at android.view.View.<init>(View.java:4950)
        at android.view.ViewGroup.<init>(ViewGroup.java:659)
        at android.widget.FrameLayout.<init>(FrameLayout.java:92)
        at android.widget.FrameLayout.<init>(FrameLayout.java:87)
        at com.google.android.exoplayer2.ui.PlayerView.<init>(PlayerView.java:308)
        at com.google.android.exoplayer2.ui.PlayerView.<init>(PlayerView.java:304)
        at com.blueframetech.bfplayer.BFPlayer.<init>(BFPlayer.java:38)
            ... 28 more

根本错误是这个:

java.lang.ClassCastException: com.blueframetech.bfplayer.facades.BFAttributeSet cannot be cast to android.content.res.XmlBlock$Parser

1 个答案:

答案 0 :(得分:0)

经过大量实验,阅读了Android源代码,然后使用Google谷歌搜索,当我发现this post to a similar question

时,我的怀疑得到了证实。

即使 看起来 ,就像Android正在解析XML文件以构建应用程序的布局一样,该XML实际上仍作为 构建时间进行处理 并转换为已加载的优化类。即时修改属性(例如通过立面)将避免这种构建视图时编译带来的性能提升。因此无法完成。无论XML在编译时是什么,这就是您的类在运行时将在AttributeSet中看到的内容。

具体来说,XML文件被编译为二进制表示形式,并由XmlBlock进行解析-android.content.res的私有类不会公开用于子类化。

通过使用反射,可以 子类化XmlBlock(因为反射绕过了很多检查和制衡,允许您访问私有成员和方法)-但是我对使用Reflection不太了解,无论如何我都认为这是一种反模式。

仍然,如果有人知道使用Reflection子类化包私有类的方法并可以发布方法,我很乐意接受它作为答案(因为这是实现我想要的唯一方法)