Vaadin - which components use to organize layout - layout

I am confused with the possibilities of layouting components in Vaadin 7 - intuition tells to use just layout - however there are also Panels or Components which can be useful.
I need to create view which will have static left menu and top bar (constant size, visible on each page). The only thing that will change the content is middle component - which should be scrollable.
My design therefore is:
Custom component, which assembles everything. Is has absolute size for top and left menu.
Vertical layout for left menu and Horizontal layout for upper menu
Panel for middle component, since Panel is supposed to support scrolling.
Inside of panel I placed CssLayout with content that doesn't fit the page. Expected behaviour is that there would be a scroll bar (preferably on the browser, but i guess it'd be on the panel). But What actually happens is that just the part that fits the screen is visible and the rest is cut. CssLayout is sizeFull and panel default, but I tried also other combinations and nothing has helped.
How can I combine layouts to have middle panel scrollable (if needed) and top and left one with absolute size?
Thanks a lot,

In Vaadin 7 & 8, you generally use one of the Layout implementations to arrange widgets within a UI subclass. For example, VerticalLayout, HorizontalLayout, and GridLayout. For pixel-positioning, use AbsoluteLayout. Of course these can be nested one within another. But don't go crazy with the nesting as it may result in overly complex HTML/CSS at runtime.
The Panel class, as the doc says, is a special container. It contains only a single component, typically another container such as a Layout. The Panel presents itself with a border and a title bar displaying its caption property. This looks something like a sub-window.
As you mentioned, a Panel supports scrolling. But keep in mind that any web page defined with a Layout also supports scrolling, obviously.
Here is a complete example app showing a stack of TextField widgets in a VerticalLayout.
package com.basilbourque.example;
import com.vaadin.annotations.Theme;
import com.vaadin.annotations.VaadinServletConfiguration;
import com.vaadin.server.VaadinRequest;
import com.vaadin.server.VaadinServlet;
import com.vaadin.ui.TextField;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;
import javax.servlet.annotation.WebServlet;
import java.time.Instant;
/**
* This UI is the application entry point. A UI may either represent a browser window
* (or tab) or some part of an HTML page where a Vaadin application is embedded.
* <p>
* The UI is initialized using {#link #init(VaadinRequest)}. This method is intended to be
* overridden to add component to the user interface and initialize non-component functionality.
*/
#Theme ( "mytheme" )
public class MyUI extends UI {
#Override
protected void init ( VaadinRequest vaadinRequest ) {
final VerticalLayout layout = new VerticalLayout();
for ( int i = 1 ; i <= 100 ; i++ ) {
TextField textField = new TextField( String.format( "%3d" , i ) );
textField.setWidth( 17 , Unit.EM ); // Widen the field to fully display our contents.
textField.setValue( Instant.now().toString() );
layout.addComponents( textField );
}
setContent( layout );
}
#WebServlet ( urlPatterns = "/*", name = "MyUIServlet", asyncSupported = true )
#VaadinServletConfiguration ( ui = MyUI.class, productionMode = false )
public static class MyUIServlet extends VaadinServlet {
}
}
As to your idea of nesting VerticalLayout and HorizontalLayout for nav panels, button/tool bars, and such…
Vaadin 7 & 8: Absolutely proper, and a good idea. Sometimes a GridLayout makes sense for that as well.
Vaadin 10: May also be a good idea. Or you may want to explore using new CSS3 features flexbox and Grid Layout.
As for your problem with CssLayout inside a Panel, I have rarely used either. I suspect the problem would lie in not properly defining a specific width & height of the CssLayout. You did not post details and code, so we can only speculate.

Related

How disable JavaFX Text

