Blackberry: Images disappearing from custom drawListRow - blackberry

I've managed to get images into a custom drawListRow:
public void drawListRow(ListField listField, Graphics graphics, int index, int y, int width) {
graphics.drawBitmap(0, (index) * listField.getRowHeight(), firstrowPostion, rowHeight, thing.image, 0, 0);
graphics.setFont(titleFont);
graphics.drawText(thing.title, firstrowPostion, y, (DrawStyle.LEFT | DrawStyle.ELLIPSIS | DrawStyle.TOP ), 250);
}
The first time though everything works perfectly but once I get to the bottom of the list and start to scroll up again, the pictures have disappeared.
Any suggestions?
Edit: I've figured out the second time through this code:
try {
InputStream inputStream = Connector.openInputStream(ImagePath);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
int i = 0;
while ((i = inputStream.read()) != -1) {
outputStream.write(i);
}
byte[] data = outputStream.toByteArray();
EncodedImage eimg = EncodedImage.createEncodedImage(data, 0,
data.length);
Bitmap image = eimg.getBitmap();
inputStream.close();
outputStream.close();
return ImageUtility.resizeBitmap(image, 70, 70);
} catch (IOException e) {
return null;
} catch (IllegalArgumentException ex) {
return null;
}
}
that InputStream inputStream = Connector.openInputStream(ImagePath); is throwing an IOException. I understand from here
that IO will be thrown under these conditions: but I don't know which is the cause:
1. more than one openInputStream() on single instance of fileconnection.
2. openInputStream() on already closed fileconnection.
3. openInputStream() on a directory.
again any suggestions?

I figured out it's best to just create a separate array of fully formed images and send both that and the thing array to drawlistrow, instead of trying to form and draw them as every row is drawn.

Related

Blackberry switch screen

