I'm trying to add custom XML attributes in existing View
Adding them to a custom View is not a big deal but I don't know how to access those attributes in a "basic" View (e.g. TextView, LinearLayout, ImageView ...)
And this is more difficult when there are Fragment and / or Library project involved
So far here is my code
Customs attributes definitions and XML (attrs.xml and the layout):
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="StarWars">
<attr name="jedi" format="string" />
<attr name="rank" format="string" />
</declare-styleable>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:sw="http://schemas.android.com/apk/res-auto"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView
android:id="@+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="28dp"
android:gravity="center_horizontal"
sw:jedi="Obiwan" />
<TextView
android:id="@+id/rank"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="28dp"
android:gravity="center_horizontal"
sw:rank="Master" />
Since I inflate this on an Fragment (so no onCreate with attrs arg !), the only way to try to get the 2 sw custom attributes is to :
Set a custom LayoutInflater.Factory to the Fragment LayoutInflater in onCreateView
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
LayoutInflater layoutInflater = inflater.cloneInContext(inflater.getContext());
layoutInflater.setFactory(new StarWarsLayoutFactory());
View fragmentView = layoutInflater.inflate(R.layout.jedi, container, false);
((TextView) fragmentView.findViewById(android.R.id.jedi)).setText("Yep");
return fragmentView;
}
Trying to get the custom attributes on the custom LayoutInflater.Factory :
public class StarWarsLayoutFactory implements Factory {
@Override
public View onCreateView(String name, Context context, AttributeSet attrs) {
*** What to do here ? ***
return null;
}
}
Anyone have had this kind of question ?
What I'm missing here ?
Thx in advance !
I have finally done this :)
You have to create a new LayoutInflater.Factory
as I did on the OP but since the Factory is used for all the inflated layout View, and you have to return null on your Factory.onCreateView
(to let Android handle the inflation) you must cache your custom XML attibutes somewhere
So here the solution :
public class AttributeParser {
private AttributeParserFactory mFactory;
private Map<Integer, HashMap<Integer, String>> mAttributeList;
private class AttributeParserFactory implements LayoutInflater.Factory{
@Override
public View onCreateView(String name, Context context, AttributeSet attrs) {
String id = attrs.getAttributeValue("http://schemas.android.com/apk/res/android", "id");
if(id != null){
// String with the reference character "@", so we strip it to keep only the reference
id = id.replace("@", "");
TypedArray libraryStyledAttributeList = context.obtainStyledAttributes(attrs, R.styleable.NewsHubLibrary);
HashMap<Integer, String> libraryViewAttribute = new HashMap<Integer, String>();
int i = 0;
for(int attribute : R.styleable.NewsHubLibrary){
String attributeValue = libraryStyledAttributeList.getString(i);
if(attributeValue != null)
libraryViewAttribute.put(attribute, attributeValue);
i++;
}
if(!libraryViewAttribute.isEmpty())
mAttributeList.put(Integer.valueOf(id), libraryViewAttribute);
libraryStyledAttributeList.recycle();
}
return null;
}
}
public AttributeParser(){
mAttributeList = new HashMap<Integer, HashMap<Integer, String>>();
mFactory = new AttributeParserFactory();
}
public void clear() {
mAttributeList.clear();
}
public LayoutInflater getLayoutInflater(LayoutInflater inflater) {
clear();
LayoutInflater layoutInflater = inflater.cloneInContext(inflater.getContext());
layoutInflater.setFactory(mFactory);
return layoutInflater;
}
public void setFactory(LayoutInflater inflater){
inflater.cloneInContext(inflater.getContext()).setFactory(mFactory);
}
public void setViewAttribute(Activity activity) {
for(Entry<Integer, HashMap<Integer, String>> attribute : mAttributeList.entrySet())
if(activity.findViewById((Integer) attribute.getKey()) != null)
activity.findViewById((Integer) attribute.getKey()).setTag(attribute.getValue());
}
public void setViewAttribute(View view) {
for(Entry<Integer, HashMap<Integer, String>> attribute : mAttributeList.entrySet())
if(view.findViewById((Integer) attribute.getKey()) != null)
view.findViewById((Integer) attribute.getKey()).setTag(attribute.getValue());
}
public Map<Integer, HashMap<Integer, String>> getAttributeList() {
return mAttributeList;
}
public void setAttributeList(Map<Integer, HashMap<Integer, String>> attributeList) {
this.mAttributeList = attributeList;
}
}
- Your custom attributes will be stored in each View tag when you use the AttributeParser :
LayoutInflater layoutInflater = mAttributeParser.getLayoutInflater(inflater);
View view = layoutInflater.inflate(R.layout.jedi, null);
mAttributeParser.setViewAttribute(view);
Standard TextView
and such will not access these attributes. You may extend it though and provide functionality that includes reading those custom attributes.
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.StarWars, 0, 0);
String jedi = a.getText(R.styleable.StarWars_jedi);
String rank = a.getText(R.styleable.StarWars_rank);
a.recycle();
Remember to always call recycle()
at the end.