How disable JavaFX Text
Text t = new Text();
t.setText("This is a text sample");
t.setDisable(true); //it does not work
Solution
You can style Text visually, so that it looks disabled, if you wish. The solution below binds the opacity setting to the disabled setting of the text.
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class DisabledText extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) {
Text enabledText = new Text("Enabled");
Text disabledText = new Text("Disabled");
disabledText.opacityProperty().bind(
Bindings.when(
disabledText.disabledProperty())
.then(0.4)
.otherwise(1)
);
disabledText.setDisable(true);
VBox layout = new VBox(10, enabledText, disabledText);
layout.setPadding(new Insets(10));
stage.setScene(new Scene(layout));
stage.show();
}
}
Notes
You might be able to accomplish this through CSS as well rather than a binding, I just didn't try that option.
In general, many times you are usually better off using a Label then Text for a lot of things as it includes more flexibility and functionality.
Background
Even though the user can't interact directly with text, so disabling text really has no effect, I guess what you are looking for is that the text look like other things when they are disabled.
Normally what happens when a control such as menu item or button is disabled, is that the CSS pseudo-class :disabled is set on the control. When this occurs, the default modena.css stylesheet for JavaFX 8 will modify the opacity for the control to -fx-opacity: 0.4;. This changes the visual look for the control so that it appears as though it is grayed out, as the low opacity makes the control looks faded.
Text is built to be a base drawing shape. Base drawing shapes in Java deliberately don't rely on CSS (though CSS can be used with them if needed - see the CSS reference guide for info on what default CSS rules apply to shapes). If your scene doesn't use any layout panes or controls, then the standard modena.css stylesheet will not be loaded and CSS processing will not be applied to the scene (for efficiency reasons so that, for instance, you could write a high performance game that did not use CSS processing). So CSS processing for Text is not necessarily required and, if used, is limited in its scope.
Two possible ways:
Add css style to the Text's instance.
Use Label instead.
I recommend using Label instead of Text.

Customizing Xamarin.Forms layouts

I'm building an Android and iOS app using Xamarin Forms.
What I'm simply trying to do is set a background drawable on my Android app for my ListView items. The root view of my ListView items are StackLayout's:
var listView = new ListView
{
ItemsSource = items,
ItemTemplate = new DataTemplate(() =>
{
return new ViewCell
{
View = new StackLayout(...)
};
}
};
I know I can access the native element by using a custom renderer:
public class MyEntryRenderer : EntryRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged(e);
if (e.OldElement == null) {
var nativeEditText = (EditText)Control;
...
}
}
}
But I'm not sure how this would work for a StackLayout (or any other layout for that matter).
I first extended StackLayout:
public class ListViewItem : StackLayout
{
}
And I read somewhere that layouts use the VisualElementRenderer, so I tried the following:
public class ListViewItemRenderer : VisualElementRenderer<StackLayout>
{
protected override void OnElementChanged(ElementChangedEventArgs<StackLayout> e)
{
base.OnElementChanged(e);
// any way to access the native element?
}
}
But VisualElementRenderer does not seem to give me access to the native element.
So is there any way I can access the native elements of Layout elements? Or maybe there is a different way to simply set a background drawable on layouts within my Android app?
Even though I still don't know how to access the native element of a layout, the VisualElementRenderer has a method for setting the background drawable on Android (which was exactly what I needed). So I ended up with the following:
protected override void OnElementChanged(ElementChangedEventArgs<StackLayout> e)
{
base.OnElementChanged(e);
SetBackgroundDrawable(Resources.GetDrawable(Resource.Drawable.listViewItem));
}
I understand you want to hook into an existing Layout renderer and extending it to access the native element with extra capabilities like background image.
Eventually the support for background-image will be supported just like background-colour is, I imagine, across the Layout controls. It may be worth while waiting for this as I can't see why they wouldn't implement these in a later release.
In the mean time you would need something that would work and is quite easy to implement?
Creating the background drawable via inheriting the renderer from a Layout may not be the simplest of solutions therefore, although does have its advantages as you can then re-use easily with the extra functionality across all Layouts for an application.
In your code for ListViewItemRenderer, however, it is inheriting from a Xamarin.Forms control (you specified StackLayout) and have not specified a native, platform dependent, control to be the base for the layout control that would have to match the Xamarin.Forms platform dependent control used.
Each Renderer is tied to a native element. Layout controls will be no different than other custom native control renderers.
For a custom control, you will write a renderer something like the following (note I haven't specified a layout renderer as I haven't had a need to do this yet and am just going from past experience - but similar rules should apply to implementing a renderer for a layout as opposed to a custom control):-
// System.Windows.Controls.Grid in this case is the root native control for a WindowsPhone renderer of MyControl
public class MyControlRenderer : ViewRenderer<MyControlView, System.Windows.Controls.Grid>
There is a simpler approach, however to achieve what you want to do:-
The simpler approach would be instead of inheriting from the Stack Layout control, it would be better to inherit from Grid as the root of the control.
Then you can add an Image control to the Grid and also a Stack Layout for the same Grid Row and Column.
By doing the above you will be able to achieve a background-image across the entire listview item row.

Text line height

