Before doing a project (like wallpaper), mainly to show the past each issue of wallpaper theme and the corresponding wallpaper, and planning requirements, it is best to dynamically change the theme presentation method, so that the user experience will be better. Well, well, the plan, we can not refute, after all, it is really good. So began to study this aspect of things.
First of all, I think of the photo wall effect, change the picture can have a different way of presentation. In this case, however, the text and the deeper custom effects cannot be achieved. Then, thinking about the next, decided to emulate the native layout of the Android file parsing method, to dynamically parse the layout.
First look at the Android native layout file parsing process:
First step: Call Layoutinflater's inflate function to parse the XML file to get a view, and then take a look at the inflate function:
//使用常见的API方法去解析xml布局文件, LayoutInflater layoutInflater = (LayoutInflater)getSystemService(); null,false
Second step: In the inflate function, get a xmlresourceparser to parse the XML layout file, and then down with inflate (parser, Root, attachtoroot):
publicinflate(int resource, ViewGroup root, boolean attachToRoot) { if (DEBUG) System.out.println("INFLATING from resource: " + resource); XmlResourceParser parser = getContext().getResources().getLayout(resource); try { return inflate(parser, root, attachToRoot); finally { parser.close(); } }
Step three: The inflate function creates a root view based on the node name of the layout, and then determines whether it is empty based on the root parameter passed in the method, and if not null, gives the root view the layout parameters of the outside parent view. The Rinflate function is then called to add all the byte points to the root view.
PublicViewInflate(Xmlpullparser parser, ViewGroup root, Boolean attachtoroot) {synchronized (Mconstructorargs) {final AttributeSet attrs = xml.asattributeset (parser); Context Lastcontext = (context) mconstructorargs[0]; mconstructorargs[0] = Mcontext;//The Mconstructorargs property is passed as a parameter to the view's constructor at the endView result = root;Try{//Look for the root node. intType while((type = Parser.next ())! = Xmlpullparser.start_tag && Type! = xmlpullparser.end_document) {//Empty}if(Type! = Xmlpullparser.start_tag) {Throw NewInflateexception (parser.getpositiondescription () +": No start tag found!"); } final String name = Parser.getname ();//node name, which is a control in the API or a custom view full qualified name if(tag_merge.equals (name)) {if(Root = =NULL|| !attachtoroot) {Throw NewInflateexception ("<merge/> can be used only with a valid"+"ViewGroup Root and Attachtoroot=true"); } rinflate (parser, root, Attrs,false); }Else{//Temp is the root view and was found in the XMLView temp;if(tag_1995.equals (name)) {temp =NewBlinklayout (Mcontext, attrs); }Else{temp = Createviewfromtag (root, name, attrs); } viewgroup.layoutparamsparams=NULL;if(Root! =NULL) {//Create layout params that match root, if supplied params= Root.generatelayoutparams (attrs);if(!attachtoroot) {//Set the layout params for temp If we is not //attaching. (If We is, we use AddView, below)Temp.setlayoutparams (params); } }//Inflate all children under tempRinflate (parser, temp, attrs,true);//We is supposed to attach all the views We found (int temp) //to root. do it now. if(Root! =NULL&& attachtoroot) {Root.addview (temp,params); }//Decide whether to return the root is passed in or the //Top view found in XML. if(Root = =NULL|| !attachtoroot) {result = temp; } } }Catch(Xmlpullparserexception e) {//...}finally{//Don ' t retain static reference on context.mconstructorargs[0] = Lastcontext; mconstructorargs[1] =NULL; }returnResult }}
Fourth step: The Rinflate method is primarily to recursively invoke the child nodes of the layout file root view. Adds the parsed view to the Parentview.
/** * Recursive method used to descend down the XML hierarchy and instantiate * views, instantiate their child Ren, and then call Onfinishinflate (). */ voidRinflate (Xmlpullparser parser, View parent,FinalAttributeSet Attrs,BooleanFinishinflate)throwsXmlpullparserexception, IOException {Final intdepth = Parser.getdepth ();intType while((type = Parser.next ())! = Xmlpullparser.end_tag | | Parser.getdepth () > Depth) && type! = xmlpullparser.end_document) {if(Type! = Xmlpullparser.start_tag) {Continue; }FinalString name = Parser.getname ();if(tag_request_focus.equals (name)) {//Handling <requestfocus/> LabelParserequestfocus (parser, parent); }Else if(tag_include.equals (name)) {//Handling <include/> Label if(parser.getdepth () = =0) {Throw NewInflateexception ("<include/> cannot be the root element"); } parseinclude (parser, parent, attrs);//Parse <include/> node}Else if(tag_merge.equals (name)) {//Handling <merge/> Label Throw NewInflateexception ("<merge/> must be the root element"); }Else if(tag_1995.equals (name)) {//Handling <blink/> Label FinalView view =NewBlinklayout (Mcontext, attrs);FinalViewGroup ViewGroup = (viewgroup) parent;FinalViewgroup.layoutparams params = Viewgroup.generatelayoutparams (attrs); Rinflate (parser, view, Attrs,true); Viewgroup.addview (view, params); }Else{//Build a View instance object according to the node name FinalView view = Createviewfromtag (parent, name, Attrs);FinalViewGroup ViewGroup = (viewgroup) parent;//Call the Generatelayoutparams () method to return an Layoutparams instance object, FinalViewgroup.layoutparams params = Viewgroup.generatelayoutparams (attrs); Rinflate (parser, view, Attrs,true);//Continue recursive callViewgroup.addview (view, params);//ok, add the view to the parent view with a specific Layoutparams value} }if(finishinflate) parent.onfinishinflate ();//Completion of parsing process, notification:}
In the 37 lines of code for the Rinflate method, final view view = Createviewfromtag (parent, name, Attrs), a View instance object constructed from parameters such as node name, because the code below is getting bigger and larger, Directly post the main implementation function, see the Android source code specifically.
/** * Default visibility so the Bridgeinflater can override it. */View Createviewfromtag (view parent, String name, AttributeSet attrs) {//... try {// if (view = = null) {if (-1 = = Name.indexof (" )) {view = Oncreateview (parent, name, Attrs); } else {view = CreateView (name, null, ATTRS); }} return view; } catch (Inflateexception e) {// } }
And then go to the Oncreateview (), will find that it is actually the main implementation of CreateView (), so we directly createview realization.
protectedonCreateView(String name, AttributeSet attrs) throws ClassNotFoundException { return"android.view.", attrs);}
In CreateView (name, "Android.view.", Attrs), an instance object of Android.view.XXX (such as TextView) is created with a reflection mechanism and returned.
This is the layout resolution process for layoutinflater.inflate .
when you are familiar with the process, the next for you to explain the dynamic analysis of the wallpaper layout ideas, you basically understand the majority of the!
Because of the original layoutinflater.inflate (R.layout.main, null,false) function, the R.layout.main resource ID is passed in, and for our project, the layout file is updated online, is indirectly stored in the SD card, so this parsing method is not, fortunately Layoutinflater API method also provides inflate (Xmlpullparser parser, ViewGroup Root,boolean Attachtoroot) Parse, generate the required xmlpullparser according to the file save path:
publicgetXmlPullParser(String resource) { XmlPullParser parser = Xml.newPullParser(); try { // InputStream is=mContext.getAssets().open("transfer_main.xml"); isnew FileInputStream(resource); parser.setInput(is"utf-8"); catch (Exception e) { e.printStackTrace(); } return parser;}
The second step in the back is actually the same process (I'll post the demo in the back). But there is one thing that needs to be done with special attention:
Because the downloaded layout file to get the parsing stream, with the program Res/layout/xxx.xml there is a very big difference, the resources id!! The layout file in the program, the registered ID and text, or drawble can be found in the R directory, and the downloaded layout file is not the benefit, it is generated outside of us, and did not go through the APK compilation process. So in order to get the ID of the layout in the download file, we need to implement the attribute parsing of the view.
protectedonCreateView(String name, AttributeSet attrs) throws ClassNotFoundException { // return createView(name, "android.view.", attrs); return"com.xxx.xxxx.viewanalysis.view.VA", attrs); //比如com.xx.view.VATextView 项目中自定义view 继承自TextView}
The layout of the control code, can be textview, such as native, or can be customized, because TextView is prefixed, and then through the following reflection method, will also run to the corresponding custom method, return a custom View object. However, since the end is to run a custom view, so we need to define the pre-defined will be used, currently I have defined 9 kinds, such as button,gridview,relativelayout. If no defined view is used directly in the file, it causes compilation errors (ClassNotFoundException).
The Vabutton class implements:
Public class Vabutton extends android. widget. Button { Public Vabutton(context context, AttributeSet attrs) {Super(context); Setattributeset (ATTRS); }@SuppressWarnings("Deprecation") Public void Setattributeset(AttributeSet attrs) {hashmap<string, paramvalue> map = Ydresource.getinstance (). Getviewmap ();intCount = Attrs.getattributecount (); for(inti =0; I < count; i++) {paramvalue key = Map.get (Attrs.getattributename (i));if(Key = =NULL) {Continue; }Switch(key) { CaseId: This. Settag (Attrs.getattributevalue (i)); Break; CaseText:string value = Ydresource.getinstance (). getString (Attrs.getattributevalue (i)) ; This. SetText (value); Break;//case ... default: Break; } }}
Ps:view ID can be obtained by setting the flag on the outside (see Demo for details)
Attribute aspects of the resolution, I do not say much, the information on the Internet a lot, interested can go to understand the next.
Demo
In the project:
Next look at the code structure in the demo:
The code body here is the implementation of Ydlayoutinflater and Paramvalue, as well as 9 kinds of custom view.
Ydlayoutinflater is mainly modeled after the implementation of Layoutinflater, to do some adaptation of the layout of the file modification processing, which has been said.
The specific code
The idea of dynamic analytic layout is finished, applied to the project, the effect is good, although there are some restrictions, but for the overall functional design is irrelevant. About the theme of dynamic updates, if there is a better way of God, please private messages me Oh!
Copyright NOTICE: This article for Bo Master original article, without Bo Master permission not reproduced.
Android dynamically parse layouts for multiple sets of themes