I have an ImageView aligned in the center of RelativeLayout, and I want it to transition to alignedParentTop. How to get deltaY necessary to set up TranslateAnimation?
EDIT:
Since I've been asked to showcase what I've already done, here's the code I'm using:
private Animation getTranslateAnimation(final View v) {
final float yDelta = getScreenHeight() - (2 * v.getHeight());
TranslateAnimation translateAnimation = new TranslateAnimation(0, 0, 0, yDelta);
translateAnimation.setDuration(Initial.DURATION_MILLIS);
translateAnimation.setInterpolator(new AccelerateDecelerateInterpolator());
return translateAnimation;
}
The problem with this approach is that I have EditText that I show after this animation, and it should be positioned just below the ImageView. I've set AnimationListener that makes it visible onAnimationEnd, but it positions it below the original position, not the new one. Is there some easy way around or should I switch to PropertyAnimation framework?
If you have a look at the Animation Resources Documentation, you will see that you can just define the values relative to parent or self. So you could have an xml-resource looking like this:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:fromXDelta="0%"
android:toXDelta="0%"
android:fromYDelta="0%"
android:toYDelta="0%p" />
</set>
This will have the effect, that the view is translated from its original position, up to aligning with parent top (because android:toYDelta="0%p" - the "p" indicates that it's relative to parent).
If you don't need to really do it programmatically, this should work.
Related
The AbsoluteLayout in Vaadin 8 (Framework) enables pixel-position-oriented placement of widgets within a layout. While not my first layout of choice, the AbsoluteLayout is suited to porting code from other UI-building platforms that use pixel-position-oriented layout.
Example code from the manual:
// A 400x250 pixels size layout
AbsoluteLayout layout = new AbsoluteLayout();
layout.setWidth("400px");
layout.setHeight("250px");
// A component with coordinates for its top-left corner
TextField text = new TextField("Somewhere someplace");
layout.addComponent(text, "left: 50px; top: 50px;");
I can see that the passed argument is simply CSS coding. But I am no HTML/CSS wizard, that’s why I am using Java-based Vaadin in the first place.
The migration guide for moving from Vaadin 8 (Framework) to Vaadin 10 (Flow) says in this list of components that the AbsoluteLayout from 8 is not included in 10, nor do they plan to add it in the future. But that page does offer this note about replacement for AbsoluteLayout:
Very easy to achieve the same in V10 using e.g. Div
Can someone explain what this would mean in a Java-based Vaadin app? Perhaps an example?
How might a person conveniently and routinely do pixel-positioning of widgets in a Vaadin 10 Flow app?
As your request for an "Hello World" example app, I downloaded the Project Starter with Spring Starter from https://vaadin.com/start and combined Tatu's solution with your example usage code. You can find it at https://github.com/Peppe/absolute-layout-demo.
You can test it live with the following commands in terminal / command line:
https://github.com/Peppe/absolute-layout-demo.git
cd absolute-layout-demo
mvn spring-boot:run
I created a class called AbsoluteLayout, with it's entirety looking like this:
public class AbsoluteLayout extends Div {
public AbsoluteLayout() {
getElement().getStyle().set("position", "relative");
}
public void add(Component component, int top, int left) {
add(component);
component.getElement().getStyle().set("position", "absolute");
component.getElement().getStyle().set("top", top + "px");
component.getElement().getStyle().set("left", left + "px");
}
}
Only change that I did, compared to what Tatu said, was to give the position relative to the parent layout. This makes the position of the children added to the layout relative to the layout, and not the body (or parent position relative in the DOM structure). Otherwise the component would be in top:50px, left:50px from browser corner.
Then the usage class looks like this:
#HtmlImport("styles/shared-styles.html")
#Route
public class MainView extends VerticalLayout {
public MainView() {
setClassName("main-layout");
//Just to add some content on the page to test relative position
for (int i = 0; i<5; i++){
add(new Div(new Text("Hello")));
}
// A 400x250 pixels size layout
AbsoluteLayout layout = new AbsoluteLayout();
layout.setWidth("400px");
layout.setHeight("250px");
// A component with coordinates for its top-left corner
TextField text = new TextField("Somewhere someplace");
layout.add(text, 50, 50);
add(layout);
}
}
I added a few lines of text before the layout to add some rows of text, just to test out the position:relative mentioned above.
Hope this helps and gets you on the right path. As you notice, this "AbsoluteLayout" doesn't have really any code to it - it is just a div. You can do this same trick with any layout in your app if you want to place one element into a relative position.
The simplest way in Java-based Vaadin app is to use Div as the layout and add components there.
For each component you want to position you need to apply CSS styles, there is Java API for that, i.e. component.getElement().getStyle().
It could be something like
public void setPosition(Component component, int x, int y) {
component.getElement().getStyle().set("position","absolute");
component.getElement().getStyle().set("top",y+"px");
component.getElement().getStyle().set("left",x+"px");
}
Probably you want to extend Div and the above method (that makes rudimentary AbsoluteLayout)
See also
https://developer.mozilla.org/en-US/docs/Web/CSS/position
I configured a layout with the following result:
The Layout of the Form's contentpane is TableLayout which consists of one cell. The contentpane has only one container as child for which I set the layoutConstraint with the following modified values: width = 100%, height = 100%, Align = CENTER, Vertical Align = CENTER.
In other words, I'm centering the components of the form.
In the preview it looks as expected but not when the app runs in the simulator!
Note:
I'm not modifiying any style or layout properties in my code! Thus I have no idea why it's not as intended in the simulator.
The approach from How to show two or more label at centre of the container in codenameone didn't work for me!
this is my code concerning that form:
#Override
protected void postIntroLangSelect(Form f) {
f.getTitleArea().setHidden(true);
f.revalidate();
// button configuration
Button btnDE = findButtonDE(f);
Button btnTR = findButtonTR(f);
btnDE.addActionListener((e) -> {
settings.setLocale(Lang.GERMAN);
showNextForm();
});
btnTR.addActionListener((e) -> {
settings.setLocale(Lang.TURKISH);
showNextForm();
});
}
Set your form's layout to BorderLayout and check the Absolute Center, then add a container on the form with its layout constraint set to Center. Give this container a BoxLayout Y layout and add all your other components in it.
If this approach didn't work for you, then you will have to hand-code your form and set it's Layout to new BorderLayout(BorderLayout.CENTER_BEHAVIOR_CENTER).
I've 3 (loader, locker and debug view) hidden views (touchEnabled and visible set to false, and zIndex to 1) above the main view (zIndex = 2).
Each 'over' view has this method:
$.debugView.show = function() {
$.debugView.touchEnabled = $.debugView.visible = true;
$.debugView.zIndex = 3;
};
$.debugView.hide = function() {
$.debugView.touchEnabled = $.debugView.visible = false;
$.debugView.zIndex = 1;
};
This screen has the 3 'over' views hidden:
Now, I'm opening the 'debug view', but, SOMETIMES it seems like it changes the positions (as if the center it's on the top left corner instead of the center of the device).
Instead of the required result:
If I use the opacity instead of the visible property, it works properly.
This might be an SDK bug right?
<Alloy>
<Window>
<View id="content"/>
<View id="locker"/>
<View id="loader"/>
<View id="debugView"/>
</Window>
</Alloy>
All of these 4 views don't have width or height (so it uses the Ti.UI.FILL as default)
I have noticed this too with a completely different implementation. I had just one view that I included in a window.
Apparently the left and top calculations were not done properly if the elements is hidden.
What I did to solve the issue is to hardcode the left/top position by calculating the left position using this:
$.content.left = (Ti.Platform.displayCaps.platformWidth - 75) / 2;
Where in my case 75 is the width the element has, so that'll be bigger in your case. You can do the same for height.
Now, this is an iOS only solution. On Android you will need to take DPI into consideration calculating it.
I do think it is a bug, though this solution works perfectly for me. I recommend looking at JIRA and see if it is a known issue, and if not, raise it with a very specific explanation of the problem, preferably with a reproducible case delivered as an app. Classic would help most. And if it is not reproducible in classic it might be an alloy issue.
I am having a strange problem. I scale an image and, while scaling works correctly, the image always gets clipped. I tried different scale types - things changed but I never could make it work.
Just to be clear, here's what I need to solve:
1. I have a HorizontalScrollView around the ImageView and a ScrollView around the HorizontalView.
2. I scroll around (using scrollTo of both scroll views) and, upon a certain event, zoom in.
3. What I'd like to happen is for the ImageView to scale around my current scroll position.
Here's the layout:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="none"
android:overScrollMode="never">
<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="none"
android:overScrollMode="never">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="3dp"
android:scaleType="fitCenter" />
</HorizontalScrollView>
</ScrollView>
</FrameLayout>
And here's the scaling code (originalWidth/originalHeight are calculated at scale of 1; targetView points to the ImageView):
public synchronized void changeScale(float newScaleFactor) {
this.scaleFactor = Math.max(min_zoom, Math.min(newScaleFactor, max_zoom));
if (targetView != null && originalWidth > 0) {
int newWidth = (int)(originalWidth * scaleFactor);
int newHeight = (int)(originalHeight * scaleFactor);
onScaleChanged(targetView, scaleFactor, newWidth, newHeight);
}
}
public void onScaleChanged(View targetView, float scaleFactor, int newWidth, int newHeight) {
ViewGroup.LayoutParams layoutParams = targetView.getLayoutParams();
layoutParams.width = newWidth;
layoutParams.height = newHeight;
// This is needed to increase the pane size (rather than zoom within the initial layout)
targetView.setLayoutParams(layoutParams);
// Tell the system to recalculate the layout
targetView.requestLayout();
// This is needed to specify the center of scaling
HorizontalScrollView horizontalScrollView = (HorizontalScrollView)targetView.getParent();
ScrollView vertScrollView = (ScrollView)horizontalScrollView.getParent();
// ~~~ the pivot points are probably wrong
targetView.setPivotX(horizontalScrollView.getScrollX() * scaleFactor);
targetView.setPivotY(vertScrollView.getScrollY() * scaleFactor);
// This is needed for actual zooming
targetView.setScaleX(scaleFactor);
targetView.setScaleY(scaleFactor);
};
public void zoomIn(float scaleDelta) {
changeScale(scaleFactor + scaleDelta);
}
public void zoomOut(float scaleDelta) {
changeScale(scaleFactor - scaleDelta);
}
Question 1: How do I prevent clipping? I can't find the right combination of scaleType and layout resizing.
Question 2: When I use setScaleX/setScaleY, should my pivot be calculated after applying the new scale factor or does the renderer take care of that automatically?
After updating the scale you need to invalidate(), and requestLayout() the views.
targetView.invalidate();
targetView.requestLayout();
I usually calculate the scale differently for images. You could try to scale the image view using the MATRIX scale type. You will need to know the size of your bound DPI.
// Get the scaled DPI
int boundBoxInDp = getResources().getDisplayMetrics().densityDpi * scaleFactor
// Determine how much to scale: the dimension requiring less scaling is
// closer to its side. This way the image always stays inside your
// bound AND either x/y axis touches the bound but neither will go over.
float xScale = ((float) boundBoxInDp) / newWidth;
float yScale = ((float) boundBoxInDp) / newHeight;
float scale = (xScale <= yScale) ? xScale : yScale;
// scale using our calculated scale
targetView.setScaleX(scale);
targetView.setScaleY(scale);
As to your second question about the Pivot. That will probably need to be set to the center of the visible scroll area. The the scrollable area should be increased when you change the image size since you are using FIT_CENTER;
See this article for code that works quite well. I ditched my HorizontalScrollView and ScrollView and attached this PanAndZoomListener to my FrameLayout and from then on it was all rainbows and unicorns.
I tried to do the same thing as you, but without success. It would seem that you can scroll an ImageView without using HorizontalScrollView and ScrollView. I'm still unclear as to what makes it happen, but I'm leaning toward the use of the image matrix (as in, setImageMatrix on ImageView) or possibly the use of MarginLayoutParams. In looking at the Gallery source code from the picture Gallery available on the Android, I'm seeing heavy use of Matrix. Unfortunately, the documentation on this seems to be quite light in my estimation.
Other people have figured it out, so plug in the PanAndZoomListener and you're done. That's what I did.
I have a very dark background to my grids so I need to make all the markings in the headers white. I've been able to do that w/everything except the arrow icons in the AdvancedDataGrid. http://flexvenom.wordpress.com/2007/12/04/howto-setting-a-custom-sortitemrenderer-to-the-advanceddatagrid/ has a solution, but then it kills the sort sequence number. How can I just make everything in the header (except the background) white?
I'm running the 3.5 SDK.
In the end it was a lot simpler than what I was trying (having an item renderer draw the whole sort part) but it was still pretty complicated, in that I had to make 2 very light-weight renderers. I could have just had the sequence number just stay one colour, but decided to get a little fancy and have a mouse over colour and a normal colour.
The header renderer (ubicAdvancedDataGridHeaderRenderer.mxml) is:
<?xml version="1.0" encoding="utf-8"?>
<mx:AdvancedDataGridHeaderRenderer xmlns:mx="http://www.adobe.com/2006/mxml"
color="{ColourGlobals.TEXT_AGAINST_DARK}"
mouseOver="bMouseOver = true" mouseOut="bMouseOver = false">
<mx:Script><![CDATA[
public var bMouseOver:Boolean = false;
]]></mx:Script>
</mx:AdvancedDataGridHeaderRenderer>
then the sort renderer is:
package assets.GridTools {
import mx.controls.advancedDataGridClasses.AdvancedDataGridSortItemRenderer;
public final class ubiAdvancedDataGridSortItemRenderer extends AdvancedDataGridSortItemRenderer {
override protected function commitProperties():void {
super.commitProperties();
const oHeader:ubicAdvancedDataGridHeaderRenderer = owner as ubicAdvancedDataGridHeaderRenderer;
label.textColor = oHeader.bMouseOver ? ColourGlobals.DARK : ColourGlobals.TEXT_AGAINST_DARK;
}
}
}
Don't ask my why I made one Flex and the other AS. It works, so not being broke, I ain't gonna fix it.