How can I specify a line height in a multi-line Text / Label?
It already seems it's not supported in css:
JavaFX CSS does not support comma-separated series of font family
names in the -fx-font-family property. The optional line height
parameter when specifying fonts is not supported. There is no
equivalent for the font-variant property.
Is there a reason for this?
Is there an alternative to set this in code? Or kinda emulate the functionality? I want to control vertical rhythm within my app.
Java 8+ implementation
RT-21683 Allow to change line-to-line spacing was implemented to add a line spacing feature to JavaFX Java 8.
The line spacibg API is defined on Text, TextFlow and Labeled nodes via a lineSpacing property and associated getters and setters. Spacing is defined in pixels, like the rest of JavaFX layout.
Java 2.2- implementation
If you cannot use Java 8+, you will need to implement line spacing yourself, e.g. By setting spacing on a VBox with separate Text nodes for each line.
Discussion of unit measurements in JavaFX
It seems that there is a corresponding (stylable) css property? If it's defined in pixels, how do I make it dpi aware
css properties work can work with em units if you use them. See for example the default modena.css stylesheet which measures almost all sizes in em units. This is why if you are just using default styles without additional pixel based layout, then if you change the root font size, everything in your layout scales automatically.
It's only the Java APIs and FXML which work with only pixel units. Even then, the JavaFX system is clever enough to know (at least on some machines), that it is running on a HiDPI display (e.g. OS X retina) so that, for example, pixel units are automatically doubled for the retina display (in Java 8).
If you are using FXML, you could use expression bindings as a way to define your own unit system.
Future versions of JavaFX may provide more support for RT-14020 Concept for a screen resolution independent definition of a GUI.
Sample Code
The sample below uses the Java 8 to demonstrate usage of the lineSpacing api and -fx-line-spacing css attribute.
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.stage.Stage;
public class LineSpacing extends Application {
public static void main(String[] args) { launch(LineSpacing.class); }
#Override public void start(Stage stage) {
Label label = new Label(
"Power is my mistress.\n" +
"I have worked too hard at her conquest " +
"to allow anyone to take her away from me.\n" +
" - Napoleon Bonaparte");
label.setPadding(new Insets(20));
// as the default font spacing in JavaFX is 13 points,
// all of the following lines will provide the same results
// (double spacing the label text).
//
// label.setLineSpacing(13);
// label.setStyle("-fx-line-spacing: 13px;");
label.setStyle("-fx-line-spacing: 1em;");
stage.setScene(new Scene(label));
stage.show();
}
}

Hide Controls At Design-Time [duplicate]

I need to handle multiple panels, containing variuous data masks. Each panel shall be visible using a TreeView control.
At this time, I handle the panels visibility manually, by making the selected one visible and bring it on top.
Actually this is not much confortable, especially in the UI designer, since when I add a brand new panel I have to resize every panel and then design it...
A good solution would be using a TabControl, and each panel is contained in a TabPage. But I cannot find any way to hide the TabControl buttons, since I already have a TreeView for selecting items.
Another solution would be an ipotethic "StackPanelControl", where the Panels are arranged using a stack, but I couldn't find it anywhere.
What's the best solution to handle this kind of UI?
You need a wee bit of Win32 API magic. The tab control sends the TCM_ADJUSTRECT message to allow the app to adjust the tab size. Add a new class to your project and paste the code shown below. Compile. Drop the new control from the top of the toolbox onto your form.
You'll get the tabs at design time so you can easily switch between pages. The tabs are hidden at runtime, use the SelectedIndex or SelectedTab property to switch between "views".
using System;
using System.Windows.Forms;
class StackPanel : TabControl {
protected override void WndProc(ref Message m) {
// Hide tabs by trapping the TCM_ADJUSTRECT message
if (m.Msg == 0x1328 && !DesignMode) m.Result = (IntPtr)1;
else base.WndProc(ref m);
}
}
A good solution would be using a TabControl, and each panel is contained in a TabPage.
But I cannot find any way to hide the TabControl buttons, since I already have a
TreeView for selecting items.
For the above,
You need to set the following properties of TabControl.
tabControl.Multiline = true;
tabControl.Appearance = TabAppearance.Buttons;
tabControl.ItemSize = new System.Drawing.Size(0, 1);
tabControl.SizeMode = TabSizeMode.Fixed;
tabControl.TabStop = false;

iPhone alike sliding header, or: how to force immediate redraw on top margin change?

