定义自定义视图时,是否可以从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
答案 0 :(得分:0)
经过大量实验,阅读了Android源代码,然后使用Google谷歌搜索,当我发现this post to a similar question
时,我的怀疑得到了证实。即使 看起来 ,就像Android正在解析XML文件以构建应用程序的布局一样,该XML实际上仍作为 构建时间进行处理 并转换为已加载的优化类。即时修改属性(例如通过立面)将避免这种构建视图时编译带来的性能提升。因此无法完成。无论XML在编译时是什么,这就是您的类在运行时将在AttributeSet中看到的内容。
具体来说,XML文件被编译为二进制表示形式,并由XmlBlock
进行解析-android.content.res
的私有类不会公开用于子类化。
通过使用反射,可以 子类化XmlBlock
(因为反射绕过了很多检查和制衡,允许您访问私有成员和方法)-但是我对使用Reflection不太了解,无论如何我都认为这是一种反模式。
仍然,如果有人知道使用Reflection子类化包私有类的方法并可以发布方法,我很乐意接受它作为答案(因为这是实现我想要的唯一方法)