Explain in detail the four whys that reference the custom internal class view in Android XML

Today, I encountered some pitfalls in applying the custom view defined in the form of internal class in XML. Although I solved this problem by reading some articles written by predecessors, I didn't fully explain why, so I decided to make this summary.

Rules for using custom internal class view

This article is mainly a summary of why, so first put forward the practice of referencing the custom view of the internal class in the XML layout file. There are four points:

1. The custom class must be static;

2. Use view as the tag in the XML file. Note that V is a lowercase letter, lowercase letter V, lowercase letter V;

3. Add the class attribute. Note that if there is no android: namespace, it indicates the complete path of the custom view, and the external class and the internal class are connected with a dollar "$" instead of ".". Note that the dollar "$" should be used instead of ".";

4. The customized view should at least contain constructors with context and attributeset parameters

Main code of layout loading process

Firstly, the loading of XML layout files is realized by using layoutinflator. Through the analysis of this article, we know that the layoutinflator class actually used is its subclass phonelayoutinflator. Then, through the analysis of this article, we know that the key entry function for the real instantiation of view is the function createviewfromtag, Then instantiate the view through createview. The following is the main code of the key functions used in the process:

Main codes used in phonelayouteinflator:

WHY

For any element in XML, start with "1" (1 means the position marked with "key 1" in the code, the same later). Follow the code flow:

1. Judge whether the name is "view" at "1", then there will be two cases. Let's assume that "view" is used first

2. Because the condition is met in "1", name is assigned the value of class attribute, such as "com.willhua.view.myview" or "com.willhua.view.myclass $MyView". In fact, this is also the reason why "view" should be defined instead of other names.

3. According to the value of name, name contains a '.' symbol, so the code goes to "3" and calls createview, and the prefix parameter is empty

4. At "4", prefix is empty, so the parameter of loadclass function is name, that is, the value of class attribute in A.2. We know that the parameter passed to loadclass is the class name of the class to be loaded. In Java, the class name of the internal class is represented by connecting the internal class name with the symbol "$" after the external class name. Therefore, the answer to point 3 mentioned at the beginning of the article is here. In addition, why come to "4" instead of the corresponding else block? When the class is first loaded, the constructor must not exist, that is, the if condition must be true. Then, when it is instantiated again later, it comes to the else block. In the else block, it only makes some judgments on whether the view can be loaded according to the Mfilter, which does not essentially affect the loading process of the view.

5. There is another important place at "4", that is, the sentence constructor = class. Getconstructor (mconstructorsignature). First, the definition of mconstructorsignature has been given at the beginning of the code:

Find the description of the getconstructor function on the Oracle documentation:

Returns a Constructor object that reflects the specified public constructor of the class represented by this Class object. The parameterTypes parameter is an array of Class objects that identify the constructor's formal parameter types,in declared order. If this Class object represents an inner class declared in a non-static context, the formal parameter types include the explicit enclosing instance as the first parameter.

The constructor to reflect is the public constructor of the class represented by this Class object whose formal parameter types match those specified by parameterTypes.

Therefore, here we actually answer two questions: (1) the constructor returned by getconstructor is the one whose parameters exactly match the parameters passed by getconstructor. If not, nosuchmethodexception will be thrown. Therefore, we know that there must be a constructor that exactly matches the mconstructorsignature, which requires two parameters: context and attributeset; (2) If the class represents a non static internal class, an external class instance should be passed in as the first parameter. The mconstructorsignature passed in our code does not contain external class instances. Therefore, we must declare our customized internal class view as static so that no error will be reported. Some students only explained that the non static internal class needs to be referenced by an external class instance, but I think what if the system will automatically construct an external class instance when loading? So here is the negative answer.

6. After getting the class information clazz, the code comes to "5". Here, pay attention to mconstructorargs, which is defined as object [2] at the front of the posted code. It is found in the source code that mconstructorargs [0] is assigned as the context passed in when creating layoutinterpreter. So we know that in "5", the parameters passed to newinstance are context and attributeset. Then there is also a sentence in the description of newinstance in the Oracle document: if the constructor's declaring class is an inner class in a non static context, the first argument to the constructor needs to be the enclosing instance; There is no external class instance passed in here, that is, an error will also be reported for static internal classes. This also verifies that the customized internal class view must be declared as a static class.

So far, assuming that "view" is used and lowercase "V" is emphasized again as the element name, we have introduced three conditions that must be met to apply the custom internal class view in XML, that is, using "$" in the class attribute to connect external and internal classes, there must be a constructor that accepts context and attributeset parameters, These three requirements must be declared static.

If the custom internal class view is not used in the form of "view", we find that when writing XML, we can only use the form of < com. Willhua. MyClass. MyView / > instead of < com. Willhua. MyClass $MyView / >. In this way, androidstudio will report the error of "tag start is not close". Obviously, if we use the form of < com. Willhua. MyClass. MyView / >, loadclass ("com. Willhua. MyClass. MyView") will be called at "key 4". As analyzed earlier, it does not comply with the complete naming rules of internal classes in Java and will report an error. Some people may carelessly write it in the form of capital V, that is, the form of < view class = "com. Willhua. MyClass $MyView"... / >. In this way, "wrong type" will be reported in the runtime, because this essentially defines an android.view.view, but you think it is defined com.willhua.myclass $MyView in the code.

At "2" of the ID, the calling process is oncreateview (parent, attrs) - > oncreateview (name, attrs) - > createview (name, attrs). As mentioned earlier, the layouteinflator we really use is phonelayouteinflator, and the oncreateview (name, attrs) function is rewritten in phonelayouteinflator, In the oncreateview function of phonelayouteinflator, i.e. "6", the function calls createview by trying to use three prefixes in front of Name: "Android. Widget." and "Android. App." respectively. If none is found, it calls oncreateview of the parent class to try to add the prefix "Android. View." to load the view. Therefore, this is why we can directly use < button / >, < view / >, for example, because these commonly used package names are included in these four package names.

summary

So far, we have found the reasons for the four requirements mentioned at the beginning. In fact, after the whole process, we found that there are three ways to correctly load the defined elements into the relevant classes:

1. Only use the simple class name, i.e. < VIEWNAME / >. Classes in the "Android. Widget", "Android. WebKit", "Android. App" and "Android. View" packages can be directly used in this form, because the corresponding package name will be automatically added to the code to form a complete path, but not for other classes, because the complete class name composed of VIEWNAME and these four package names cannot be found.

2. Use the form of < "full class name" / > such as < com. Willhua. MyView / > or < Android. Widget. Button / > for any non internal class view. But not for internal classes, because such class names do not conform to the definition rules of complete class names of internal classes in Java. If the form of < com. Willhua. MyClass $MyView / > can be compiled, it must be able to correctly inflate the MyView. Unfortunately, an error will be prompted when writing.

3. Use the form of < view class = "full class name" /. This is the most common. All classes can do this, but the premise is that the "complete class name" is written correctly. For example, the internal classes mentioned above are connected with the "$" symbol.

In order to find out the problem of loading the internal class view, I have a certain understanding of the whole loading process. I feel I can say more why, and I still gain a lot. The above is the whole content of this article. I hope it will be helpful to your study, and I hope you can support programming tips.

The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>