I am trying to implement a similar effect like the iPhone-alike sliding header in the iPhone contact app (the sliding header that group the contacts by it's starting letter).
This is the screen of my app, and what I want to achieve is the following:
I have a 'guide header' and three 'tabs' for sorting the list. When the user scrolls the list up, I want everything to scroll up (guide header, tabs, list). However, when the tabs reach the top of the screen (and the guide header will just be gone off the screen), I want the tabs to stop and stay there (remain as "sticky header"), and only the list items scroll as in any regular list view.
I have a view group (guide header) above a list view.
First of all, I want to have the guide header adjust it's position depending on the scrolling position of the list view.
First approach:
My idea was to set an onScrollListener to the list view and change the top margin of the guide header to whatever the scroll position of the first item in the list view is (which would be a negative value).
The logic is correct, but the problem I'm facing is that the guide header view doesn't get redrawn fast enough while I'm scrolling in the list view. The guide header view only updates (to my changed top margin value) when the list view fling comes to an end. Even slow scrolling doesn't work. Invalidating (invalidate()) the guide header view or it's parent also doesn't help, since it would just put an invalidation request to the queue, but the invalidation and redrawing doesn't happen immediately, but only when the UI thread becomes idle, which doesn't seem to happen while the user still has his fingers on the scroll list view. Seems that flinging the list view blocks the whole UI thread or keeps it busy for itself.
So the main problem is: changing the margin of the guide header view doesn't become visible immediately while the user is scrolling the list view. The code I'm using it this:
#Override
public void onScroll(final AbsListView view, final int firstVisibleItem,
final int visibleItemCount, final int totalItemCount) {
// Get the first list item and check it's scroll position. This will be the value (top), that we also
// use the scroll the header parallel.
View v = mainList.getChildAt(0);
final int top = (v==null)?0:v.getTop();
// This logs the current scroll position of the first list item element/view group.
Log.d("onScroll", "onScroll: " + top);
// Here we finally change the margin (setting a negative margin) to the header element.
((LinearLayout.LayoutParams)(findViewById(R.id.header_container).getLayoutParams())).setMargins(0, top, 0, 0);
// was just a test: invalidating the outer container/view group, doesn't help
// findViewById(R.id.ll_container).invalidate();
}
I do see the "onScroll:" log output I inserted in the code above in the logcat, but the following adjustment of the top margin just doesn't become visible.
My second approach: is to use a scrollview for the guide header + tabs and work with those. Scrolling the guide header (which is then a scroll view) from code with scrollView.scrollTo(0,Math.abs(Math.abs(top)) from the onScroll method of the list view does work and almost immediately shows on the screen, however, it's not very accurate/stable when the user flings the list view very fast - meaning it jumps in intervals and doesn't look smooth; it's only accurate/stable when scrolling slowly.
My question is now: is there any best practice to accomplish such a sliding header effect, and more concrete: is there a way to force the guide header view to be redrawn while the user is still scrolling the list view (in my first mentioned approach).
For this you should use some tricks (afaik there is no ready-to-use implementation of such a feature).
For instance, you could detect gestures on your view, and
if the current gesture matches a
scroll down, and the first list item
is visible, animate-shrink the
header's size to 0, the tab view's
size to match_parent. Start scrolling
the list only when the header is not
present anymore.
if the current gesture matches scroll
up, and the first is already visible,
animate-expand the header to it's
original size.
So using Animation on the header view might be your solution.
Update
An other workaround would be to extend your List (the value array of your adapter):
Inster a new (dummy) item at the top for the header representation, and modify your ListAdapter's getView method:
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
if (position == 0)
{
convertView = inflater.inflate(R.layout.sliding_header, parent,
false);
return convertView;
}
//TODO: your original method body comes here
}
where the xml referenced by R.layout.sliding_header should contain the header layout of your list.
A custom OnScrollListener implementation applied to the ListView would make unnoticeable that the header actually is an item of the list, since it would hide the scrollbar.
You should add this listener to your listView in the activity's onCreate method:
listView.setOnScrollListener(new MyScrollListener());
where MyScrollListener is:
/**
* Custom OnScrollListener
*/
private final class MyScrollListener implements OnScrollListener
{
#Override
public void onScrollStateChanged(AbsListView view, int scrollState)
{}
#Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount)
{
if (view.getFirstVisiblePosition() == 0)
view.setVerticalScrollBarEnabled(false);
else if (!view.isVerticalScrollBarEnabled())
view.setVerticalScrollBarEnabled(true);
}
}
I think you can also try and use my ExpandAnimation for that.
http://udinic.wordpress.com/2011/09/03/expanding-listview-items/
Just pass the animation class that "guide header" view, and let the animation do the work for you, no scrolling is needed in that case, and it's smooth.

Resources