I noticed that sometimes the content pane changes its size after initialisation and was wondering why that would be.
Here's some code to demonstrate this in the simulator - run it in the simulator and watch the output - smaller skins have smaller differences:
public class FormScrollExtras extends Form {
private int entrytally = 0;
private Runnable runnableLog = null;
public FormScrollExtras() {
setTitle("FormScrollExtras");
setScrollable(false);
setTensileDragEnabled(false);
Container contentPane = getContentPane();
contentPane.setLayout(new BoxLayout(BoxLayout.Y_AXIS));
contentPane.setScrollableY(true);
contentPane.setTensileDragEnabled(false);
TextArea textArea = new TextArea("This form logs the content panes height and layoutHeight - right after init and whenever an entry is created.");
textArea.setEditable(false);
contentPane.add(textArea);
FloatingActionButton floatingActionButton = FloatingActionButton.createFAB(FontImage2.MATERIAL_ADD);
floatingActionButton.bindFabToContainer(getContentPane(), Component.RIGHT, Component.BOTTOM);
floatingActionButton.addActionListener((e) -> newEntry(contentPane));
contentPane.getParent().layoutContainer(); // Doesn't make a difference
runnableLog = () -> {
Log.p("x/y, height/layoutHeight: " +
contentPane.getAbsoluteX() + "/" + contentPane.getAbsoluteY() + ", " +
contentPane.getHeight() + "/" + contentPane.getLayoutHeight());
};
runnableLog.run();
}
private void newEntry(Container contentPane) {
contentPane.add(new Label("" + ++entrytally));
runnableLog.run();
contentPane.repaint();
}
}
Without the numbers related to the height change it's hard to guess but you can verify this by looking if this only happens for iOS skins or only for Android skins.
I'm guessing it would be because of the status bar space that is reserved in iOS and impacts the toolbar height. Notice that during construction elements aren't laid out yet so there is no size guarantee at this stage. There are several callbacks that are more deterministic such as initComponent() or laidOut().
Related
I have an Android application that uses Back Command to go back to the start screen.
The start screen has a label with a number inside, that I want to update when the back command is used.
I could figure out a solution with the code inside the back command, but I don't know if my approach is the best, since the ClassOne gets sort of loaded twice.
Here is the code I already have:
public class ClassOne {
public ClassOne(ClassPojo classPojo) {
// I want to change the text of this label when calling the back command
labelOne.setText(classPojo.getStringTest());
formOne.show();
}
}
public class ClassTwo {
public ClassTwo(Form a , ClassPojo classPojo) {
Command back = new Command("A") {
#Override
public void actionPerformed(ActionEvent evt) {
// I am adding the new value for the label here inside the back command
classPojo.setStringTest("testing");
a.showBack();
new ClassOne(classPojo);
}
};
formTwo.setBackCommand(back);
}
I'm not sure what the problem is, your example is a bit generic. However, a complete minimal example where the startScreen form instance is not recreated is this one:
Form startScreen = new Form("Start screen", BoxLayout.y());
Wrapper<Integer> count = new Wrapper<>(1);
Label numberLabel = new Label(count.get() + "");
Button button1 = new Button("Go to Form 2");
startScreen.addAll(numberLabel, button1);
startScreen.show();
button1.addActionListener(l -> {
Form form2 = new Form("Form 2", BoxLayout.y());
Label label = new Label("Use the back button");
form2.add(label);
form2.getToolbar().setBackCommand("Back", Toolbar.BackCommandPolicy.ALWAYS, ll -> {
count.set(count.get() + 1);
numberLabel.setText(count.get() + "");
startScreen.showBack();
});
form2.show();
});
If you don't even want to recreate the form2 instance, then you can do so:
Form startScreen = new Form("Start screen", BoxLayout.y());
Wrapper<Integer> count = new Wrapper<>(1);
Label numberLabel = new Label(count.get() + "");
Button button1 = new Button("Go to Form 2");
startScreen.addAll(numberLabel, button1);
startScreen.show();
Form form2 = new Form("Form 2", BoxLayout.y());
Label label = new Label("Use the back button");
form2.add(label);
form2.getToolbar().setBackCommand("Back", Toolbar.BackCommandPolicy.ALWAYS, ll -> {
count.set(count.get() + 1);
numberLabel.setText(count.get() + "");
startScreen.showBack();
});
button1.addActionListener(l -> {
form2.show();
});
In my opinion, whether or not to recreate the instances of a Form should be evaluated on a case-by-case basis. Among the variables between taking into consideration, according to my modest opinion, there is also the readability of the code and what it does, especially in complex cases.
The overhead of recreating a form instance is negligible so that wouldn't be a problem but in recent years we try to reuse form instances more. Not because of the performance.
The benefit is in minor behaviors e.g. scroll position within the form. These are very hard to replicate.
During testing, I found an easy solution that is adding the label to the constructor. I hope this snippet can be helpful.
public ClassTwo(Form a, ClassPojo classPojo, Label label) {
Command back = new Command("A") {
#Override
public void actionPerformed(ActionEvent evt) {
label.setText(classPojo.getStringTest());
a.showBack();
}
};
I created a code that works, but I'm not sure that it's the best way to place an Image scaled automatically to the available width space. I need to put some content over that image, so I have a LayeredLayout: in the first layer there is the Label created with the following code, on the second layer there is a BorderLayout that has the same size of the Image.
Is the following code fine or is it possible to do better?
Label background = new Label(" ", "NoMarginNoPadding") {
boolean onlyOneTime = false;
#Override
public void paint(Graphics g) {
int labelWidth = this.getWidth();
int labelHeight = labelWidth * bgImage.getHeight() / bgImage.getWidth();
this.setPreferredH(labelHeight);
if (!onlyOneTime) {
onlyOneTime = true;
this.getParent().revalidate();
}
super.paint(g);
}
};
background.getAllStyles().setBackgroundType(Style.BACKGROUND_IMAGE_SCALED_FIT);
background.getAllStyles().setBgImage(bgImage);
Shorter code:
ScaleImageLabel sl = new ScaleImageLabel(bgImage);
sl.setUIID("Container");
You shouldn't override paint to set the preferred size. You should have overriden calcPreferredSize(). For ScaleImageLabel it's already set to the natural size of the image which should be pretty big.
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions asking for code must demonstrate a minimal understanding of the problem being solved. Include attempted solutions, why they didn't work, and the expected results. See also: Stack Overflow question checklist
Closed 9 years ago.
Improve this question
I'm new to Flash and intermediate in HTML/CSS. I would like to create a grid of buttons that when you hover over them, they change color and show a linkable caption/tooltip on hover. I would also like this to be dynamic as possible as I will need a grid of 2000+ buttons/squares that may have to be updated from time to time.
Here is an example created with HTML/CSS and some JS
//<![CDATA[
$(window).load(function(){
// Create the tooltips only when document ready
$(document).ready(function () {
// This will automatically grab the 'title' attribute and replace
// the regular browser tooltips for all <a> elements with a title attribute!
$('a[title]').qtip();
});
});//]]>
I need to know the best way to build this in flash, and would really appreciate a barebones breakdown of the code/AS. I am somewhat new to flash and this is just way above my head.
Thanks in advance.
Ok, so you want grid of small bitmaps/shapes on stage. I would recommend bitmaps since they work better when you want to transform something - but if the grid will be standing still, then you can use Sprite filled with color. Below you can find a snippet complete example. You can compile it using mxmlc compiler or using Flash Develop||FDT, or connecting this file as a document class in Flash IDE.
It's the most efficient way I can imagine. We have only one Sprite and we color some parts of it depending on where our mouse is. Hope this helps.
package
{
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.Point;
/**
* ...
* #author
*/
public class Main extends Sprite
{
private var color:int = 0xf2f2f2;
private var color1:int = 0xff000;//colors definition
private var size:int = 12;//size of our square
private var container:Sprite;//container definition
private var rows:int = 48;//number of rows
private var margin:int = 2;//margin on each side
private var count:int = 2000;//number of elements
private var totalspace:int = size + margin;
private var currentPoint:Point = new Point();
private var lastPoint:Point = null;
public function Main():void
{
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event = null):void
{
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.align = StageAlign.TOP_LEFT;
removeEventListener(Event.ADDED_TO_STAGE, init);
// entry point
container = new Sprite();//define squares container;
container.graphics.beginFill(color);//define fill color;
var posX:int;
var posY:int;
for ( var i:int = 0; i < count; i++) {
posX = int(i % rows) * totalspace;//calculate x axis position based on modulo
posY = int(i / rows) * totalspace;//calculate y axis position
container.graphics.drawRect( posX, posY, size, size );//call drawRect method on graphics object.
}
addChild(container);//add container to stage - meaning it will be visible, because flash is rendering it.
container.buttonMode = true;//set mouse to button mode
container.addEventListener( MouseEvent.MOUSE_MOVE, onMM );
container.addEventListener( MouseEvent.ROLL_OUT, onMO );
//add eventListener for mouse move - so each time you move a mouse over container object it will trigger an event.
}
private function onMO(e:MouseEvent):void
{
currentPoint = null;
}
private function onMM(e:MouseEvent):void
{
if ( lastPoint == currentPoint ) {
//if moouse is on the same square exit this function
return;
}
//calculate x and y position of square we need to color
currentPoint = new Point( int( container.mouseX/totalspace ) * totalspace, int(container.mouseY/totalspace) * totalspace );
container.graphics.beginFill( color1 );
container.graphics.drawRect( currentPoint.x, currentPoint.y, size, size );
if ( lastPoint ) {
//if last point is present, make it grey, as for roll out
container.graphics.beginFill( color );
container.graphics.drawRect( lastPoint.x, lastPoint.y, size, size );
}
lastPoint = currentPoint;
}
}
}
I've got a custom (and getting complex) TabControl. It's a gathering of many sources, plus my own wanted features. In it is a custom Panel to show the headers of the TabControl. Its features are to compress the size of the TabItems until they reached their minimum, and then activates scrolling features (in the Panel, again). There is also another custom panel to hold a single button, that renders on the right of the TabItems (it's a "new tab" button).
It all works great, until I try to animate the scrolling.
Here are some relevant snippets :
In the CustomTabPanel (C#, overriding Panel and implementing IScrollInfo):
private readonly TranslateTransform _translateTransform = new TranslateTransform();
public void LineLeft()
{
FirstVisibleIndex++;
var offset = HorizontalOffset + _childRects[0].Width;
if (offset < 0 || _viewPort.Width >= _extent.Width)
offset = 0;
else
{
if (offset + _viewPort.Width > _extent.Width)
offset = _extent.Width - _viewPort.Width;
}
_offset.X = offset;
if (_scrollOwner != null)
_scrollOwner.InvalidateScrollInfo();
//Animate the new offset
var aScrollAnimation = new DoubleAnimation(_translateTransform.X, -offset,
new Duration(this.AnimationTimeSpan), FillBehavior.HoldEnd) { AccelerationRatio = 0.5, DecelerationRatio = 0.5 };
aScrollAnimation.Completed += ScrollAnimationCompleted;
_translateTransform.BeginAnimation(TranslateTransform.XProperty, aScrollAnimation , HandoffBehavior.SnapshotAndReplace);
//End of animation
// These lines are the only ones needed if we remove the animation
//_translateTransform.X = -offset;
//InvalidateMeasure();
}
void ScrollAnimationCompleted(object sender, EventArgs e)
{
InvalidateMeasure();
}
the _translateTransform is initialized in the constructor :
base.RenderTransform = _translateTransform;
Again, everything is fine if I remove the animation part and just replace it with the commented out lines at the end.
I must also point out that the problem is NOT with the animation itself. That part works out well. The problem is about when I remove some tab items : all the layout then screws up. The TranslateTransformation seems to hold on some wrong value, or something.
Thanks in advance.
Well. As it's often the case, I kept working on the thing, and... answered myself.
Could still be useful for other people, so here was the catch. In the line :
var aScrollAnimation = new DoubleAnimation(_translateTransform.X, -offset, new Duration(this.AnimationTimeSpan), FillBehavior.HoldEnd)
{ AccelerationRatio = 0.5, DecelerationRatio = 0.5 };
the FillBehavior should have been FillBehavior.Stop.
As easy as that!
I want to show my window on top of the TaskBar's clock when the windows starts.
How can I find the bottom right corner location of my desktop?
I use this code that works well in windows forms app but does not work correctly in WPF:
var desktopWorkingArea = System.Windows.Forms.Screen.PrimaryScreen.WorkingArea;
this.Left = desktopWorkingArea.Right - this.Width;
this.Top = desktopWorkingArea.Bottom - this.Height;
This code works for me in WPF both with Display 100% and 125%
private void Window_Loaded(object sender, RoutedEventArgs e)
{
var desktopWorkingArea = System.Windows.SystemParameters.WorkArea;
this.Left = desktopWorkingArea.Right - this.Width;
this.Top = desktopWorkingArea.Bottom - this.Height;
}
In brief I use
System.Windows.SystemParameters.WorkArea
instead of
System.Windows.Forms.Screen.PrimaryScreen.WorkingArea
To access the desktop rectangle, you could use the Screen class - Screen.PrimaryScreen.WorkingArea property is the rectangle of your desktop.
Your WPF window has Top and Left properties as well as Width and Height, so you could set those properties relative to the desktop location.
You can use the window's SizeChanged event instead of Loaded if you want the window to stay in the corner when its size changes. This is especially handy if the window has Window.SizeToContent set to some value other than SizeToContent.Manual; in this case it will adjust to fit the content while staying in the corner.
public MyWindow()
{
SizeChanged += (o, e) =>
{
var r = SystemParameters.WorkArea;
Left = r.Right - ActualWidth;
Top = r.Bottom - ActualHeight;
};
InitializeComponent();
}
Note also that you should subtract ActualWidth and ActualHeight (instead of Width and Height as shown in some other replies) to handle more possible situations, for example switching between SizeToContent modes at runtime.
My code:
MainWindow.WindowStartupLocation = WindowStartupLocation.Manual;
MainWindow.Loaded += (s, a) =>
{
MainWindow.Height = SystemParameters.WorkArea.Height;
MainWindow.Width = SystemParameters.WorkArea.Width;
MainWindow.SetLeft(SystemParameters.WorkArea.Location.X);
MainWindow.SetTop(SystemParameters.WorkArea.Location.Y);
};
I solved this problem with a new window containing a label named MessageDisplay. The code accompanying the window was as follows:
public partial class StatusWindow : Window
{
static StatusWindow display;
public StatusWindow()
{
InitializeComponent();
}
static public void DisplayMessage( Window parent, string message )
{
if ( display != null )
ClearMessage();
display = new StatusWindow();
display.Top = parent.Top + 100;
display.Left = parent.Left + 10;
display.MessageDisplay.Content = message;
display.Show();
}
static public void ClearMessage()
{
display.Close();
display = null;
}
}
For my application, the setting of top and left puts this window below the menu on the main window (passed to DisplayMessage in the first parameter);
This above solutions did not entirely work for my window - it was too low and the bottom part of the window was beneath the taskbar and below the desktop workspace. I needed to set the position after the window content had been rendered:
private void Window_ContentRendered(object sender, EventArgs e)
{
var desktopWorkingArea = System.Windows.SystemParameters.WorkArea;
this.Left = desktopWorkingArea.Right - this.Width - 5;
this.Top = desktopWorkingArea.Bottom - this.Height - 5;
}
Also, part of the frame was out of view, so I had to adjust by 5. Not sure why this is needed in my situation.
#Klaus78 's answer is correct. But since this is first thing google pops up and if working in environments where screen resolution can change often such that your app runs on virtual desktops or virtual servers and you still need it to update its placement when the screen resolution changes I have found linking to the SystemEvents.DisplaySettingsChanged event to be beneficial. Here is an example using rx and you can put this in your constructor for your view.
Observable
.FromEventPattern<EventHandler, EventArgs>(_ => SystemEvents.DisplaySettingsChanged += _, _ => SystemEvents.DisplaySettingsChanged -= _)
.Select(_ => SystemParameters.WorkArea)
.Do(_ =>
{
Left = _.Right - Width;
Top = _.Bottom - Height;
})
.Subscribe();