I want to resize a UIView (e.g. TextField, UIGridView, ...) after the keyboard appears. Getting the appropriate events isn't the issue. I really stock by calculating the delta to shrink my view depending on the height of the keyboard.
If I have my iPad in front of me in portrait mode all calculations happens as expected. But If my device is in landscape mode or upside down, I'm really struggling getting the right dimensions! Some view frames/bounds aren't converted (in landscape mode is the height still higher then its width). Other views are translated depending if I've started my app already in landscape mode. The next challenge is working with consistent size measures. Sometimes the dimensions are like the real pixels (for a new iPad 2048) sometimes the scale factor isn't considered (for a new iPad 1024).
Any other idea how to the the correct frame size? Or are there any helpers which recognise the current orientation and provides me with the effective sizes?
My current code to achieve my expected behaviour is like this:
MyViewController
public override void ViewWillAppear(bool animated)
{
base.ViewWillAppear(animated);
// My code...
AppDelegate.CurrentDelegate.KeyboardAppeared += KeyboardAppeared;
AppDelegate.CurrentDelegate.KeyboardDisappeared += KeyboardDisappeared;
}
public override void ViewWillDisappear(bool animated)
{
base.ViewWillDisappear(animated);
AppDelegate.CurrentDelegate.KeyboardAppeared -= KeyboardAppeared;
AppDelegate.CurrentDelegate.KeyboardDisappeared -= KeyboardDisappeared;
}
private void KeyboardAppeared(object sender, KeyboardEventArgs e)
{
if (_keyboardButton != null)
_keyboardButton.Enabled = true;
var frame = LayoutHelper.RectangleForKeyboardAppearance(_textField, View.Window, e.Height);
LayoutHelper.ResizeViewForKeyboard(_textField, frame);
}
private void KeyboardDisappeared(object sender, EventArgs e)
{
if (_keyboardButton != null)
_keyboardButton.Enabled = false;
var frame = View.Bounds;
LayoutHelper.ResizeViewForKeyboard(_textField, frame);
}
MyHelperClass
public static RectangleF RectangleForKeyboardAppearance(UIView view, float windowHeight, float keyboardHeight)
{
var topOfKeyboard = windowHeight - keyboardHeight; // Obere linke Ecke der Tastatur auf den Bildschirm bezogen
var viewHeightWindow1 = view.ConvertPointFromView(new PointF(0, topOfKeyboard), null);
var frame = new RectangleF(view.Frame.X, view.Frame.Y, view.Frame.Width, (viewHeightWindow1.Y + view.Bounds.Y));
return frame;
}
public static void ResizeViewForKeyboard(UIView view, RectangleF frame)
{
UIView.BeginAnimations("viewMovementForKeyboard");
UIView.SetAnimationBeginsFromCurrentState(true);
UIView.SetAnimationDuration(0.3f);
view.Frame = frame;
UIView.CommitAnimations();
}
Related
I wrote the UIView descendant in Xamarin and overrided Draw method.
Among other staff it draws text. It looks good in any screen orientation, but when I rotate device to the other orientation the text is stretched or shrinks.
The drawing code is:
public override void Draw(CGRect rect)
{
base.Draw(rect);
var ss = new UIStringAttributes();
ss.ForegroundColor = mTextColor;
ss.Font = mTextFont;
ss.ParagraphStyle = new NSMutableParagraphStyle() { Alignment = UITextAlignment.Center };
using (var g = UIGraphics.GetCurrentContext())
{
for (nint i = 0; i < mBars.Length; ++i)
{
var r = mBars[i];
g.SetFillColor(i + 1 == mBars.Length ? mLastBarColor : mBarColor);
g.FillRect(r);
var ns = new NSAttributedString(mDataSource[i].Item2, ss);
var textRect = new CGRect(r.Left, r.Bottom, r.Width, mTextRectHeight);
ns.DrawString(textRect);
}
}
}
Russell is right. I didn't know about UIView contentMode.
It has to be set to 'redraw'
I am new to the Processing and now trying to use FaceOSC. Everything was done already, but it is hard to play the game I made when everything is not a mirror view. So I want to flip the data that FaceOSC sent to processing to create video.
I'm not sure if FaceOSC sent the video because I've tried flip like a video but it doesn't work. I also flipped like a image, and canvas, but still doesn't work. Or may be I did it wrong. Please HELP!
//XXXXXXX// This is some of my code.
import oscP5.*;
import codeanticode.syphon.*;
OscP5 oscP5;
SyphonClient client;
PGraphics canvas;
boolean found;
PVector[] meshPoints;
void setup() {
size(640, 480, P3D);
frameRate(30);
initMesh();
oscP5 = new OscP5(this, 8338);
// USE THESE 2 EVENTS TO DRAW THE
// FULL FACE MESH:
oscP5.plug(this, "found", "/found");
oscP5.plug(this, "loadMesh", "/raw");
// plugin for mouth
oscP5.plug(this, "mouthWidthReceived", "/gesture/mouth/width");
oscP5.plug(this, "mouthHeightReceived", "/gesture/mouth/height");
// initialize the syphon client with the name of the server
client = new SyphonClient(this, "FaceOSC");
// prep the PGraphics object to receive the camera image
canvas = createGraphics(640, 480, P3D);
}
void draw() {
background(0);
stroke(255);
// flip like a vdo here, does not work
/* pushMatrix();
translate(canvas.width, 0);
scale(-1,1);
image(canvas, -canvas.width, 0, width, height);
popMatrix(); */
image(canvas, 0, 0, width, height);
if (found) {
fill(100);
drawFeature(faceOutline);
drawFeature(leftEyebrow);
drawFeature(rightEyebrow);
drawFeature(nosePart1);
drawFeature(nosePart2);
drawFeature(leftEye);
drawFeature(rightEye);
drawFeature(mouthPart1);
drawFeature(mouthPart2);
drawFeature(mouthPart3);
drawFeature(mouthPart4);
drawFeature(mouthPart5);
}
}
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
void drawFeature(int[] featurePointList) {
for (int i = 0; i < featurePointList.length; i++) {
PVector meshVertex = meshPoints[featurePointList[i]];
if (i > 0) {
PVector prevMeshVertex = meshPoints[featurePointList[i-1]];
line(meshVertex.x, meshVertex.y, prevMeshVertex.x, prevMeshVertex.y);
}
ellipse(meshVertex.x, meshVertex.y, 3, 3);
}
}
/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
public void found(int i) {
// println("found: " + i); // 1 == found, 0 == not found
found = i == 1;
}
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
The scale() and translate() snippet you're trying to use makes sense, but it looks like you're using it in the wrong place. I'm not sure what canvas should do, but I'm guessing the face features is drawn using drawFeature() calls is what you want to mirror. If so, you should do place those calls in between pushMatrix() and popMatrix() calls, right after the scale().
I would try something like this in draw():
void draw() {
background(0);
stroke(255);
//flip horizontal
pushMatrix();
translate(width, 0);
scale(-1,1);
if (found) {
fill(100);
drawFeature(faceOutline);
drawFeature(leftEyebrow);
drawFeature(rightEyebrow);
drawFeature(nosePart1);
drawFeature(nosePart2);
drawFeature(leftEye);
drawFeature(rightEye);
drawFeature(mouthPart1);
drawFeature(mouthPart2);
drawFeature(mouthPart3);
drawFeature(mouthPart4);
drawFeature(mouthPart5);
}
popMatrix();
}
The push/pop matrix calls isolate the coordinate space.
The coordinate system origin(0,0) is the top left corner: this is why everything is translated by the width before scaling x by -1. Because it's not at the centre, simply mirroring won't leave the content in the same place.
For more details checkout the Processing Transform2D tutorial
Here's a basic example:
boolean mirror;
void setup(){
size(640,480);
}
void draw(){
if(mirror){
pushMatrix();
//translate, otherwise mirrored content will be off screen (pivot is at top left corner not centre)
translate(width,0);
//scale x -= 1 mirror
scale(-1,1);
//draw mirrored content
drawStuff();
popMatrix();
}else{
drawStuff();
}
}
//this could be be the face preview
void drawStuff(){
background(0);
triangle(0,0,width,0,0,height);
text("press m to toggle mirroring",450,470);
}
void keyPressed(){
if(key == 'm') mirror = !mirror;
}
Another option is to mirror each coordinate, but in your case it would be a lot of effort when scale(-1,1) will do the trick. For reference, to mirror the coordinate, you simply need to subtract the current value from the largest value:
void setup(){
size(640,480);
background(255);
}
void draw(){
ellipse(mouseX,mouseY,30,30);
//subtract current value(mouseX in this case) from the largest value it can have (width in this case)
ellipse(width-mouseX,mouseY,30,30);
}
You can run these examples right here:
var mirror;
function setup(){
createCanvas(640,225);
fill(255);
}
function draw(){
if(mirror){
push();
//translate, otherwise mirrored content will be off screen (pivot is at top left corner not centre)
translate(width,0);
//scale x -= 1 mirror
scale(-1,1);
//draw mirrored content
drawStuff();
pop();
}else{
drawStuff();
}
}
//this could be be the face preview
function drawStuff(){
background(0);
triangle(0,0,width,0,0,height);
text("press m to toggle mirroring",450,470);
}
function keyPressed(){
if(key == 'M') mirror = !mirror;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.5.4/p5.min.js"></script>
function setup(){
createCanvas(640,225);
background(0);
fill(0);
stroke(255);
}
function draw(){
ellipse(mouseX,mouseY,30,30);
//subtract current value(mouseX in this case) from the largest value it can have (width in this case)
ellipse(width-mouseX,mouseY,30,30);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.5.4/p5.min.js"></script>
Okay so I'm starting to make a main menu for a small flash game and to do this I want to use the mouse to click on buttons etc. I have a button class in which I create two rectangles: a rectangle for the button and a rectangle for the mouse based on its X and Y, 1 pixel by 1 pixel. I use Rectangle.Intersects to check if they are touching before seeing if left mouse button is down. Problem is, the thing the mouse position is relative to changes every time so no matter where the mouse button is on the screen, it's never the same co-ordinates as in a different build in that exact same position. I seriously just need ideas now as I'm running out. If I didn't explain it very well or you need further details to help please ask - I WOULD BE SO GRATEFUL.
Will post back if I find an answer
Update - Okay here's the button class
class OnScreenButton
{
public Texture2D texture;
Vector2 position;
Rectangle rectangle;
Color colour = new Color(255, 255, 255, 255);
public Vector2 size;
public OnScreenButton(Texture2D newtexture, GraphicsDevice graphics)
{
texture = newtexture;
// ScreenW = 500, ScreenH = 600
// Img W = 80, Img H = 20
size = new Vector2(graphics.Viewport.Width / 10, graphics.Viewport.Height / 30);
size = new Vector2(texture.Width, texture.Height);
}
bool down;
public bool isClicked;
public void Update(MouseState mouseState)
{
rectangle = new Rectangle((int)position.X, (int)position.Y, (int)size.X, (int)size.Y);
Rectangle mouseRectangle = new Rectangle(mouseState.X, mouseState.Y, 1, 1);
if (mouseRectangle.Intersects(rectangle))
{
if (colour.A == 255)
{
down = false;
}
if (colour.A == 0)
{
down = true;
}
if (down)
{
colour.A += 3;
}
else
{
colour.A -= 3;
}
if (mouseState.LeftButton == ButtonState.Pressed)
{
isClicked = true;
}
}
else if (colour.A < 255)
{
colour.A += 3;
isClicked = false;
colour.A = (255);
}
}
public void SetPosition(Vector2 newPos)
{
position = newPos;
}
public void Draw(SpriteBatch spriteBatch)
{
spriteBatch.Draw(texture, rectangle, colour);
}
}
}
(Sorry for weird formatting, brand new to stack overflow and the posting is still a little confusing)
Here is some other code I think is relevent...
Game.1 initializing stuff
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
protected override void Initialize()
{
// TODO: Add your initialization logic here
Mouse.WindowHandle = Window.Handle;
base.Initialize();
}
public Main()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
KeyboardState keyboardState;
MouseState mouseState;
Main menu update routine...
private void UpdateMainMenu(GameTime gameTime)
{
// Button options
if (buttonPlay.isClicked == true)
{
CreateNewGame();
currentGameState = GameState.playing;
}
buttonPlay.Update(mouseState);
if (buttonExit.isClicked == true)
{
this.Exit();
}
buttonExit.Update(mouseState);
// Press enter to play
if (keyboardState.IsKeyDown(Keys.Enter))
{
CreateNewGame();
currentGameState = GameState.playing;
}
}
Here's thee draw routine for main menu...
public void DrawMainMenu()
{
spriteBatch.Draw(mainMenuBackground, new Vector2(0, 0), Color.White);
buttonPlay.Draw(spriteBatch);
buttonExit.Draw(spriteBatch);
spriteBatch.DrawString(playerAmmoFont, String.Format("{0}", mouseState), new Vector2(0, 0), Color.White);
}
okay that's all I can think of
UPDATE - Okay so I know a few things that aren't the problem...
The whole of my button class is fine, I made a new project and inserted all the relevant code into it and it worked absolutely perfectly so I'm starting to think its something to do with the code positioning and the graphics device stuff although I still don't have a clue how to fix it.
the window appears at the same spot every time
there is no pattern to the change in coordinates at all
this is really annoying
UPDATE - OKAY. I spent a long time writing down the coordinates that I got each time I ran the code and stuck to cursor in the top right corner of the screen. Here is what I got.
(-203, -225)
(-253, -275)
(-53, -75)
(-103, -125)
(-153, -175)
(-203, -225)
(-253, -275)
(-53, -75)
(-103, -125)
(-153, -175)
(-203, -225)
(-253, -275)
(-53, -75)
(-103, -125)
(-153, -175)
(-203, -225)
(-253, -275)
(-53, -75)
(-103, -125)
(-153, -175)
(-203, -225)
(-78, -100)
(-128, -150)
(-178, -200)
(-228, -250)
(-28, -50)
(-53, -75)
(-103, -125)
(-153, -175) < AND FROM HERE THE PATTERN LOOPS ROUND.
I just don't get how the same code can execute a different bug on different executions like this.
Also, mouse.Wheel doesn't go up or down whereas it works on the project that I made to test the relevant code where the mouse position was relevant to the top left pixel of the game window.
UPDATE - EVEN MORE DAMN COMPLICATIONS - So I just rand it a few times again and the offset values are offset... the increase is the same but I got values like (-178, -200) then (-228, -250). I have also discovered that the mouse is not relative to the game window what so ever, if I jam the mouse in the top right corner of the screen and check the coordinates, then move the game window and do the same again, the coordinates don't change. Please please please help me, or tell me if I'm being stupid, or something. Thanks.
The mouse coordinates are relative to the monitor. Here is my general button class to try and work for your situation.
public class Button
{
public event EventHandler<EventArgs> Clicked;
public Vector2 Position { get; set;}
public Texture2D Texture { get; set;}
public Color Tint { get; set; }
public float Scale { get; set; }
public float Rotation { get; set; }
public int Width
{
get
{
if (texture == null)
return 0;
else
return texture.Width;
}
}
public int Height
{
get
{
if (texture == null)
return 0;
else
return texture.Height;
}
}
private void OnClick()
{
if (Clicked != null)
Clicked(this, EventArgs.Empty);
}
public Button(Vector2 position, Texture2D texture)
: base(parent)
{
Position = position;
Texture = texture;
Tint = Color.White;
Scale = 1.0f;
Rotation = 0.0f;
}
public bool HandleClick(Vector2 vector)
{
if (vector.X >= Position.X)
{
if (vector.X <= Position.X + Width)
{
if (vector.Y >= Position.Y)
{
if (vector.Y <= Position.Y + Height)
{
OnClick();
return true;
}
}
}
}
return false;
}
public bool HandleEntered(Vector2 vector)
{
if (vector.X >= Position.X)
{
if (vector.X <= Position.X + Width)
{
if (vector.Y >= Position.Y)
{
if (vector.Y <= Position.Y + Height)
{
return true;
}
}
}
}
return false;
}
public override void Draw(SpriteBatch spriteBatch)
{
spriteBatch.Draw(Texture, Position, null, Tint, Rotation, Vector2.Zero, Scale, SpriteEffects.None, 0.0f);
}
Declare a button:
Button btn = new Button(position where you want the button, texture for the button);
btn.Clicked += () => { /* Handle button clicked code here */ };
In your update method inside your main game:
public void Update (GameTime gameTime)
{
MouseState mouseState = Mouse.GetState();
if(mouseState.LeftButton == ButtonState.Pressed) // check if mouse is clicked
{
btn.HandleClicked(new Vector2(mouseState.X, mouseState.Y)); // If true then the button clicked event will fire
// Here you can also change the color of the button if the button is currently clicked
}
// Here you can change the color of the button if the mouse is hover over the control
// Example:
btn.Tint = btn.HandleEntered(new Vector2(mouseState.X, mouseState.Y)) ? Color.White * 0.75f : Color.White;
}
Note: You can also use a rectangle for the button to adjust its size instead of strictly using the textures dimensions. Hope this gives some insight.
So here's what was going on: I had a bullet class in my game for every bullet shot. In this class I check whether the bullets hits nothing, hits the asteroid, or destroys the asteroids. If the latter is true then I would increment playerScore by 5. PlayerScore was a Game1 attribute so I thought the easiest way to do this would be to create a new Game1 in bullet.cs to allow me to refer to the variable. Deleting the "Main mainGame = new Main():" in Bullet.cs fixed this issue and I think the issue was coming from a new graphics device being made every single time I fired a single bullet.
Could someone tell me how to catch this event?
Because when I rotated the phone to landscape mode, the app could not display correctly.
Thanks,
Duy
When this happens then BB UI framework definitelly calls layout(int width, int height) for your screen. This is because MainScreen is also a Manager, so it should layout all its child fields before BB UI framework starts painting.
So in layout() you could track current orientation state (with net.rim.device.api.system.Display.getOrientation()) and compare with the previous one. If it is changed, then the device has just been rotated.
I've come up with a method different to the suggested by Arhimed and it seems to work well (I use it to draw a custom Field differently - when the device is tilted). I have a method
protected void myOrientation() {
// portrait is true when dh > dw
boolean portrait = (Display.getOrientation() == Display.ORIENTATION_PORTRAIT);
// dw and dh = real horizontal and vertical dimensions of display - regardless of device orientation
int dw = portrait ? Math.min(Display.getWidth(), Display.getHeight()) : Math.max(Display.getWidth(), Display.getHeight());
int dh = portrait ? Math.max(Display.getWidth(), Display.getHeight()) : Math.min(Display.getWidth(), Display.getHeight());
// here I draw my custom Field
invalidate();
}
and call it once in the constructor and after that it is called on every Accelerometer event:
public class MyScreen extends MainScreen implements AccelerometerListener {
private MyScreen() {
if (AccelerometerSensor.isSupported()) {
orientationChannel = AccelerometerSensor.openOrientationDataChannel( Application.getApplication() );
orientationChannel.addAccelerometerListener(this);
}
....
}
public void onData(AccelerometerData accData) {
if (old != accData.getOrientation()) {
myField.myOrientation();
}
}
Maybe this helps you and yes - you have to check, if the keyboard is slided out on Torch
Regards
Alex
For the sake of this question, let us suppose that I want a row of buttons. I want to put as many buttons in that row as I can fit on the screen, but no more. In other words, as long as a prospective button will not be cut off or have its text shortened, add it.
It seems that I should be able to do something like:
HorizontalFieldManager hfm = new HorizontalFieldManager();
int remainingWidth = Display.getWidth();
int i =0;
while(true) {
ButtonField bf = new ButtonField("B " + i);
remainingWidth -= bf.getWidth();
if(remainingWidth<0)
break;
hfm.add(bf);
i++;
}
add(hfm);
But this doesn't work. bf.getWidth() is always 0. I suspect that this is because the button has not yet been laid out when I query for the width.
So, perhaps I could just make sure the buttons are always the same size. But this won't work for a few reasons:
Different BB platforms have different looks for buttons and text that will fit on a button on a Curve won't fit on a button on a Storm.
3rd party themes may change the look of buttons, so I can't even count on buttons being a certain size on a certain platform.
Is there no way for me to actually check the remaining space before adding a button? It feels like a fairly useful feature; am I just missing something?
Try to draw the Manager before adding component to it.
You should probably add the padding too, if the button has one.
public class Main extends UiApplication{
public static void main(String[] args) {
Main main = new Main();
main.enterEventDispatcher();
}
public Main() {
Screen main = new Screen();
this.pushScreen(main);
main.init();
}
}
public class Screen extends MainScreen {
HorizontalFieldManager hfm;
public Screen() {
hfm = new HorizontalFieldManager();
this.add(hfm);
}
public void init() {
int remainingWidth = Display.getWidth();
int i = 0;
while(true) {
ButtonField bf = new ButtonField("B " + i);
hfm.add(bf);
remainingWidth -= bf.getWidth();
System.out.println(remainingWidth);
if(remainingWidth <= 0) {
hfm.delete(bf);
break;
}
i++;
}
}
You're correct that getWidth() returns the width of the field after it's been laid out (that's why it's 0), but getPreferredWidth() will return the value that's given to the Layout Manager, and is probably what you want.
The other problem you have is that you're comparing remainingWidth to 0. If a button's preferred width is, say, 36 pixels, and the remaining width is 20 pixels, you'll draw a button in a spot where you don't have enough room to display it.
Try something like this:
HorizontalFieldManager hfm = new HorizontalFieldManager();
int remainingWidth = Display.getWidth();
for (int i; true; i++ ) {
ButtonField bf = new ButtonField("B " + i);
int preferredWidth = bf.getPreferredWidth();
if ( remainingWidth < preferredWidth )
break;
remainingWidth -= preferredWidth;
hfm.add(bf);
}
add(hfm);
Try to make your own HorizontalFiledManager (extends Manager) and override
protected void sublayout( int maxWidth, int maxHeight ){
setExtent(maxWidth,maxHeight);
}
Get the number of childs, and with a for loop go trough and start to lay out the childs (setPosition, layoutChild).
When you lay out the child, count his width and always check if the counted width + the next child`s width is greater then maxWidth then do not lay out and break the for loop.
Hopefully this help you.