Custom XML attributes without custom View in Fragm

2019-03-28 19:36发布

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 :

  1. 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;
    }
    
  2. 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 !

2条回答
beautiful°
2楼-- · 2019-03-28 20:15

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.

查看更多
够拽才男人
3楼-- · 2019-03-28 20:20

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 :

  • Your layout XML View must have and android:id

  • Create the class which will keep your custom attributes :

    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);
查看更多
登录 后发表回答