I'm trying to make a tab bar in android using a RelativeLayout and a RadioGroup that has an indicator which will slide to indicate the active RadioButton within the RadioGroup.
These are customized radio buttons that are effectively rectangles with an icon over text, all contents centered, which uses a custom state-selector for the background.
Here's my current hierarchy:
<RelativeLayout> // Main view of the tab bar
<RadioGroup> // The buttons
<RadioButton />
<RadioButton />
<RadioButton />
</RadioGroup>
<ImageView /> // The indicator
</RelativeLayout>
The idea I had was when a radio button was clicked, I would align the indicator to the top & bottom of the selected radio button (and animate it). However, It seems that layout_align<Edge> only works with sibling elements, not with members of another view group, i.e. I can align to the RadioGroup itself, but not to a RadioButton inside it.
I thought of putting the indicator as a member of the RadioGroup, but since RadioGroup is an extension of LinearLayout, there doesn't seem to be a way to place it at the edge of a given RadioButton.
Can anyone think of how I might solve this issue? Or perhaps there's a better solution than my animate-align-to-button technique?
UPDATE
With #superjos help, I managed to work this out fairly well.
I had to give each button a known height instead of using wrap_content (not ideal, but for this project it should probably work fine). Make the indicator height match that of the buttons. Make sure the RadioGroup is set to wrap content and be centered vertically in the parent view. Set indicator to alignTop and toRightOf the RadioGroup. Then, for the button click listener:
int prevY, newY;
int prevButtonIndex, newButtonIndex;
public void moveIndicator(button, indicator, index) {
prevY = newY;
newY = prevY + (index - prevButtonIndex) * button.getHeight();
TranslateAnimation animation = new TranslateAnimation(0, 0, prevY, newY);
animation.setDuration(500);
animation.setFillAfter(true); // stay at final animation position
indicator.setAnimation();
animation.start();
}
Here's my idea about it. Following are some more detailed steps.
The idea is to have the ImageView has a RadioGroup sibling, as it is in your sample. The ImageView is initially positioned on the right of the RadioGroup, and its horizontal middle line is aligned with the one of the first (or whatever chosen) RadioButton.
Then, upon a RadioButton click, a TranslateAnimation is built/configured at runtime in order to let the ImageView shift vertically until its middle line gets aligned with the one of the clicked button. The animation is set with fillAfter so to stay firm after it completes.
Steps:
Create your resources so that ImageView and RadioButtons have the same height, or else work with vertical paddings so that they result having the same height.
To initially align the ImageView to the first RadioButton, some XML could suffice: in ImageView attach layout_toRightOf and layout_alignTop to the RadioGroup. In order to take into account the top-padding from RadioGroup, you could add that to the initial ImageView top-padding XML attribute. Or better as top-margin.
When a RadioButton is clicked, create a TranslateAnimation that will be applied to the ImageVew, changing only the Y coordinate starting from 0 to its destination toY value (while from/toX attribute will be 0). That is given by a formula such as:
toY = (ClickedRadioButton.Index - LastClickedRadioButton.Index) * RadioButton.Heigth
Here Index is the ordinal position of the RadioButton within the group (e.g. from 0 to 2) (Warning: that is pseudo-code, not necessarily an existing Java field named Index).
Apply the animation to the ImageView and start it.
You could customise the RadioButton style to include a 9patched version of the indicator image as a background for the selected (might be checked for radios..) state. Or as drawable
However, it depends what you want the animation to look like.
For example.
<style name="TabRadioButton" parent="#android:style/Widget.CompoundButton.RadioButton">
<item name="android:background">#drawable/tab_bg</item>
<item name="android:drawableLeft">#drawable/tab_indicator</item>
</style>
Related
I have a LinearLayout that has 2 children: a ImageView aligned left and a TextView aligned right.
I've set the background of the LinearLayout to be a #drawable XML resource that has two <item> tags. One of them has android:state_pressed="true". Also, the LinearLayout has android:clickable="true".
When the LinearLayout is clicked it correctly changes its background to the android:state_pressed style, but clicking on one of its children doesn't propagate the click action up to the LinearLayout.
Is there a way to easily achieve a click state on the parent view when a child view is clicked?
Dont use Both as it will give Exception
Use this to your parent
android:addStatesFromChildren="true"
Or add in your child views
`android:duplicateParentState="true"`
Hope it helps
not sure if it works for your specific implementation, but a very easy way of achieving this "complex" button is by making a normal button and using android:drawableLeft or android:drawableRight or android:drawableTop or android:drawableBottom and android:drawablePaddig to achieve the same visual result in just one view.
for example:
<LinearLayout orientation=vertical>
<ImageView/>
<TextView/>
</LinearLayout>
is pretty much the same as
<Button
drawableLeft="#drawable/..."
text="..."
/>
that way your whole layout is simpler and the pressed states works out-of-the-box.
Consider using a single TextView and using the android:drawableLeft or android:drawableRight attribute to show your image. It's better design and more performant than a LinearLayout with two children.
If that won't work for you, try adding android:addStatesFromChildren="true" to the LinearLayout.
I'm trying to apply a TranslateAnimation to an ImageView inside of a LinearLayout. As soon as the ImageView(marked "1" in the image below) crosses the bounds of the LinearLayout that contains the ImageView, it goes "black"/disappears. This does NOT happen if I animate the entire green LinearLayout, so I don't think it has to do with it's z-value. Rather, I believe that the ImageView can't visually "escape" its container layout (green). What can I do to make the ImageView display in front of everything when the animation is being performed? I've already tried .bringToFront()(followed by .requestLayout/.invalidate of the root view).
Try to set android:clipChildren="false" in a parent container
I am developing an app with an activity with member reactions on a hike event. The reactions are the yellow "balloons" which are made using a LinearLayout. Each item is constructed from a XML file (listitem_deelnemerreactie.xml) which defines the layout for a reaction item. The top level of this layout file is a LinearLayout my itself.
I want some spacing between the separate elements, as well as some right margin. The most straightforward way to so this should be: setting a bottom and right margin on the top-level LinearLAyout element of the listitem_deelnemerreactie.xml layout file.
But setting the bottom margin on the LinearLayout has no effect on the vertical spacing, though the right margin does have an effect.
The only way to be able to set a vertical margin appears to be: setting is in the Java code, after attaching the inflated view to the container.
See the two images for the effect and the code.
Though setting the margins in the code is a working workaround, I still think it is strange this cannot be achieved in the XML. Why is the bottom margin attribute ignored while the right margin is not?
Any ideas?
Have you tried to set an android:padding="10dp" for example on your elements to spaced them ?
How do I go about implementing a button bar with buttons of different shapes and heights? As an example (please excuse my poor drawing skills for this quick mockup):
The example bar has 3 buttons, with the middle button (3) a different shape and height than the other 2 buttons (1,2). The button bar will be a view that is included and merged into other views so as to seem to float on top of the parent view.
I was thinking of implementing buttons 1 and 2 into a layout, and then button 3 as another layout that I then merge with the first two button's layout.
like my previous comrades said, you need some kind of layout or container that can have a background (if you wish for button #3 to hoover above it) then use relative layout for mixing the two the disadvantage of this other than complexity is that youcannot relate to the other two buttons since they reside in a different layout.
More elegant solution may be to have a special background drawable that can:
have a method setCurrentHeight() that will specify the height the actual viewable section should have the rest will be filled with transparent color.
override it's own draw so just before it's drawing it will have a callback called, call back you can register yourself to.
then you can register the callback in your activity to take the current position of the #3 button and set the height accordingly, this way you are still with one layout with special drawable as background.
A customized LevelDrawable might do the trick.
I would layout this bar as follows:
A RelativeLayout as a container for the rest, with height set to wrap_content and alignparentbottom = true
An ImageView for the bar
2 Buttons with a transparent background (Button 1 and 2)
Button 3 is a custom Button with a custom Image of a trapezoid as background
So you will have a Layout similar to this:
<RelativeLayout
...>
<ImageView
.../>
<Button
... Button 1 />
<Button
... Button 2 />
<Button
... Button 3 />
</RelativeLayout>
I don't exactly know that this will work, and I can't test it, but you might give something like this a try; I believe it can all be done elegantly through XML.
Have a RelativeLayout (id:mainLayout) that will contain all of your views, wrap_content for both dimensions.
Have a blank View as your first child that will serve as your background bar
Set the View's background color/image to what you want; layout_width to fill_parent; layout_height to wrap_content; layout_alignTop="#id/leftButton"; layout_alignBottom="#id/leftButton".
Add an ImageButton for your center button (id:bigButton), wrap_content for both dimensions; layout_centerInParent="true".
Add an ImageButton for your left button (id:leftButton), wrap_content for both dimensions; layout_toLeftOf="#id/bigButton"; layout_centerInParent="true".
Add an ImageButton for your right button (id:rightButton), wrap_content for both dimensions; layout_toRightOf="#id/bigButton"; layout_centerInParent="true".
In my head, I believe this works, but I could be off. Regardless, something to think about, and I hope it helps you find a solution. :)
Better you can tablelayout with different button styles or relative layout for button "3"
i use this a s surface view in my application and now i want to add text view to this programmatically. How can i do that.
"<com.csfcse.udrawer.UdrawerDrawingSurface
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="#+id/drawingSurface"
android:layout_gravity="left" />"
Thanks......
An option may be to have a FrameLayout with two children, first the SurfaceView, then whatever you want to overlay on the SurfaceView. In the sample below, the second View in the FrameLayout is a horozontil LinearLayout with a Button and a TextView. The FrameLayout displays all its children as piled in Z order with the first child at the bottom and all children positioned at the upper left corner. In this case the LinearLayout has Gravity.CENTER_VERTICAL (I think you could do the same thing with padding on the LinearLayout.
I have never found much (good) documentation about how SurfaceViews (or any of the screen drawing) works. There may be a problem with flickering or refresh, but I don't see much problem on my Froyo Evo. (This test app draws 'twirling' lines on the SurfaceView below the Button and TextView.
If the question is simply: How do you programmatically add a TextView to a Layout that was created by a XML inflated Layout, then, get a reference to the Layout instance and call addView() to it.
Layout lay = parentLayo.findViewById(R.id.drawingSurfaceParent);
TextView tv = new TextView(this);
tv.setText("New textview");
lay.addView(tv);
You'll need to use the Canvas and a corresponding text draw method:
drawText(String text, int index, int count, float x, float y, Paint paint).
This is not possible, a SurfaceView cannot contain other Views.