I'm using MonoDevelop and xamarin for creating iOS application.
I have a view which looks like this:
public class MyView: UIView
{
private Timer _t = new Timer(1000);
private int _i = 0;
public MyView()
{
_t.Elapsed += (sender, e) => {
_i = (_i + 1) % 100;
this.Draw(new Rectangle(0, 0, 110, 110));
};
_t.Enabled = true;
}
public override void Draw(RectangleF rect)
{
base.Draw(rect);
var uiImage = UIImage.FromFile("myImage");
var uiImageView = new UIIMageView(new Rectangle(_i, 0, 10, 10));
uiImageView.Image = uiImage;
AddSubView(uiIMageView);
}
}
I expect that my image will move. But as a result I've only initial static image. Can anybody suggest approach how to do animation like described?
I would suggest modifying your code like so:
public MyView()
{
var uiImage = UIImage.FromFile("myImage");
var uiImageView = new UIIMageView(new Rectangle(0, 0, 10, 10));
uiImageView.Image = uiImage;
AddSubView(uiIMageView);
_t.Elapsed += (sender, e) => {
_i = (_i + 1) % 100;
this.Draw(new Rectangle(_i, 0, 110, 110));
};
_t.Enabled = true;
}
public override void Draw(RectangleF rect)
{
base.Draw(rect);
InvokeOnMainThread(()=>{
uiImageView.Frame = rect;
});
}
I think part of your issue was that the timer Elapsed handler runs on a separate thread than the main UI Thread. Also you it'll be more efficient to not recreate the UIImageView each time.
Related
I need to create an image slideshow in my app (i.e. the image should be changed at a regular interval of time with a fade in / fade out effect). I have tried some code, but it throws illegal exception.
Is there any option to change images in picture scroll field programatically? i.e. from a thread or something?
public class SlideTransition extends MainScreen {
final Bitmap image000 = Bitmap.getBitmapResource("img1.jpg");
final Bitmap image001 = Bitmap.getBitmapResource("img2.jpg");
final Bitmap image002 = Bitmap.getBitmapResource("img3.jpg");
final BitmapField animationField = new BitmapField(image000);
int counter = 0;
Timer animationTimer = new Timer();
TimerTask animationTask;
public SlideTransition() {
animationTask = new TimerTask() {
public void run() {
if (counter == 0) {
animationField.setBitmap(image000);
}
if (counter == 1) {
animationField.setBitmap(image001);
}
if (counter == 2) {
animationField.setBitmap(image002);
counter = -1;
}
counter++;
}
};
animationTimer.scheduleAtFixedRate(animationTask, 0, 100);
add(animationField);
}
}
The code posted will throw an IllegalStateException because it is attempting to modify a UI element directly from a background (timer) thread:
animationField.setBitmap(image000);
You need to use UiApplication.getUiApplication().invokeLater(), or something similar, to modify the UI from a background thread.
Also, you can have your timer continuously adjust the bitmap field's alpha value (opacity) to produce a fade effect.
Here's an example, starting with the code you provided:
public class SlideTransition extends MainScreen {
private final Bitmap image000 = Bitmap.getBitmapResource("img1.jpg");
private final Bitmap image001 = Bitmap.getBitmapResource("img2.jpg");
private final Bitmap image002 = Bitmap.getBitmapResource("img3.jpg");
private AlphaBitmapField animationField = new AlphaBitmapField(image000);
private Timer animationTimer = new Timer();
private TimerTask animationTask;
private final int animationPeriodMsec = 40; // 25 Hz animation timer
public SlideTransition() {
animationTask = new AnimationTask();
add(animationField);
animationTimer.scheduleAtFixedRate(animationTask, 0, animationPeriodMsec);
}
// I separated your anonymous timer task into its own class for readability
private class AnimationTask extends TimerTask {
private final int fadeDurationMsec = 500;
private final int displayDurationMsec = 1500;
private final int fadeInEndCount = fadeDurationMsec / animationPeriodMsec;
private final int fadeOutStartCount = (fadeDurationMsec + displayDurationMsec) / animationPeriodMsec;
private final int endCount = (2 * fadeDurationMsec + displayDurationMsec) / animationPeriodMsec;
private int imgCounter = 0;
private int cycleCounter = 0;
public void run() {
if (cycleCounter >= endCount) {
cycleCounter = 0;
}
Bitmap newImage = null;
if (cycleCounter == 0) {
// time to switch the images
if (imgCounter == 0) {
newImage = image000;
} else if (imgCounter == 1) {
newImage = image001;
} else if (imgCounter == 2) {
newImage = image002;
imgCounter = -1;
}
imgCounter++;
}
// assign final variables to use inside a UI thread Runnable
final Bitmap currentImage = newImage;
final int i = cycleCounter;
UiApplication.getUiApplication().invokeLater(new Runnable() {
public void run() {
if (i == 0) {
// switch images, and start with the image invisible (alpha = 0)
animationField.setAlpha(0);
animationField.setBitmap(currentImage);
} else if (i <= fadeInEndCount) {
// fade in by changing alpha
animationField.setAlpha(i * 255 / fadeInEndCount);
} else if (i >= fadeOutStartCount) {
// fade out by changing alpha
animationField.setAlpha(255 + (fadeOutStartCount - i) * 255 / (endCount - fadeOutStartCount));
}
}
});
cycleCounter++;
}
}
// this class extends BitmapField to allow alpha adjustment, and automatic repainting
private class AlphaBitmapField extends BitmapField {
private int _alpha;
public AlphaBitmapField(Bitmap img) {
super(img);
}
public void setAlpha(int alpha) {
if (alpha != _alpha) {
_alpha = alpha;
// force a repaint
invalidate();
}
}
protected void paint(Graphics graphics) {
graphics.setGlobalAlpha(_alpha);
super.paint(graphics);
}
}
}
I changed the time values from your code, but you can modify them to whatever you like. I had the animation timer run at 25 cycles per second. I find that this is a reasonable value for smartphone animations. It's fast enough to look fairly smooth, but is only about twice the rate that the human eye can process information like this ... any faster is probably wasteful.
I defined constants that you can use to control the length of time that the fade in and fade out takes (fadeDurationMsec) and the time that each image is displayed between fade in and fade out (displayDurationMsec). All values are in milliseconds.
Helpful References
BlackBerry AnimatedGIFField sample code.
BlackBerry forums thread about fade in / fade out
I was wondering if MonoTouch allows the developer to change the colour of UIPageControl dots to suit a light background - they seem to be fixed white, which makes them very hard to see on a light textured background.
I am aware there is no public API available for this but I was wondering if anything was internally implemented in MonoTouch to improve on this.
Otherwise, what's the recommended approach to using a UIPageControl on a light background?
I took a stab at translating this. I'm not sure it will work, but it does compile. Note that the page linked to contains comments indicating that Apple frowns upon this code and may reject it:
using System;
using MonoTouch.Foundation;
using MonoTouch.UIKit;
namespace Whatever
{
public class StyledPageControl : UIPageControl
{
public StyledPageControl () : base()
{
}
public override int CurrentPage {
get {
return base.CurrentPage;
}
set {
base.CurrentPage = value;
string imgActive = NSBundle.MainBundle.PathForResource("activeImage", "png");
string imgInactive = NSBundle.MainBundle.PathForResource("inactiveImage", "png");
for (int subviewIndex = 0; subviewIndex < this.Subviews.Length; subviewIndex++)
{
UIImageView subview = this.Subviews[subviewIndex] as UIImageView;
if (subviewIndex == value)
subview.Image = UIImage.FromFile(imgActive);
else
subview.Image = UIImage.FromFile(imgInactive);
}
}
}
public override int Pages {
get {
return base.Pages;
}
set {
base.Pages = value;
string img = NSBundle.MainBundle.PathForResource("inactiveImage", "png");
for (int subviewIndex = 0; subviewIndex < this.Subviews.Length; subviewIndex++)
{
UIImageView subview = this.Subviews[subviewIndex] as UIImageView;
subview.Image = UIImage.FromFile(img);
}
}
}
}
}
Convert the following code to C#: http://apptech.next-munich.com/2010/04/customizing-uipagecontrols-looks.html
I combined this and this for monotouch. I hope this helps.
The usage is like this:
_pager.Change += delegate(object sender, EventArgs e) {
var pc = sender as PageControl;
Console.WriteLine ("Change Delegate== " + pc.currentPage);
var toPage = pc.currentPage;
var pageOffset = _scroll.Frame.Width*toPage;
PointF p = new PointF(pageOffset, 0);
Console.WriteLine (pageOffset);
_scroll.SetContentOffset(p,true);
};
And the class like this.
public class PageControl:UIView
{
#region ctor
public PageControl (RectangleF rect) :base(rect)
{
this.BackgroundColor = UIColor.Red;
this.CurrenColor=new CGColor(.2f,15f,10F);
this.OtherColor =new CGColor(.77F,.71F,.60F);
}
#endregion
#region Fields
float kDotDiameter= 7.0f;
float kDotSpacer = 7.0f;
int _currentPage;
int _numberOfPages;
CGColor CurrenColor{get;set;}
CGColor OtherColor{get;set;}
public int currentPage
{
set
{
_currentPage = Math.Min(Math.Max(0, value),_numberOfPages-1);
this.SetNeedsDisplay();
}
get{return _currentPage;}
}
public int numberOfPages
{
set
{
_numberOfPages = Math.Max(0,value);
_currentPage = Math.Min(Math.Max(0, _currentPage), _numberOfPages-1);
this.SetNeedsDisplay();
}
get{return _numberOfPages;}
}
#endregion
#region Overrides
public override void Draw (RectangleF rect)
{
base.Draw (rect);
CGContext context = UIGraphics.GetCurrentContext();
context.SetAllowsAntialiasing(true);
RectangleF currentBounds = this.Bounds;
float dotsWidth = this.numberOfPages*kDotDiameter + Math.Max(0,this.numberOfPages-1)*kDotSpacer;
float x = currentBounds.GetMidX() - dotsWidth/2;
float y = currentBounds.GetMidY() - kDotDiameter/2;
for (int i = 0; i < _numberOfPages; i++) {
RectangleF circleRect = new RectangleF(x,y,kDotDiameter,kDotDiameter);
if (i==_currentPage) {
context.SetFillColor(this.CurrenColor);
}
else {
context.SetFillColor(this.OtherColor);
}
context.FillEllipseInRect(circleRect);
x += kDotDiameter + kDotSpacer;
}
}
public override void TouchesBegan (MonoTouch.Foundation.NSSet touches, UIEvent evt)
{
base.TouchesBegan (touches, evt);
PointF touchpoint = (touches.AnyObject as MonoTouch.UIKit.UITouch).LocationInView(this);
RectangleF currentbounds = this.Bounds;
float x = touchpoint.X- currentbounds.GetMidX();
if (x<0 && this.currentPage>=0) {
this.currentPage--;
Change(this,EventArgs.Empty);
}
else if (x>0 && this.currentPage<this.numberOfPages-1) {
this.currentPage++;
Change(this,EventArgs.Empty);
}
}
#endregion
#region delegate
public event EventHandler Change;
#endregion
}
I can not find a way to stop the out of memory error. The error occurs when I move the brightness or contrast track bar a few times.
Here is the code from the main form design for the button_click event that that opens the brightness and contrast form and send the value of "foto".
private void menuItemBrightness_Click(object sender, EventArgs e)
{
BrightnessContrast bFrm = new BrightnessContrast();
bFrm.Foto = imageHandler.CurrentBitmap;
if (bFrm.ShowDialog() == DialogResult.OK)
{
this.Cursor = Cursors.WaitCursor;
imageHandler.RestorePrevious();// this is for a undo or redo ... ignore this part
imageHandler.CurrentBitmap = bFrm.Foto1;
pictureBox1.Refresh();
this.Cursor = Cursors.Default;
}
}
Here is the complete code for the brightness and contras form
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
namespace SMART {
public partial class BrightnessContrast : Form {
ImageHandler imageHandler = new ImageHandler();
public int B = 0;
private int C = 0;
private static Bitmap foto;
private static Bitmap foto1;
private static Bitmap NewBitmap;
public BrightnessContrast()
{
this.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint, true);
InitializeComponent();
btnOK.DialogResult = DialogResult.OK;
btnCancel.DialogResult = DialogResult.Cancel;
domainUpDownB.SelectedIndex = 255;
domainUpDownC.SelectedIndex = 75;
}
private void BrightnessForm_Load(object sender, EventArgs e)
{ pictureBox2.Image = foto; }
public Bitmap Foto
{
get { return foto; }
set { foto = value; }
}
public Bitmap Foto1
{
get { return foto1; }
set { foto1 = value; }
}
public void brightnesstrackBar1_ValueChanged(object sender, EventArgs e)
{
domainUpDownB.Text = ((int)brightnessTrackBar.Value).ToString();
B = ((int)brightnessTrackBar.Value);
pictureBox2.Image = AdjustBrightness(foto, B);
foto1 = (Bitmap)pictureBox2.Image;
}
private void domainUpDown_TextChanged(object sender, EventArgs e)
{
try
{
brightnessTrackBar.Value = Convert.ToInt32(domainUpDownB.Text);
brightnesstrackBar1_ValueChanged(sender, e);
}
catch (Exception)
{
MessageBox.Show("Incorect Input Value Format");
}
}
public static Bitmap AdjustBrightness(Bitmap Image, int Value)
{
Bitmap TempBitmap = Image;
float FinalValue = (float)Value / 255.0f;
NewBitmap = new Bitmap(TempBitmap.Width, TempBitmap.Height);
Graphics NewGraphics = Graphics.FromImage(NewBitmap);
float[][] FloatColorMatrix ={
new float[] {1, 0, 0, 0, 0},
new float[] {0, 1, 0, 0, 0},
new float[] {0, 0, 1, 0, 0},
new float[] {0, 0, 0, 1, 0},
new float[] {FinalValue, FinalValue, FinalValue, 1, 1
}
};
ColorMatrix NewColorMatrix = new ColorMatrix(FloatColorMatrix);
ImageAttributes Attributes = new ImageAttributes();
Attributes.SetColorMatrix(NewColorMatrix);
NewGraphics.DrawImage(TempBitmap, new Rectangle(0, 0, TempBitmap.Width, TempBitmap.Height), 0, 0, TempBitmap.Width, TempBitmap.Height, GraphicsUnit.Pixel, Attributes);
Attributes.Dispose();
NewGraphics.Dispose();
return NewBitmap;
}
public void contrastrackBar1_ValueChanged(object sender, EventArgs e)
{
domainUpDownC.Text = ((int)contrastTrackBar.Value).ToString();
C = ((int)contrastTrackBar.Value);
pictureBox2.Image = AdjustContrast(foto, C);
foto1 = (Bitmap)pictureBox2.Image;
}
public static Bitmap AdjustContrast(Bitmap Image, int Value)
{
Bitmap TempBitmap = Image;
float FinalValue = (float)Value*0.04f;
NewBitmap = new Bitmap(TempBitmap.Width, TempBitmap.Height);
Graphics NewGraphics = Graphics.FromImage(NewBitmap);
ImageAttributes Attributes = new ImageAttributes();
ColorMatrix NewColorMatrix = new ColorMatrix (new float[][] {
new float[] {FinalValue, 0f, 0f, 0f, 0f},
new float[] {0f, FinalValue, 0f, 0f, 0f},
new float[] {0f, 0f, FinalValue, 0f, 0},
new float[] {0f, 0f, 0f, 1f, 0f},
new float[] {0.001f, 0.001f, 0.001f, 0f, 1f}
});
Attributes.SetColorMatrix(NewColorMatrix);
NewGraphics.DrawImage(TempBitmap, new Rectangle(0, 0, TempBitmap.Width, TempBitmap.Height), 0, 0, TempBitmap.Width, TempBitmap.Height, GraphicsUnit.Pixel, Attributes);
Attributes.Dispose();
NewGraphics.Dispose();
return NewBitmap;
}
OK here is the problem ... if I load a big image(as in pixels for example) and start moving the track Bar after a few moves it will show the famous "Out of memory exception was unhanded" and the error points to this line
NewGraphics.DrawImage(TempBitmap,
new System.Drawing.Rectangle(0, 0, TempBitmap.Width, TempBitmap.Height), 0, 0,
TempBitmap.Width, TempBitmap.Height, System.Drawing.GraphicsUnit.Pixel,
Attributes);
I am using Attributes.Dispose(); NewGraphics.Dispose(); way i still have the error what I am missing?
Dispose gets rid of the unmanged resources (handles, unmanaged memory). There is probably a managed memory footprint here as well and that only gets free when the garbage collect occurs. My guess is that you need to reuse some of these bigger memory objects rather than having to recreate them each time.
I am a Blackberry java developer. I am trying to develop a simple slot machine logic. I am new to animated graphics etc in blackberry. So, can anyone tell me how to design a simple slot machine where on pressing a button the images in 3 blocks must start rotating and after it stops the prizes will be displayed according to the pics. So can u plz help me with some samples or tutorials of how to do it...
Edit: I am developing it just as fun application that doesnt involve any money transactions. So, any Blackberry developers plz guide me how to achieve the task and to spin the three images on click of a button...
This is a simple example but you will have to deal with decoration, smooth rolling etc yourself.
Let's say you have 6 images 70x70.
Simple BitmapField extension to paint current slot image, half of image above and half of image below:
class SlotField extends BitmapField {
Bitmap bmp1 = Bitmap.getBitmapResource("img1.png");
Bitmap bmp2 = Bitmap.getBitmapResource("img2.png");
Bitmap bmp3 = Bitmap.getBitmapResource("img3.png");
Bitmap bmp4 = Bitmap.getBitmapResource("img4.png");
Bitmap bmp5 = Bitmap.getBitmapResource("img5.png");
Bitmap bmp6 = Bitmap.getBitmapResource("img6.png");
Bitmap[] bmps = new Bitmap[] { bmp1, bmp2, bmp3, bmp4, bmp5, bmp6 };
int mPos = 0;
public SlotField(int position) {
mPos = position;
}
public int getBitmapHeight() {
return bmp1.getHeight() * 2;
}
public int getBitmapWidth() {
return bmp1.getWidth();
}
protected void layout(int width, int height) {
setExtent(getBitmapWidth(), getBitmapHeight());
}
int getNextPos() {
if (mPos == bmps.length - 1) {
return 0;
} else
return mPos + 1;
}
int getPrevPos() {
if (mPos == 0) {
return bmps.length - 1;
} else
return mPos - 1;
}
protected void paint(Graphics g) {
Bitmap hImg = bmps[getPrevPos()];
Bitmap mImg = bmps[mPos];
Bitmap lImg = bmps[getNextPos()];
g.drawBitmap(0, 0, 70, 35, hImg, 0, 35);
g.drawBitmap(0, 35, 70, 70, mImg, 0, 0);
g.drawBitmap(0, 105, 70, 35, lImg, 0, 0);
}
}
Now put these fields on screen and animate with timer:
class MainScr extends MainScreen {
SlotField slot1 = new SlotField(0);
SlotField slot2 = new SlotField(3);
SlotField slot3 = new SlotField(5);
boolean running = false;
public MainScr() {
HorizontalFieldManager hField = new HorizontalFieldManager();
add(hField);
hField.add(slot1);
hField.add(slot2);
hField.add(slot3);
ButtonField btnRoll = new ButtonField("Roll");
btnRoll.setChangeListener(new FieldChangeListener() {
public void fieldChanged(Field field, int context) {
if (!running)
rollSlots();
}
});
add(btnRoll);
}
void rollSlots() {
Timer timer = new Timer();
final Random rnd = new Random();
TimerTask ttask1 = new TimerTask() {
int cycle = 0;
public void run() {
slot1.mPos = slot1.getNextPos();
invalidate();
cycle++;
if (cycle >= 100+rnd.nextInt(6))
cancel();
}
};
TimerTask ttask2 = new TimerTask() {
int cycle = 0;
public void run() {
slot2.mPos = slot2.getNextPos();
invalidate();
cycle++;
if (cycle >= 100+rnd.nextInt(6))
cancel();
}
};
TimerTask ttask3 = new TimerTask() {
int cycle = 0;
public void run() {
slot3.mPos = slot3.getNextPos();
invalidate();
cycle++;
if (cycle >= 100+rnd.nextInt(6))
cancel();
}
};
timer.schedule(ttask1, 0, 50);
timer.schedule(ttask2, 200, 50);
timer.schedule(ttask3, 400, 50);
}
}
alt text http://img534.imageshack.us/img534/2172/slots.jpg
For UI functionality read
Blackberry User Interface Design - Customizable UI?
and
Blackberry - fields layout animation
The simulation of mechanical reels on a gaming machine is protected by United States Patent 7452276. The patent web page has links to 40 other US and international patents that you would have to investigate before you could start developing your software.
After you received permission from all of the different US and international patent holders to develop your software, you would develop a long .gif strip with the different images that you quickly move down in three or more positions. Your software would have to distort the top and bottom edges of the visible portions of the .gif strip to give the appearance of a mechanical slot wheel.
I'm creating a inner class in a method. After that I am accessing some statements like,
public class Test extends MainScreen
{
HorizontalFieldManager hfm;
Bitmap bitmap[] = new Bitmap[100];
BitmapField[] bitmapField = new BitmapField[100];
int countBitmap = 0;
Test()
{
VerticalFieldManager vfm_Main = new VerticalFieldManager();
hfm = new HorizontalFieldManager(HorizontalFieldManager.USE_ALL_WIDTH);
vfm_Main.add(hfm);
add(vfm_Main);
}
void drawBitmap()
{
bitmap[countBitmap] = new Bitmap(100, 100);
bitmapField[countBitmap] = new BitmapField(bitmap[countBitmap]){
public void paint(Graphics g)
{
................
................
g.drawLine(x1,y1,x2,y2);
}
}
synchronized(UiApplication.getEventLock())
{
for(int i = 0 ; i < bitmapField.length; i++)
{
if(bitmapField[i] != null)
{
bitmapField[i].setBitmap(bitmap[i]);
}
}
hfm.add(bitmapField[countBitmap]);
countBitmap++;
But here the problem is after creating the bitmap & before creating the bitmapField, control goes to
synchronized(UiApplication.getEventLock()){hfm.add(bitmapField[countBitmap]); }
So before creating the bitmapField, it adds it in hfm.
So the output is coming like "Everytime a new BitmapField is added in hfm (means at the same position by replacing the previous one)". But I want the BitmapFields come next to each other in hfm.
How to do it?
Any solution why the control goes first to hfm.add() before the new bitmapField() inner class?
first of all, several suggestions about code:
see no reason using synchronized since it's UI thread
don't setBitmap, bitmap is already passed to BitmapField constructor
actually all BitmapFields come next to each other in hfm, to make it clear, I've add number to each one.
if you want some custom constructor or new fields in BitmapField, it's better to create new class as an extension of BitmapField
class TestScr extends MainScreen {
HorizontalFieldManager hfm;
Bitmap bitmap[] = new Bitmap[100];
BitmapField[] bitmapField = new BitmapField[100];
TestScr() {
hfm = new HorizontalFieldManager();
add(hfm);
drawBitmap();
}
void drawBitmap() {
for (int i = 0; i < 5; i++) {
bitmap[i] = new Bitmap(50, 50);
Graphics graphics = new Graphics(bitmap[i]);
graphics.setColor(Color.RED);
String number = Integer.toString(i);
Font font = graphics.getFont().derive(Font.BOLD, 40, Ui.UNITS_px);
graphics.setFont(font);
int textWidth = graphics.getFont().getAdvance(number);
int textHeight = graphics.getFont().getHeight();
int x = (bitmap[i].getWidth() - textWidth) / 2;
int y = (bitmap[i].getHeight() - textHeight) / 2;
graphics.drawText(number, x, y);
bitmapField[i] = new BitmapField(bitmap[i]) {
public void paint(Graphics g) {
super.paint(g);
int width = getWidth() - 1;
int height = getHeight() - 1;
g.setColor(Color.GREEN);
g.drawLine(0, 0, width, 0);
g.drawLine(width, 0, width, height);
g.drawLine(width, height, 0, height);
g.drawLine(0, height, 0, 0);
}
};
hfm.add(bitmapField[i]);
}
}
}