I'm wondering if anyone can shed some insight as to the best practice for dynamically creating controls (inflate vs instantiate).
Inflate:
TextView styledText = (TextView)inflater.inflate(R.layout.styledTextView);
Instantiate:
TextView styledText = new TextView(mContext);
styledText.setTextAppearance(R.style.StyledTextStyle);
The object being created can either contain attributes in the inflated XML file, or be contained in a Style definition which is added to the instantiated object afterwards. (Assume that this styling includes width, background, text color, etc).
Haven't been able to run any time/memory tests of each method, was wondering if anyone knew which was quickest/most efficient.
LayoutInflator has a slight overhead because it has to parse xml in order to build the object. It also temporarily takes more memory for the same reason. Other than that, it builds the View object in the same manner that you would anyway. It may be something to worry about if you call it hundreds of times a second for some reason. 99.9% of the time though you'll never know the difference.
Also to note, any method that accepts an xml resource like "setTextAppearance" will have the same xml parsing overhead. The only difference in the examples you provided is it's not parsing the TextView xml, but it would still have to parse the style attributes.
Though this post asks about controls specifically, I think it's relevant to note that .. for working with a layout you want to dynamically create/add, I found in using the new (aka instantiate) approach , I was not able to get a reference to an inner ImageButton element that was defined in the xml file for which I instantiate the layout object reference.
When I use the inflate approach, the ImageButton was present upon reference.
So in my case:
Works :)
LayoutInflater inflater = LayoutInflater.from(getActivity());
CardView myCardView = (CardView) inflater.inflate(R.layout.my_cardview, null);
ImageView icon = (ImageView) myCardView.findViewById(R.id.iconId);
~~~~~~~~~~~~
Don't Work :( .. variable icon is null in this case
CardView myCardView = new CardView(getActivity());
ImageView icon = (ImageView) myCardView.findViewById(R.id.iconId);
Related
I'm trying to make simple program that allows to "load" an .xml file and display it as the View in setContentView of a new activity. Basically, what I would like is that the view of the new activity would be the same as if I called
setContentView(R.layout.my_view)
where R.layout.my_view would be that xml file. But in my case, that file doesn't yet exist at compilation time, and should be loaded dynamically (e.g., from storage).
I guess I could parse my .xml file and build the view dynamically by code, but that feels like reinventing the wheel and it's also most likely that the views won't be identical. I thought there should be some easy way to do this, but all the methods I found so far from similar questions (e.g.,
getResources().getXml(R.layout.my_view);
//or
LayoutInflater.from(context).inflate(R.layout.my_view, null) )
seem to require the file already in compilation time. Is there something I'm missing?
well, its not possible to load XML from server and parse it like usual layout XML. from LatourInflater doc:
Important For performance reasons, view inflation relies heavily on pre-processing of XML files that is done at build time. Therefore, it is not currently possible to use LayoutInflater with an XmlPullParser over a plain XML file at runtime.
and it isn't related to generating IDs or similar mechanism, just performance - so consider that you really need this feature, it is kind of blocked for a reason ;)
but if you still really want to load layout from third party you can use json2view lib. good luck!
It is impossible as you describe. But you can do it by Java code.
When we create a layout xml file under layout resource folder and give some id using android:id="#+id/textview" IDE generate an ID R.java file that hold all IDs we assign.
When we call that view from Activity or Fragment we create an Object using that ID like
TextView textview = (TextView) findViewById(R.id.textview);
and user that.
If you want to load xml code form storage or server as a plan text in xml format. You will assign some ID for your views. But IDE won't generate any ID for that. So you can not create object of them.
So solution is you have to generate view by Java code that means programmatically.
setContentView(R.layout.activity_main);
Display display = getWindowManager().getDefaultDisplay();
int width = display.getWidth();
width = width - (100/width)*80;
LinearLayout layout = (LinearLayout) findViewById(R.id.lo_dynamic_view_container);
LinearLayout.LayoutParams lparams = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
TextView tv = new TextView(this);
tv.setLayoutParams(lparams);
tv.setWidth(width);
float redious [] = { 0, 0, 8.3f, 8.5f, 8.2f, 8.9f, 0, 0 };
ShapeDrawable shape = new ShapeDrawable (new RoundRectShape(redious,null,null));
shape.getPaint().setColor(Color.GREEN);
tv.setBackground(shape);
layout.addView(tv);
Here activity_main.xml will have only one empty LinearLayout layout. You can generate view according to your condition or instruction document form storage or server and add them to LinearLayout.
Short Story:
I have a layout "layout.xml", which gets replaced by another layout "success.xml" after a successful web request. Both layouts have an ImageView that provides the backgrounds to the layouts. These 2 backgrounds both need to be the same, and both are dependent on a user preference.
Longer Story: This all happens in a Fragmnet with an AsyncTask replacing the contentView with "success.xml" in onPostExecute after the web request. This happens as follows:
View view = getView();
view = null;
view = View.inflate(context, R.layout.success, null);
What I tried to do is give both ImageViews the following android:id="#+id/background_image" and then call
ImageView background = (ImageView)view.findViewById(R.id.background_image);
background.setImageResource(R.drawable.bg1);
This background-setting works for the initial view (layout.xml), but on trying to change to "success.xml", I get a NullPointException because background is null.
I've checked and the View's id is set to -1 while the original view's background_image id is set to something sensible and valid.
I've also tried setting the second view's background id like this: android:id="#id/background_image", i.e. without the '+', but still no luck.
The added complication is that it's not just 2 layouts, but about 5 that I need to do this for, so it would be really handy to recycle view id's.
Any help would be greatly appreciated.
Your code for replacing the fragment's view will not do what you want, the original view will remain the same as you change only a reference to that view and not the actual object.
To replace the view of the fragment with the new layout you could have another ViewGroup(for example a FrameLayout) in the basic layout (layout.xml) wrapping your current content(don't forget to give it an id) of layouts.xml(as I understand this is the basic layout). Then, when it's time to replace the layout you could simply do:
// remove the current content
((ViewGroup) getView().findViewById(R.id.yourWrapperLayout)).removeAllViews();
// add the new content
View.inflate(context, R.layout.success, ((ViewGroup) getView().findViewById(R.id.yourWrapperLayout)));
You could avoid adding an extra layout if, by any chance, all your five layouts have the same type for the root view(like a LinearLayout etc). In this case you would use the same code as above but you'll modify the other layouts file to use a merge tag. Also, you'll be looking for the id of the root in the layout.xml layout into which you'll add the content of the other files.
Then you could have the same ids, but you'll have to reinitialize any reference to the views(meaning that you'll have to search for the view again if you store a reference to the view(like a Button field in the fragment class)).
I want to design the layouts programatically that means without the xml file as per project requirement.
But the terms used programatically is completely different from xml file.Is their any useful tutorial to learn programatically that means without xml file. guide me!
you can create any view you want
a linear layout
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.FILL_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
A Text View
final TextView tv = new TextView(this);
tv.setBackgroundColor(0xFFFF00FF);
tv.setTextColor(0xFF000000);
tv.setTypeface(null, Typeface.BOLD);
tv.setText("Where is Fred?");
tv.setGravity(Gravity.CENTER_HORIZONTAL);
and anything else.
Source
I advise you to spend some time learning about the View class and its popular subclasses such as LinearLayout, RelativeLayout, and so on. (Also, spend a bit of time looking at Drawables.)
When you're creating a layout using XML, you're using XML to define a hierarchy of View objects which, at runtime, are "inflated" into a real hierarchy of View objects that the XML layout file describes. For example, your first XML layout file might be a simple LinearLayout that contains a TextView (note I'm simplifying it for brevity):
<LinearLayout ... >
<TextView ... />
</LinearLayout>
In your Activity you would use this layout using setContentView().
All that XML file is doing is providing a specification, if you like, about the View structure that the system needs to build (or inflate) for you. The end result is that there will be a real LinearLayout object (which is a subclass of View) that has a reference a child TextView (again a subclass of View) together with suitable layout parameters.
To do the above programmatically (i.e. by creating instances of objects and using their methods, rather than inflating from XML) you might do something like (again simplified):
LinearLayout container = new LinearLayout(this);
TextView tv = new TextView(this);
tv.setText("hello");
container.addChild(tv); // Simple example - usually you'd specify layout parameters
setContentView(container);
The basic point I'm making is that, in really simple terms, a layout XML file can be thought of as a kind of 'script' that you can use to tell the system how to create a hierarchy of Views. You can create exactly the same result by programmatically creating instances of View objects and calling appropriate methods on them. Whichever route you take, the end result is the same: a bunch of View objects in memory that represent a View hierarchy.
What you will find is that XML layout attribute names aren't necessarily the same as the corresponding method names, but you can use the relevant API documentation to see what corresponding XML attribute strings are for given methods. For instance, the API documentation for LinearLayout details all of the methods as well as the XML attributes.
As mentioned in topic, I have some Views, e.g. a TableRow with always the same background used as topic, or a special TableRow containing a TextView with some special styles/properties. These Views are set dynamically, so it's problematic to use a XML for this. As I read it's not possible to set styles programmatically too. So what's the best way to solve that?
Possibility 1:
I use and instance derived Views, like this:
public class TopicTableRow extends TableRow {
public TopicTableRow(Context context) {
super(context);
setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));
setBackgroundColor(Color.parseColor("#777777"));
setClickable(false);
}
}
Possibility 2:
I could create a valid xml template with a special layout I never use in the application, containing the needed Views which have already all assigned styles. Afterward I access the needed Views by R.id....
But this method seems to be very dilettante to me.
I don't think that those 2 possibilities are the "real" Android way to do this, so how is this usually done?
If you want to set specific styles for groups of elements, you can use the themes and styles concepts in android.
You can read up on them here: http://developer.android.com/guide/topics/ui/themes.html
It is not possible though to change the style attribute of a view programatically.
Therefore the android way is probably to create the Views you need in XML and use a LayoutInflater to get create an 'java' version of the xml view. This allows you to reuse the component and fill it with apropriate data for as many rows as you would like.
Button view = (Button) LayoutInflater.from(this).inflate(R.layout.textViewFromWeb, null);
I hope this will be of use to you!
Why is
TextView test = (TextView) findViewById(R.id.testTextView);
test.getText();
generating a null pointer exception? The id is correct, testTextView is correctly declared in my XML layout file.
The only reason for findViewById to return null if you are passing a valid id is that you are either setting the wrong content view (with setContentView) or not setting a content view at all.
I think you might have written setContentView(..) after defining the TextView. Reverse these, and it should work.
Change:
TextView test = (TextView) findViewById(R.id.testTextView);
.
.
setContetView(..)
To:
setContetView(..)
.
.
TextView test = (TextView) findViewById(R.id.testTextView);
You probably haven't called setContentView. You can only use findViewById to get elements of views that have already been inflated.
You could also use a layoutinflater to inflate the view, but that's probably not what you want.
Are you sure the TextView is set on the right XML?
For example if you're creating a Dialog that loads a custom XML, to get an element from that xml you have to mention it in dialog.findViewById(R.id.testTextView);
Like Falmarri said, the view has to be inflated.
I understand you solved it by creating a new project, but still thought to mention it for future users.
It can also be that you defined the activity in two files. For example layout and layout-v21 and some information like id is missing on one of them. So check all the activity's layouts
In my case, the layout was not finished inflating. Solved by adding a small delay before trying to access the TextView.
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
#Override
public void run() {
TextView test = (TextView) findViewById(R.id.testTextView);
test.getText();
}
}, 100);
I struggled with this for a while and what I realized was, that, if you have more than one layout file version like:
"activity_one.xml" in "layout" folder and one in "layout - small" folder
Which I used for multiple phone layout support, the problem was that one of the TextViews was in both, with the exact same ID and everything, however the difference was that one was higher up in the hierarchy of views in the layout.
When I changed them to be in the same spot it worked.
(I know this is already answered, but maybe this helps someone out there. Very rare though.)