I used to do alot of Flash Actionscript and now I am getting into Android. Is there something in the Android API that is similar to duplicateMovieClip() in Actionscript? I'm sure there is probably a way to write such a method, but I am wondering if there are any existing shortcuts.
For example, say I have an ImageView, TextView, or other kind of View Object on screen and I want to have a button to click which will make a duplicate of some object on screen.
If you don't mind my asking, why do you need something like duplicateMovieClip()?
To answer the question, Android doesn't have a notion of the AS2 duplicateMovieClip(). Much like in AS3 (which also didn't have duplicateMovieClip()) you'll have to implement your own cloning method. Java does have an unimplemented '.clone()' method as part of every Java object, so if there's a particular View you would like to clone you might be able to implement your cloning there by
Overriding the clone method.
I think what you'd probably end up doing instead is doing something more akin to instantiating from the Library by making small view layouts in xml and inflating them using the Inflater tools.
View result = null;
// where pContext is a context object, either supplied by the application
// or just by the current Activity (if available)
LayoutInflater inflater = (LayoutInflater) pContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
// where id is the layout id such as R.layout.myclonableview.
// where pRoot is the parent container for the new result.
// where pAttachToRoot is whether to immediately inflate the new view into the root.
result = inflater.inflate(id, pRoot, pAttachToRoot);
// Now "clone" your old view by copying relevant fields from the old one to the
// one stored in result
Related
TL;DR: Is there anything in com.android.layoutlib.bridge.android.BridgeContext that can substitute for Activity#findViewById(...)? I've looked at the source, but I can't find anything.
When running on a real device, an attached view's #getContext() returns the Activity. The view can cast it and call #findViewById(...) to obtain a reference to some other view.
But when running in a WYSIWYG editor, #getContext() returns an instance of a different class. I'm getting com.android.layoutlib.bridge.android.BridgeContext. This class isn't part of the public API, so I'm planning to access it via reflection and degrade gracefully if the implementation changes.
If you're wondering why my view wants a reference to another view... I've created a view that appears to have a hole in it. It works by delegating its drawing to another view. If the view with the hole is placed on top of other views, then it appears to punch a hole through any views beneath it, all the way down to the view it's using for drawing. It works perfectly on a real device, but it would be nice to have it also work in the WYSIWYG editor.
It's bad to assume that View.getContext(), or any other platform method that returns Context, can be cast directly to more concrete classes, like Activity. There exist classes like ContextThemeWrapper which can easily destroy your assumption.
I would recommend restructuring what you are doing so that you have a parent layout that can act as an intermediary for the hole-y View and what's below it.
Or you could have a setter which would provide the View for you.
A last option is to call View.getParent() a bunch of times to get the root View and call findViewById() on that:
ViewParent parent;
while(getParent() != null) {
parent = getParent();
}
View root = (View) parent;
root.findViewById(R.id.my_view);
BTW, BridgeContext is used in the WYSIWYG in place of Activity because it only mocks the Android View/Layout/Rendering system, it doesn't emulate it completely. This can be seen in other ways like how it renders shadows or shape drawable rounded corners.
I awarded the bounty to dandc87 because his answer led me to the solution. However, the code snippet in his answer crashes with a ClassCastException because the root ViewParent is not a View. The mods keep rejecting my edits, so here's the complete and correct solution:
private View findPeerById(int resId) {
View root = this;
while(root.getParent() instanceof View) {
root = (View) root.getParent();
}
return root.findViewById(resId);
}
Studying some (known to be good) code I can see the logic as follows:
if (getContext() instanceof Activity) {
inflater=((Activity)getContext()).getLayoutInflater();
}
else {
inflater=LayoutInflater.from(getContext());
}
I wonder, why this if/else, how it is better to, just, using LayoutInflater.from in all cases?
It doesn't really matter much.
Activity delegates getLayouInflater() to Window. The usual policy implementation of Window PhoneWindow in turn initializes its inflater with LayoutInflater.from(context) where context is the activity.
So the inflater object is really the same, using the same Context in case of an activity.
LayoutInflater.from() is really a wrapper for Context.getSystemService(). Now, system services are looked up by name from a map and then retrieved from a cache. This lookup has some overhead when compared to accessing an already initialized member variable in Activity.
So it smells like a micro optimization that does not likely affect much runtime performance when compared to actual view hierarchy inflation.
This optimization can actually have negative impact on developer productivity as people need to stop and spend some thinking why the code is there.
By the same code it seams that LayoutInflater.from is used in the contexts that are not an activity. I would assume that using the Activity's inflater reuses an already created inflater versus the other choice which would create a layoutinflater from a context.
The only change I would make is saving the context in a variable to prevent calling the same function and retrieving the same value from the object repeatedly:
Context ctx = getContext();
if(ctx instanceof Activity) {
inflater = ((Activity)ctx).getLayoutInflater();
}
else {
inflater = LayoutInflater.from(ctx);
}
Android has a lot of optimizations in place to reuse items when available, like the views for ListViews which can be reused.
from the android documentation it is suggested to use getLayoutInflater( ) instead.The documentation says the following about the LayoutInflator.from.. :
Instantiates a layout XML file into its corresponding View objects. It
is never used directly. Instead,
it suggests to use :
use Activity.getLayoutInflater() or
Context#getSystemService to retrieve a standard LayoutInflater
instance that is already hooked up to the current context and
correctly configured for the device you are running on.
in other words for the sake of simpler code and performance you better use getLayoutInflater from the context that has already been initilized.
i would like to ask a lot of questions about how this whole id system works in android. I looked up the View documentation, but the description was too shallow for my taste.
Is there a pattern, how the IDE (Eclipse/Netbeans) generates the ids
when i use android:id="#+id/..."? Or is it completely random?
If i set ids programmatically, then will it be found by the Context
classes findViewById() function?
If the answer for the previous question is yes, then if i want to
create a large amount of Views, but i want them to have distinct ids
for later identification, then wich one is better to use? (To answer
this question, it would be really useful to know the answer for the
first two)
For example generating random ids in the largest possible range:
Random random = new Random();
for(int i=0; i<100; i++)
{
View view = new View(someContext);
view.setId(random.nextInt(Integer.MAX_VALUE));
}
Or setting the ids in some sort of order, for example:
final int addToId = 5670;
for(int i=0; i<100; i++)
{
View view = new View(someContext);
view.setId(i+addToId);
}
Also i would like to know, what happens, when you use a
LayoutInflater for example to populate a ListView using a
pre-defined xml layout for every item in the list. Then you get your
sub-views in the getView() function by the findViewById(). So i
assume, that all the identical Views across your listitems have the
same id. If so, then is it a good practice to use the tag
attribute to distinguish the items in an inflated layout?
Any clear explanation for these question would be highly appreciated!
#+id/.... creates an id value that lives within the applications namespace. Contrast this with #android:id/.... which lives in the android namespace.
When you set the id in code and add the view element to the layout it will then become available to access through the code. You won't be able to reference it from the xml
Not sure you want to be using random to generate your ids? think sequential would be better but even then what is the point of a random id? How do you know which view you are referring to?
Definitely use the tag option and look to use the ViewHolder pattern for smoother list scrolling. You could add the id to the view holder class if you need access to it but it would be available anyway through the data set being used to populate the list. A quick search will give you plenty of examples for this.
I would like to create dynamic table in android (custom number of rows and columns). Minimum sdk is 3.0
I suppose to crate it via one of 2 ways:
1) via creating new TextView
TableRow tr = ....;
for ( i = 0; i < NumOfRows .... ) {
TextView tv = new TextView(this);
tv.setLayoutParams(...);
tv.setText("Text I wanna to see");
tr.add(tv);
}
2) via inflater
LayoutInflater inflater = (LayoutInflater) this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
for ( i = 0; i < NumOfRows .... ) {
TextView tv = (TextView) mInflater.inflate(R.layout.my_cell_layout, null, false)
.findViewById(R.id.my_cell_item);
tv.setText("Text I wanna to see");
tr.add(tv);
}
3) Your way :)
What is faster? What should I select?enter code here
It's all as per your requirement that which is better.
from link http://www.aslingandastone.com/2010/dynamically-changing-android-views/
Because layouts can be created either in XML or in code, you could probably make do without ever having to do dynamic XML layout loading. That being said, there are some clear advantages as to why I think one may want to do so:
Code cleanliness. Doing anything more than basic layouts in code can get very messy, very fast.
Code re-use. It’s extremely easy to inflate an XML layout into a specified view with one or two lines of code
Performance. Creating the objects necessary for an in-code layout leads to unnecessary garbage collection. As per the Android Designing for Performance article, “avoid creating short-term temporary objects if you can.”
Attribute availability. Defining Views in an XML layout exposes attributes that are not always available by object methods.
*
Possible disadvantages:
It make take more time to do an XML layout versus defining the layout in code, especially if there are only one or two interface elements that need to be changed.
*
To find out which is faster, implement both methods and use the method profiling utility, TraceView. That being said, its easier to maintain an XML file when making changes than it is to modify code. So, if I were you, I would prefer technique #2.
I'm new to Android and find it brutal (there seems to be an near infinite number of details and dependencies to remember).
Anywho, I got the TextSwitcher1 example app working, which uses ViewSwitcher. I'm assuming ViewSwitcher is the way to go, need to either display a map or a table, user can pick, and switch back and forth.
So I created my MapActivity in another application, seems to work. Next integrate into main app. So, call
View v = findViewById(R.layout.mapview);
and then
mSwitcher.addView(v);
except "v" is null. Why? Do I create the activity? But I don't want to show it yet. Is there such a call as "create activity but hide it until needed"? Or am I barking up the wrong tree?
Thanks for any insight.
The findViewById function returns a View based on an ID resource (R.id.something) for whatever view you have loaded in your activity (using setContentView(R.layout.main)). In your sample code, you're using a layout resource (R.layout.mapview). You should inflate the XML file, which will return a View that you can use to add to the ViewSwitcher.
Example Code:
LayoutInflater vi = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View v = vi.inflate(R.layout.mapview, null);
mSwitcher.addView(v);
However, you should be able to define everything in your XML file and not have to manually add the pages to your ViewSwitcher. Here's some example code on how to do that: http://inphamousdevelopment.wordpress.com/2010/10/11/using-a-viewswitcher-in-your-android-xml-layouts/