To navigate through the pages of my app i use:
UiApplication.getUiApplication().invokeLater(new Runnable(){
public void run(){
UiApplication.getUiApplication().pushScreen(screen);
}
});
This is working well on simple screens with not too many fields. But when I try to go to a Screen more elaborated (it contains a custom "listField" made out of multiple managers with multiple fields) the UI takes A LOT of time to update. I've noticed that the page has actually finished constructing but it just doesn't show up, I have to move the trackpad or do a click and then the screen will show.
Code for the screen:
public class List extends MainScreen{
public List(Categoria categoria){
Bitmap bmap = Bitmap.getBitmapResource("img/fondo.png");
getMainManager().setBackground(BackgroundFactory.createBitmapBackground(bmap, Background.POSITION_X_LEFT, Background.POSITION_Y_TOP, Background.REPEAT_SCALE_TO_FIT));
setTitle("Establecimientos");
vfm = new VerticalFieldManager(Manager.VERTICAL_SCROLL | Manager.VERTICAL_SCROLLBAR);
try {
DbHelper dbHelper = new DbHelper();
dbHelper.open();
EstablecimientoDAO eDao = new EstablecimientoDAO(dbHelper.db);
OfertaDAO oDao = new OfertaDAO(dbHelper.db);
Vector ests = eDao.getEstablecimientosPorCategoria(categoria);
for(int i = 0; i < ests.size(); i++){
Establecimiento est = (Establecimiento) ests.elementAt(i);
Oferta oferta = oDao.getOferta(est.getIdEstablecimiento());
EstablecimientosListField elf1 = new EstablecimientosListField();
Bitmap bm = null;
String location = "";
FileSystem fs = Utilities.getFileSystem();
if(fs.hasSdCard()){
location = "/SDCard/Blackberry/pictures/";
}
else if(fs.hasMCard()){
location = "/store/";
}
FileConnection fc = (FileConnection)Connector.open("file://" + location + "e_" + est.getIdEstablecimiento() + ".jpg");
if(fc.exists() && fc.fileSize() > 0){
InputStream is = fc.openInputStream();
byte [] data = new byte[(int) fc.fileSize()];
data = IOUtilities.streamToBytes(is);
is.close();
fc.close();
//bm = Bitmap.createBitmapFromBytes(data, 0, data.length, 1);
EncodedImage eImage = EncodedImage.createEncodedImage(data, 0, data.length);
eImage = Utilities.resizeToWidth(eImage, Display.getWidth() / 3, 1, true);
Bitmap tmp = eImage.getBitmap();
if(tmp.getHeight() > 150){
eImage = Utilities.resizeToHeight(eImage, 1, 150, true);
}
bm = eImage.getBitmap();
}
else{
EncodedImage eImage = EncodedImage.getEncodedImageResource("img/default_esta.png");
eImage = Utilities.resizeToHeight(eImage, 1, 150, true);
bm = eImage.getBitmap();
}
String desc = "";
if(oferta != null && oferta.getDescripcion() != null){
desc = oferta.getDescripcion();
}
if(est.getDescEstablecimiento() != null){
desc += est.getDescEstablecimiento();
}
ButtonField verMas = new ButtonField("VER MÁS"){
protected boolean touchEvent( TouchEvent message ) {
int x = message.getX( 1 );
int y = message.getY( 1 );
if( x < 0 || y < 0 || x > getExtent().width || y > getExtent().height ) {
// Outside the field
return false;
}
switch( message.getEvent() ) {
case TouchEvent.UNCLICK:
fieldChangeNotify(0);
return true;
}
return super.touchEvent( message );
}
protected boolean navigationClick(int status, int time) {
if (status != 0) { // you did not have this check
fieldChangeNotify(0);
}
return true;
}
protected boolean trackwheelClick( int status, int time )
{
if (status != 0) fieldChangeNotify(0);
return true;
}
};
verMas.setCookie(est);
verMas.setChangeListener(new FieldChangeListener(){
public void fieldChanged(Field field, int context) {
final Establecimiento est = (Establecimiento)field.getCookie();
//if(field instanceof )
/*UiApplication.getUiApplication().invokeLater(new Runnable(){
public void run(){
UiApplication.getUiApplication().pushScreen(new DetalleEstablecimientoScreen(est));
}
});*/
Runnable runnable = new Runnable(){
public void run(){
UiApplication.getUiApplication().pushScreen(new DetalleEstablecimientoScreen(est));
}
};
PleaseWaitPopupScreen.showScreenAndWait(runnable, "Cargando...");
}
});
EncodedImage eImage = EncodedImage.getEncodedImageResource("img/det_fb.png");
eImage = Utilities.resizeToWidth(eImage, (int) (Display.getWidth() / 16), (int) (Display.getWidth() / 16), true);
BitmapField bmFb = new BitmapField(eImage.getBitmap(), Field.FOCUSABLE){
protected boolean touchEvent( TouchEvent message ) {
int x = message.getX( 1 );
int y = message.getY( 1 );
if( x < 0 || y < 0 || x > getExtent().width || y > getExtent().height ) {
// Outside the field
return false;
}
switch( message.getEvent() ) {
case TouchEvent.UNCLICK:
fieldChangeNotify(0);
return true;
}
return super.touchEvent( message );
}
protected boolean navigationClick(int status, int time) {
if (status != 0) { // you did not have this check
fieldChangeNotify(0);
}
return true;
}
protected boolean trackwheelClick( int status, int time )
{
if (status != 0) fieldChangeNotify(0);
return true;
}
};
bmFb.setCookie(est);
bmFb.setChangeListener(new FieldChangeListener(){
public void fieldChanged(Field field, int context){
try{
DbConnection dbHelper = new DbConnection();
dbHelper.open();
OfertaDAO oDao = new OfertaDAO(dbHelper.db);
final Establecimiento est = (Establecimiento)field.getCookie();
final Oferta oferta = oDao.getOferta(est.getIdEstablecimiento());
dbHelper.close();
UiApplication.getUiApplication().invokeLater(new Runnable(){
public void run(){
try{
String post = "Banco de Bogotá";
if(oferta.getDescripcion() != null){
post = oferta.getDescripcion();
}
//String [] opciones = {"Aceptar", "Cancelar"};
int respuesta = Dialog.ask(Dialog.D_OK_CANCEL, "Usted está a punto de compartir la siguiente oferta:\n" + post, Dialog.YES);
if(respuesta == Dialog.D_OK){
new FacebookPost(oferta, est);
}
}
catch(Exception ex){
Dialog.alert("ERROR: " + ex.toString());
}
}
});
} catch(Exception ex){
UiApplication.getUiApplication().invokeLater(new Runnable(){
public void run(){
Dialog.alert("Error al compartir");
}
});
}
}
});
eImage = EncodedImage.getEncodedImageResource("img/det_tw.png");
eImage = Utilities.resizeToWidth(eImage, (int) (Display.getWidth() / 16), (int) (Display.getWidth() / 16), true);
BitmapField bmTw = new BitmapField(eImage.getBitmap(), Field.FOCUSABLE){
protected boolean touchEvent( TouchEvent message ) {
int x = message.getX( 1 );
int y = message.getY( 1 );
if( x < 0 || y < 0 || x > getExtent().width || y > getExtent().height ) {
// Outside the field
return false;
}
switch( message.getEvent() ) {
case TouchEvent.UNCLICK:
fieldChangeNotify(0);
return true;
}
return super.touchEvent( message );
}
protected boolean navigationClick(int status, int time) {
if (status != 0) { // you did not have this check
fieldChangeNotify(0);
}
return true;
}
protected boolean trackwheelClick( int status, int time )
{
if (status != 0) fieldChangeNotify(0);
return true;
}
};
bmTw.setCookie(est);
bmTw.setChangeListener(new FieldChangeListener(){
public void fieldChanged(Field field, int context){
try{
DbConnection dbHelper = new DbConnection();
dbHelper.open();
OfertaDAO oDao = new OfertaDAO(dbHelper.db);
final Establecimiento est = (Establecimiento)field.getCookie();
final Oferta oferta = oDao.getOferta(est.getIdEstablecimiento());
dbHelper.close();
UiApplication.getUiApplication().invokeLater(new Runnable(){
public void run(){
String tuit = "tweeteando desde fidelity";
if(oferta.getDescripcion() != null){
tuit = oferta.getDescripcion();
}
//String [] opciones = {"Aceptar", "Cancelar"};
int respuesta = Dialog.ask(Dialog.D_OK_CANCEL, "Usted está a punto de compartir la siguiente oferta:\n" + tuit, Dialog.OK);
if(respuesta == Dialog.OK){
UiApplication.getUiApplication().pushScreen(new BrowserFieldScreen(tuit));
}
}
});
} catch(Exception ex){
UiApplication.getUiApplication().invokeLater(new Runnable(){
public void run(){
Dialog.alert("Error al compartir");
}
});
}
}
});
BitmapField pin = new BitmapField(Bitmap.getBitmapResource("img/pin.png"), Field.FOCUSABLE){
protected boolean touchEvent( TouchEvent message ) {
int x = message.getX( 1 );
int y = message.getY( 1 );
if( x < 0 || y < 0 || x > getExtent().width || y > getExtent().height ) {
// Outside the field
return false;
}
switch( message.getEvent() ) {
case TouchEvent.UNCLICK:
fieldChangeNotify(0);
return true;
}
return super.touchEvent( message );
}
protected boolean navigationClick(int status, int time) {
if (status != 0) { // you did not have this check
fieldChangeNotify(0);
}
return true;
}
protected boolean trackwheelClick( int status, int time )
{
if (status != 0) fieldChangeNotify(0);
return true;
}
};
pin.setCookie(new Long(est.getIdEstablecimiento()));
//pin.setChangeListener(this);
pin.setChangeListener(new FieldChangeListener(){
public void fieldChanged(Field field, int context) {
final long idEstablecimiento = ((Long)field.getCookie()).longValue();
//if(field instanceof )
UiApplication.getUiApplication().invokeLater(new Runnable(){
public void run(){
UiApplication.getUiApplication().pushScreen(new MapaScreen(idEstablecimiento, "establecimiento"));
}
});
}
});
elf1.add(new BitmapField(bm));
elf1.add(new LabelField(est.getNombre(), LabelField.ELLIPSIS));
elf1.add(new LabelField(desc, LabelField.ELLIPSIS));
elf1.add(verMas);
elf1.add(pin);
elf1.add(bmFb);
elf1.add(bmTw);
vfm.add(elf1);
}
} catch (Exception ex){
}
add(vfm);
//UiApplication.getUiApplication().requestForeground();
//invalidate();
}
}
I would be interested to know what device and OS level you are seeing this on.
I don't think we can figure out your problem from the information presented, we are just guessing. If you want some more detailed assistance, you will need to give example code. I appreciate that you may not want to post your own code, but perhaps you can create a small sample using an array of Data.
That said, my experience is that on the initial load of the screen, if there is a delay, then it is most likely a delay in loading content rather than the screen itself. So for example, if you are reading data and images off the SD Card to populate the screen, then that will slow it down. In fact you should not be doing this, you should populate your screen with 'filers' and then update by reading the required data off the SD Card and updating the screen as it goes from a Background Thread.
The other thing that comes to mind is that updating a screen with lots of managers present, can cause each of these managers to have to reposition. Once you get a large number of Managers, this update can introduce noticeable lag. But you would probably needs 100's for Managers/Fields to notice this. And this laying out occurs when the Screen is on display, not during construction.
Finally have you seen any signs of storage shortage, for example an hour glass or watch face icon on the screen during this slow processing.
I hope the above helps, if not, you may have to give us representative code.
Update
Looking at your code, it would seem you could do a lot of optimisation of this code before converting to asynchronous loading. Remember Bitmap manipulation is expensive in terms of performance. I would recommend proceeding in two stages:
Move the repeated code out of the loop
Do the loading that is outside the loop only once.
Here as example of step 1.
Currently inside the loop you have:
eImage = EncodedImage.getEncodedImageResource("img/det_tw.png");
eImage = Utilities.resizeToWidth(eImage, (int) (Display.getWidth() / 16), (int) (Display.getWidth() / 16), true);
BitmapField bmTw = new BitmapField(eImage.getBitmap(), Field.FOCUSABLE){
Outside the loop do
int displayWidth = Display.getWidth(); // I was told this was an expensive call so minimise its use
EncodedImage det_twEImage = EncodedImage.getEncodedImageResource("img/det_tw.png");
det_twEImage = Utilities.resizeToWidth(det_twEImage, (int) (displayWidth / 16), (int) (displayWidth / 16), true);
Bitmap bmTwBitmap = eImage.getBitmap();
and in the loop:
BitmapField bmTw = new BitmapField(bmTwBitmap, Field.FOCUSABLE){
And an example of step 2.
Currently you have this:
public List(Categoria categoria){
Bitmap bmap = Bitmap.getBitmapResource("img/fondo.png");
You could look at doing this:
static Bitmap bmap = null;
public List(Categoria categoria){
if ( bmap == null ) {
bmap = Bitmap.getBitmapResource("img/fondo.png");
)
This specific example is perhaps not so relevant, but you can appreciate that this approach can stop doing work twice.
Most large applications that I have worked on have implement some sort of caching for Bitmaps, which generally will involve preloading (while a splash screen is being displayed perhaps) all the static Bitmaps and dynamically loading but retaining a reference to other used Bitmaps so that they are only loaded once. Bitmaps are immutable, so you can reference them as many times as you like in BitmapFields.
There are few other things that might be contributing but one bit of code you need to look at is replacing this:
byte [] data = new byte[(int) fc.fileSize()];
data = IOUtilities.streamToBytes(is);
with
byte [] data = IOUtilities.streamToBytes(is);
which saves allocating and then throwing away a large chunk of memory.
If you do all this, and asynchronously load the images you can't load once, I think you will find performance improves significantly. Also as an experiment, always use the (loaded once) default image for your list items and that will give you an impression of how fast things will be.
Have you checked the event log on the device or simulator to see if an exception is thrown? You can see it by going to the device home screen, holding 'alt' and pressing L G L G. It could be something that goes wrong on the first paint, and then the user interaction with the trackpad causes a refresh.
All of the answers given are very useful to have a better app and I'll do them, but the actual "error" I've found after debugging the code couldn't be guessed by the code I had given. The error was in a LabelField I passed to the manager, inside the manager I was changing the font of the Labelfield and was causing this crazy error. To resolve it I simply changed the font in the constructor of the mainscreen rather than inside the manager.
Potential problems with your code:
Performing IO (DB/file/network connections) on the Main thread
FieldchangeListeners without checking for programmatic context (yes, that second param to fieldChanged is actually useful).
Multiple events handled for a single user input (touchEvent + navigationClick + trackwheelClick)
Even if doing IO in the Main thread is not the problem for your current data set, it is a bad practice, and you might run into problems in the future.
What you should be doing:
A)
Show loading dialog
Fetch data in background
Initialize list screen
Hide loading dialog
Change to list screen
or
B)
Change to list screen
Show loading dialog or message
Fetch data in BG
Initialize list
Hide loading dialog/message

Custom List With Images in Blackberry

I want to implement something like this in my application:
That is, each image contains one heart icon. I want to handle the click event on heart click and for that I have the following code
list.setEmptyString("No Image Available", DrawStyle.HCENTER);
list.setRowHeight(Display.getHeight() - 100);
list.setSize(data.size());
if (listVManager != null && listVManager.getFieldCount() > 0) {
listVManager.deleteAll();
}
list.setCallback(new ListFieldCallback() {
public void drawListRow(ListField list, Graphics graphics,
int index, int y, int w) {
int yPos = y + list.getRowHeight() - 1;
graphics.setColor(0x434343);
graphics.fillRect(0, y, w, list.getRowHeight());
if (logoThumbnailImage != null
&& logoThumbnailImage.length > index
&& logoThumbnailImage[index] != null) {
EncodedImage img = logoThumbnailImage[index];
graphics.drawImage(0, y + 10, Display.getWidth(),
Display.getHeight() - 100, img, 0, 0, 0);
graphics.drawText("Hello", 10,
Display.getHeight() - 150);
graphics.drawImage(Display.getWidth() - 70,
Display.getHeight() - 150 + 300,
heart.getWidth(), heart.getHeight(), heart,
0, 0, 0);
} else {
graphics.drawImage(
15,
y + 10,
Display.getWidth(),
Display.getHeight() - 100,
sizeImage(iconImage, Display.getWidth(),
Display.getHeight() - 100), 0, 0, 0);
}
graphics.drawText("Hello", 10,
Display.getHeight() - 150);
graphics.drawLine(0, yPos, w, yPos);
}
public Object get(ListField listField, int index) {
return null;
}
public int getPreferredWidth(ListField listField) {
return Display.getWidth();
}
public int indexOfList(ListField listField, String prefix,
int start) {
return 0;
}
});
listVManager.add(list);
loadImages = new LoadImages(80, 80);
loadImages.start();
}
});
here load image is thread that load images in background and store them in logoThumbnailImage array and invalidate list from there when the it loads the image.
The Load image thread class:
private class LoadImages extends Thread {
int widthL;
int heightL;
LoadImages(int width, int height) {
this.widthL = width;
this.heightL = height;
}
public void run() {
logoThumbnailImage=new EncodedImage[numberOfItem];
if (object != null) {
for (int i = 0; i < numberOfItem; i++) {
try {
String text=object[i].getJSONArray("UrlArray").getString(0).toString();
EncodedImage encodedImg = JPEGEncodedImage.encode(connectServerForImage(text), quality); //connectserverForImage load Images from server
logoThumbnailImage[i] = sizeImage(encodedImg, Display.getWidth(), Display.getHeight()-100);
list.invalidate();
} catch (Exception e)
{
e.printStackTrace();
}
}
} else {
UiApplication.getUiApplication().invokeLater(new Runnable() {
public void run() {
Dialog.alert("No Data Found");
}
});
}
}
}
The application runs smoothly but I got the following output:
I have the following problem
1. The heart and description is displayed on only one list row. Can any one tell me what I am missing?
2. How to perform the click event on heart
Having looked at this only briefly, the problem appears to be that you are, in places, ignoring the 'y' position that is passed in to your drawListRow() method:
public void drawListRow(ListField list, Graphics graphics,
int index, int y, int w) {
Effectively the 'canvas' that you should be using to paint the current row (the row identified using int index) is bounded by the rectangle
(0, y, w, list.getRowHeight()).
In fact, you can actually paint anywhere in the extent that belongs to the ListField, i.e. the area you can paint onto is actually the rectangle
(0, 0, list.getWidth(), list.getHeight()).
You can do this, but you shouldn't. If you go outside your row's rectangle you will be painting over another row.
In your case, painting outside the selected row is exactly what your code does. You do this:
graphics.drawText("Hello", 10,
Display.getHeight() - 150);
This will actually be positioned on the ListField, 10 pixels in from the left and Display.getHeight() - 150 down from the top. It will be positioned at this point in the ListField, regardless of which row you are painting. So every row will put the Hello text in the same place.
So when coding your drawListRow(), make sure you offset all the positions to stay within the bounds of the row you are supposed to be painting. The origin of the area you are painting is (0, y), so offset all vertical positions using y. Do not use Display.getHeight(), use list.getRowHeight() to get the height you can paint (starting at y), and do not use Display.getWidth(), use the w variable that is passed in to get the width that you can paint. All your graphics actions should occur within these bounds.

Is there any LazyLoader for images to load image in ListField in BlackBerry?

I am new to BlackBerry development. But good about android.
I want to load Images coming from the server in ListField.
I have implement like below code but not getting success:
package mypackage;
public class TempScreen extends MainScreen implements ListFieldCallback{
Bitmap[] images=null;
private ListField mylist;
private static Bitmap _bitmap;
private ImageDownloader downloader;
int size = 0;
String[] urls={
"http://www.kentnews.co.uk/polopoly_fs/damian_lewis_at_port_lympne_wild_animal_park_c_taf_1_1738362!image/2626063106.jpg_gen/derivatives/landscape_225/2626063106.jpg",
"http://www.kentnews.co.uk/polopoly_fs/damian_lewis_at_port_lympne_wild_animal_park_c_taf_1_1738362!image/2626063106.jpg_gen/derivatives/landscape_225/2626063106.jpg",
"http://www.kentnews.co.uk/polopoly_fs/damian_lewis_at_port_lympne_wild_animal_park_c_taf_1_1738362!image/2626063106.jpg_gen/derivatives/landscape_225/2626063106.jpg",
"http://www.kentnews.co.uk/polopoly_fs/damian_lewis_at_port_lympne_wild_animal_park_c_taf_1_1738362!image/2626063106.jpg_gen/derivatives/landscape_225/2626063106.jpg"};
public TempScreen()
{
images=new Bitmap[urls.length];
size = urls.length;
mylist = new ListField();
mylist.setCallback(this);
mylist.setSize(4);
mylist.setRowHeight(getFont().getHeight() * 3);
add(mylist);
Thread downloader=new Thread(new ImageDownloader());
downloader.start();
}
public void drawListRow(ListField listField, Graphics graphics, int index,
int y, int width) {
if(images[index]==null)
{
//Load placeholder image
_bitmap = Bitmap.getBitmapResource("close_btn.png");// load some bitmap
// of your choice
// here
}
else
//Load Bitmap
_bitmap = images[index];
graphics.drawText("row details", 100, y + 30);
//graphics.drawBitmap(0, y, _bitmap.getWidth(), _bitmap.getHeight(),_bitmap, 0, 0);
mylist.invalidate(index);
}
public class ImageDownloader implements Runnable
{
public void run()
{
for(int i=0; i<size;i++)
{
if(images[i]==null)
{
images[i]=connectServerForImage(urls[i].toString());//replace downloadImage method to whatever method you have to download the bitmap from url
UiApplication.getUiApplication().invokeLater(new Runnable(){
public void run()
{
mylist.invalidate();
}
});
}
}
}
}
public Object get(ListField listField, int index) {
// TODO Auto-generated method stub
return null;
}
public int getPreferredWidth(ListField listField) {
// TODO Auto-generated method stub
return 0;
}
public int indexOfList(ListField listField, String prefix, int start) {
// TODO Auto-generated method stub
return 0;
}
public static Bitmap connectServerForImage(String url) {
HttpConnection httpConnection = null;
DataOutputStream httpDataOutput = null;
InputStream httpInput = null;
int rc;
Bitmap bitmp = null;
try {
// httpConnection = (HttpConnection)
// Connector.open(url+";interface=wifi");
httpConnection = (HttpConnection) Connector.open(url);
rc = httpConnection.getResponseCode();
// System.out.println("===============================");
Dialog.alert("beore if condition");
if (rc == HttpConnection.HTTP_OK) {
System.out.println(" ============= IN FUNCTION. . . . .");
httpInput = httpConnection.openInputStream();
InputStream inp = httpInput;
byte[] b = IOUtilities.streamToBytes(inp);
EncodedImage hai = EncodedImage.createEncodedImage(b, 0,
b.length);
bitmp = hai.getBitmap();
} else {
throw new IOException("HTTP response code: " + rc);
}
} catch (Exception ex) {
System.out.println("URL Bitmap Error........" + ex.getMessage());
} finally {
try {
if (httpInput != null)
httpInput.close();
if (httpDataOutput != null)
httpDataOutput.close();
if (httpConnection != null)
httpConnection.close();
} catch (Exception e) {
e.printStackTrace();
}
}
return bitmp;
}
}
Dont know where i am wrong. Please can any budy help me for the same.
Several problems with your code:
The BitmapLazyLoader class looks like a consumer. It holds a Thread reference. This alone is very confusing, since Runnables are intended to be passed to a Thread constructor, but Runnables should not know about the thread for the sake of encapsulation. Letting this apart, this class attempts to spawn a thread only once, but as you are creating an instance of Runnable each time a row is drawn, you'll end up spawning a considerable number of threads. This will probably end in a TooManyThreadsException being thrown as in BlackBerry the max number of threads is limited to 16 per app. Even if you don reach the limit, performance will degrade as BlackBerries, which sport a single core CPU, you shouldn't have more than 2-3 threads running at the same time. EVEN if you could spawn infinite threads, in BlackBerry you can only have X connections opened at the same time (I think X is 5 for the whole OS, not sure about this). So first of all modify the code to ensure only a single worker thread is downloading images. (and if possible, extract the thread instantiation and launch out of the Runnable class).
When the bitmap is downloaded, you are not doing anything with it. Look at the ImageDownloadCompleted method, it is empty. (BTW, the convention for methods is to start with lowercase) So you should store the bitmap somewhere and call invalidate on your list, which in turn will paint the stored bitmaps.
Hope it helps.
You can try using this link :
http://www.coderholic.com/blackberry-webbitmapfield/
You have to create a separate class named as WebBitmapField as suggested in above link.
How to use that class in your list field image objects:
For every image url create WebBitmapField object
photoList_vector is the vector through which populate elements in
list field
WebBitmapField web = new WebBitmapField("http://www.image1.png");
photoList_vector.addElement(web);
web = new WebBitmapField("http://www.image2.png");
photoList_vector.addElement(web);
Now use this vector to work on your list field......
In the above lines we try to ensure that when we simultaneously send multiple requests to get the images then each image corresponds to a particular WebBitmapField Object.
Each object is then added to vector so that it can be added to the list field.
Each url send is tied to an object of WebBitmapField.
So though request is send in a separate thread it gets tied to its associated object only
Hope it helps
:)
I have worked on this problem, earlier, and I am posting my technique here, though its not ideal solution, as it was coupled very much with Screen class, but still might be helpful.
First in your screen class have one array for bitmaps having size equal to list field items.
public class TempScreen extends MainScreen{
Bitmap[] images=null;
String[] urls={"image1_url", "image2_url".....};
public TempScreen()
{
images=new Bitmap[urls.length];
}
now in drawListRow method of ListFieldCallBack, check for the following:
public void drawListRow(ListField list, Graphics g, int index, int y, int width){
if(bitmap[index]==null)
{
//Load placeholder image
}
else
//Load Bitmap
}
Now create a thread class to download the images:
public class ImageDownloader implements Runnable
{
public void run()
{
for(int i=0; i<size;i++)
{
if(images[i]==null)
{
images[i]=downloadImage(url[i]);//replace downloadImage method to whatever method you have to download the bitmap from url
UiApplication.getUiApplication().invokeLater(new Runnable(){
public void run()
{
list.invalidate()
}
});
}
}
}
}
Now in constructor of the screen class, after setting callback to listfield, start thread:
Thread downloader=new Thread(new ImageDownloader());
downloader.start();
Edit: Change TempScreen constructor to following:
public TempScreen()
{
images=new Bitmap[urls.length];
size = urls.length;
mylist = new ListField();
mylist.setCallback(this);
mylist.setSize(4);
mylist.setRowHeight(getFont().getHeight() * 3);
add(mylist);
Thread downloader=new Thread(new ImageDownloader());
downloader.start();
}

Saving bmp image multiple times on the same path C#

So, I'm working on my paint application. Every time I make changes, the current screen state is copied and saved as a bitmap image on my disk (so I can use it in my paint event).
The problem occurs when I minimize and return the window to its normal state and then try to draw. This triggers my event reacting to changes, the program tries to save the image ---->>> kabooom.
It says "A generic error occurred in GDI+".. So, I've been surfing through various forums in search for the answer but none of them gave me true answer, they all mention wrong paths etc. but I'm pretty sure that's not the problem. Do I have to dispose bitmap or do something with the stream?
int width = pictureBox1.Size.Width;
int height = pictureBox1.Size.Height;
Point labelOrigin = new Point(0, 0); // this is referencing the control
Point screenOrigin = pictureBox1.PointToScreen(labelOrigin);
int x = screenOrigin.X;
int y = screenOrigin.Y;
Rectangle bounds = this.Bounds;
using (Bitmap bitmap = new Bitmap(width, height))
{
using (Graphics g = Graphics.FromImage(bitmap))
{
g.CopyFromScreen(new Point(x, y), Point.Empty, bounds.Size);
}
bitmap.Save(_brojFormi + ".bmp", System.Drawing.Imaging.ImageFormat.Bmp);
}
You're saving an image to disk so you can use it in another event? Wow.
Why not just use a class-global variable to store the bitmap?
class MyForm
{
Bitmap currentImage = null;
Graphics gfx = null;
private void btnLoad_Click(object sender, EventArgs e)
{
// ...
currentImage = new Bitmap(fileName);
gfx = Graphics.FromImage(currentImage);
}
private void pbEditor_Paint(object sender, PaintEventArgs e)
{
if (currentImage != null && gfx != null)
{
lock(currentImage) e.Graphics.DrawImage(currentImage, ...);
}
}
private void pbEditor_Click(object sender, MouseEventArgs e)
{
// quick example to show bitmap drawing
if (e.Button == MouseButtons.Left)
lock(currentImage) currentImage.SetPixel(e.Location.X, e.Location.Y, Colors.Black);
}
}

Blackberry: Dowloading images in custom field slows VerticalFieldManager

Here is the paint method of my custom field:
protected void paint(Graphics graphics) {
new downloadImage();
Bitmap img = downloadImage.connectServerForImage(this.poster);
graphics.drawBitmap(0, 0, 100, 150, img, LEFT, TOP);
}
Here is the connectServerForImage method:
public static Bitmap connectServerForImage(String url) {
HttpConnection httpConnection = null;
DataOutputStream httpDataOutput = null;
InputStream httpInput = null;
int rc;
Bitmap bitmp = null;
try {
httpConnection = (HttpConnection) Connector.open(url);
rc = httpConnection.getResponseCode();
if (rc != HttpConnection.HTTP_OK) {
throw new IOException("HTTP response code: " + rc);
}
httpInput = httpConnection.openInputStream();
InputStream inp = httpInput;
byte[] b = IOUtilities.streamToBytes(inp);
EncodedImage hai = EncodedImage.createEncodedImage(b, 0, b.length);
return hai.getBitmap();
} catch (Exception ex) {
System.out.println("URL Bitmap Error........" + ex.getMessage());
} finally {
try {
if (httpInput != null)
httpInput.close();
if (httpDataOutput != null)
httpDataOutput.close();
if (httpConnection != null)
httpConnection.close();
} catch (Exception e) {
e.printStackTrace();
}
}
return bitmp;
}
I'm putting multiple instances of my custom field into a verticalfieldmanager, but the images slows the scrolling down. It seems as if every time I scroll it runs the paint method again even though the image has already been downloaded.
I'm thinking I will need to download the images in another thread? Someone lead me in the right direction.
You must start any HTTP interaction in a separate thread (no blocking call).
To avoid downloading same image multiple times cache already downloaded image. You can use image download URL as image tag to store
them.
Every time there is a event on screen, paint method will be called. So don't start download an image if it is already downloaded.

Resources