I have a custom view that, on a high level, takes whatever drawable one might pass into it and draws a ring around it, the ring is animated, and comes with a whole set of parameters; things such as ringWidth, gradient colors, etc.
Currently, to show/specify this view in my XML, my markup looks a lot like this:
<RingedImageView
android:id="#+id/ringedImageView"
android:layout_height="#dimen/image_gigantic"
android:layout_width="#dimen/image_gigantic"
app:source="#drawable/ic_icon_1"
app:ringWidth="2dp" />
This is all well and good. However, instead of passing in a drawable as the image in the middle, what I would like is to be able to pass in a whole view, entirely seperate from #id/ringedImageView.
Ideally, I'd like this to look as follows in my XML:
<RingedImageView
android:id="#+id/ringedImageView"
android:layout_height="#dimen/image_gigantic"
android:layout_width="#dimen/image_gigantic" ...>
<ImageView
android:id="#+id/imageView" .../>
</RingedImageView>
My question then is, can I access that nested ImageView in my RingedImageView.java class? How can I specify how to handle this situation?
Some answers point to having to extend ViewGroup instead of View, but as this isn't a layout manager per se, and the component comes with its own rules for displaying content, a View seems more appropriate. Would appreciate a pointer.
Thanks all
Related
I'm new to Android and have recently started adopting the pattern to create an auto-layout-loading custom view based on a layout file. In the layout you use the 'merge' tag as the root, then in the constructor of your view, you inflate that layout into yourself so you are essentially the root of the merged-in controls. Right after you inflate, still in the constructor, you can find the child controls from the layout and assign them to private fields in the custom class so they will always be available, even when using recycler views.
Contrast that with including a layout. In that case, the layout has to define a root element (which can be your custom control, but in that case, you would not inflate the layout into yourself and would have to move the control lookup to onFinishInflate) but otherwise it ends up with the same view hierarchy.
In short, instead of this...
<include layout="#layout/layout_myCustomControl" />
You can now do this...
<com.mydomain.MyCustomControl />
However I find the auto-loading custom control version to be much more flexible and maintainable. The advantages of the second one are not only that you can use custom attributes in the referencing XML layouts (which you can't with the 'include' version) but it also gives you a central place to manage the code as well as layout management/control lookup.
So now I can do this...
<com.mydomain.MyCustomControl
app:myCustomAttribute="Foo" />
And if the control has to have code backing up its behavior, it's nicely encapsulated in the custom view.
Here's an example layout for the auto-loading version...
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="This is static text in the header" />
<TextView android:id="#+id/dateTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</merge>
And here's the class that uses it...
public class MainHeader extends LinearLayout
{
TextView dateTextView;
public MainHeader(Context context, #Nullable AttributeSet attrs)
{
super(context, attrs);
setOrientation(VERTICAL);
LayoutInflater.from(context).inflate(R.layout.header_main, this);
dateTextView = (TextView)findViewById(R.id.dateTextView);
dateTextView.setText(<<Code for setting date goes here>>);
}
}
But what I'm wondering is if the layout is purely static in nature--say holding a static image and fixed text--is it still considered good practice to use a custom view to represent it? I'd say yes for consistency, plus should I want to extend it in the future, you're already prepared for it and have to do so in just one place regardless of how many places it's used. In contrast, if you included the layout directly in say 20 places, you may have to update all 20 (depending on what's actually changed/needed.)
Pros for the Custom View approach:
Central location for managing layout loading and control lookup
Can support backing code implicitly/internally for updating the view.
Can use attributes when being referenced in other layout files
Better encapsulation (you can hide the layout itself from the outside world exposing behaviors via direct methods)
Can simply be 'new'd' up in code and the layout will automatically load. No inflating or casting needed.
Cons for Custom View approach
When used in Layout files, you now have to also specify width and height making usage a little more verbose.
May add extra classes to your project (not always, but sometimes.)
Again, it seems to me like a no-brainer, but I'm wondering if there's a preferred 'Androidy' way, or is it just a preference up to each developer?
Such approach will add additional level in your view trees. So you only made the UI tree more complex for no good reason. From other side, you can follow next steps to get rid of that side-effect:
<merge /> can only be used as the root tag of an XML layout
when inflating a layout starting with a <merge />, you must specify a parent
ViewGroup and you must set attachToRoot to true (see the
documentation of the inflate() method)
That's it.
Well apparently what I came up with wasn't that unique after all! Here's an article explaining in detail this exact scenario, along with the pros and cons of each. Looks like they too came to the same conclusion.
TrickyAndroid: Protip. Inflating layout for your custom view
For animation i use a ViewPropertyAnimator.
mAnimatedView.animate().translationYBy(50).start();
Before translation:
After translation:
I want buttons to stay below AnimatedView (according to the mentioned above illustrations it is not right now). There is only one idea that comes to my mind: animate buttons too, but i am sure it is not the best solution. Maybe there is a method that recalculates all layout while animation, that i have missed?
Add Buttons to xml before AnimatedView:
<Button ...>
<AnimatedView ...>
I want on the initialization of my activity in android to set the position of some imageView's
by code.
Lets say I have five cards displayed on the screen, all placed in (0,0) by me in the XML.
I want to calculate the screen size (easy to do) and then place the first card at 0.2height , 0.2 width the second one 0.4height, 0.4 width, ETC.
I want to do it through code so i could change some constants in the future and the rest of the changes will apply automatically
Thanks.
Create a LinearLayout container view in your layout, and then use code to add your new View to it
<LinearLayout
android:id="#+id/container"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
And then:
((ViewGroup)findViewById(R.id.container)).addView(myImageView);
try to use "findViewById()" to inflate some parental layouts,
"new ImageView()" to construct new Views and ".add(myImageView)" to add them.
In your code u can use all kinds of setters for the imageview...
(btw: more details would be great, the question is kinda ambiguous)
When declaring custom view in xml, what is the difference between declaring a View of a custom class, or declaring a completely custom view:
<LinearLayout>
<view class="packageName.MyView" android:id="#+id/myView" />
</LinearLayout>
and
<LinearLayout>
<packageName.myView android:id="#+id/myView" />
</LinearLayout>
?
I've created a subclass of EditText, and when instatiating it as View class=".." my Activity crashes with ClassCastException when trying to access MyView:
(MyView) myView = (MyView) findViewById(R.id.myView);
When declared as second option, everything works as expected.
I'm not 100% sure on this, but let me give it a go. A couple of things could be happening. The parser might not understand the class attribute correctly (e.g. it thinks it is part of a stylesheet). I am not sure how the parser handles the class attribute, since I have never seen or used it (in fact, I've never seen the <View> tag used either). A better explanation, though, might be this: the parser is trying to down-cast your View into packageName.myView class and fails (down-casting is always risky; up-casting is always safe).
Regardless of what is happening, I would always use the second option you listed, <packageName.myView android:id...>, instead of using the <View> tag. Reason being, it's redundant to use the <View> tag. Everything in this xml file must be a view (LinearLayout, Button, TextView, etc. are all descendants of the View class).
Hope that helps. If you're really, really curious, you could always download the source code for the parser...
I'm having trouble developing the UI for my first lame "game".
Here is a screenshot.
I'm using a LinearLayout that contains a TableLayout with TableRows. It seems so tedious and hard to control the position of elements.
For example, to get things to line up, I've inserted empty TextViews to "push" other elements into place.
I've also added padding to the buttons to get them to be the size I want.
Is there a better way of doing this?
Thanks!
You definitely want to be using a Relative Layout for this.
You would be able to specify where each button is in relation to other buttons.
Absolutely AVOID developing your UIs the way you are currently trying. The TextViews will be different sizes for different distributions of Android, and will likely only look right on the device you tested them for.
EDIT:
If you need empty space, use the XML attribute android:weightSum="x" in the parent view and android:layout_weight="y" in the child. This will make the child take up (y/x) of the space allotted to it in the layout_height and layout_width.
EDIT:
I think another good bit of advice for this would be to use individual layouts for things like your "direction" buttons. You'll be able to handle where they are on the screen as a group, instead of having to move each individually.
You should use RelativeLayout to solve this problem. I've gone through a similar problem once...
I didn't use the the Android's default buttons, for I had my own images for the pressed and unpressed behaviors...
Let suppose you want to place the east "button". You could use a function like:
public void addEastImageView(RelativeLayout myBackgroundLayout, ImageView center, ImageView east, int leftPadding, int topPadding, int rightPadding, int bottomPadding){
RelativeLayout.LayoutParams rightSide = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
rightSide.addRule(RelativeLayout.RIGHT_OF, center.getId());
east.setPadding(leftPadding, topPadding, rightPadding, bottomPadding);
myBackgroundLayout.addView(east, rightSide);
}
The ImageView called "center" would be the one you called "i" in your image. The padding parameters would allow you to control the distance between the ImageViews. You can create functions like this one to add the "west", "south" and "north" buttons also: you just have to change the parameter "RelativeLayout.RIGHT_OF" to "RelativeLayout.LEFT_OF", "RelativeLayout.BELLOW" and "RelativeLayout.ABOVE" accordingly.
If you want some behavior for your ImageViews, you just have to set it in the setOnClickListener. You can then change your ImageView's "image" with setBackgroundResource, for example, and set the others logic behaviors you want.
Hope it helps :D
Use an AbsoluteLayout - it lets you state exactly where to put every element
http://mobiforge.com/designing/story/understanding-user-interface-android-part-1-layouts
<?xml version="1.0" encoding="utf-8"?>
<AbsoluteLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
xmlns:android="http://schemas.android.com/apk/res/android"
>
<Button
android:layout_width="188px"
android:layout_height="wrap_content"
android:text="Button"
android:layout_x="126px"
android:layout_y="361px"
/>
<Button
android:layout_width="113px"
android:layout_height="wrap_content"
android:text="Button"
android:layout_x="12px"
android:layout_y="361px"
/>