пятница, 8 января 2010 г.

Проблемы при запуске palm-inspector

При запуске palm-inspector в Ubuntu 9.10 получил сообщение
palm-inspector: error while loading shared libraries: libicui18n.so.38: cannot open shared object file: No such file or directory

Причина - в репах karmic лежит libicu40 и видимо версия 3.8 стала не нужна. Решение - найти эту библиотеку где-нибудь (например в репе для 9.04)

четверг, 15 октября 2009 г.

BlackBerry Layer UI Manager

     Выбор layout manager (на русский это лучше не переводить) в библиотеке BlackBerry явно не блещет разнообразием. Абстрактный Manager и его наследники FlowFieldManager, HorizontalFieldManager, VerticalFieldManager - все, на что решились ребята из RIM (есть конечно еще DialogFieldManager, но это комбинация предыдущих...).
     Может этого и хватит для страшненького простенького интерфейса, но для чего-то более приятного глазу явно мало. В официальной документации советуют создавать собственные имплементации, чем и приходится заниматься.
     Наиболее востребованными оказались 2 метода расположения элементов:
  • слоистый (каждый следующий добавленный элемент располагается сверху предыдущего, левый верхний угол - точка (0,0))
  • абсолютный (элементы помещаются на родителя с указанием абсолютных координат (x,y) )
     Если второй метод я мог заменить компонованием разных лейаутов (это в 90% случаев получалось), то первый заменить стандартными менеджерами не представлялось возможным в рамках поставленной задачи.
вот что из этого получилось. Метод №1:
import net.rim.device.api.ui.Field;
import net.rim.device.api.ui.Manager;
public class AXHSimpleLayeredFieldManager extends Manager {

 public AXHSimpleLayeredFieldManager(long l) {
  super(l);
 }

 protected void sublayout(int width, int height) {
                //Display.getWidth() || Graphics.getScreenWidth()
  int maxWidth = Config.SCREEN_WIDTH;
                //Display.getHeight() || Graphics.getScreenHeight()
  int maxHeight = Config.SCREEN_HEIGHT;//
  for (int i = 0, n = getFieldCount(); i < n; i++) {
   Field field = getField(i);
   layoutChild(field, Display.getWidth(), Display.getHeight());
   long fieldStyle = field.getStyle();
   long fieldHalign = fieldStyle & Field.FIELD_HALIGN_MASK;
   int fieldXOffset = 0;
   if (fieldHalign == Field.FIELD_RIGHT) {
    fieldXOffset = maxWidth - field.getWidth();
   } else if (fieldHalign == Field.FIELD_HCENTER) {
    fieldXOffset = (maxWidth - field.getWidth()) / 2;
   }
   int fieldYOffset = 0;
   long fieldValign = fieldStyle & Field.FIELD_VALIGN_MASK;
   if (fieldValign == Field.FIELD_BOTTOM) {
    fieldYOffset = maxHeight - field.getHeight();
   } else if (fieldValign == Field.FIELD_VCENTER) {
    fieldYOffset = (maxHeight - field.getHeight()) / 2;
   }
   setPositionChild(field, fieldXOffset, fieldYOffset);
  }
  setExtent(maxWidth, maxHeight);
 }
}

Blackberry GIF Rendering

Как известно, рендеринг анимированной гифки в BlackBerry отсутствует. Решение - костыль, рисующий кадр за кадром в отдельном потоке, представленный RIM в базе знаний .
Единственная проблема - алгоритм не поддерживает оптимизированные gif.

Небольшая модификация кода от RIM позволяет эту проблему решить
Рисуем кадр-за кадром на отдельном битмапе, который впоследствии отображается на экране.

...
private Bitmap background;
...
 protected void paint(Graphics graphics) {
//Optimized gif support: draw frame-by-frame on the background layer
  Graphics g = new Graphics(background);
  g.drawImage(_image.getFrameLeft(_currentFrame), _image
    .getFrameTop(_currentFrame), _image
    .getFrameWidth(_currentFrame), _image
    .getFrameHeight(_currentFrame), _image, _currentFrame, 0, 0);
  graphics.drawBitmap(0, 0, background.getWidth(),
    background.getHeight(), background, 0, 0);

 }

Весь код:
import net.rim.device.api.system.Bitmap;
import net.rim.device.api.system.GIFEncodedImage;
import net.rim.device.api.ui.Graphics;
import net.rim.device.api.ui.UiApplication;
import net.rim.device.api.ui.component.BitmapField;

//A field that displays an animated GIF.

public class AnimatedGIFField extends BitmapField {
 private GIFEncodedImage _image; // The image to draw.
 private int _currentFrame; // The current frame in
 // the animation sequence.
 private int _width; // The width of the image
 // (background frame).
 private int _height; // The height of the image
 // (background frame).
 private AnimatorThread _animatorThread;
 private Bitmap background;

 public AnimatedGIFField(GIFEncodedImage image) {
  this(image, 0);
 }

 public AnimatedGIFField(GIFEncodedImage image, long style) {
  // Call super to setup the field with the specified style.
  // The image is passed in as well for the field to
  // configure its required size.
  super(image.getBitmap(), style);

  // Store the image and it's dimensions.
  _image = image;
  _width = image.getWidth();
  _height = image.getHeight();
  background = image.getBitmap();
  // Start the animation thread.
  _animatorThread = new AnimatorThread(this);
  _animatorThread.start();
 }

 protected void paint(Graphics graphics) {
//Optimized gif support: draw frame-by-frame on the background layer
  Graphics g = new Graphics(background);
  g.drawImage(_image.getFrameLeft(_currentFrame), _image
    .getFrameTop(_currentFrame), _image
    .getFrameWidth(_currentFrame), _image
    .getFrameHeight(_currentFrame), _image, _currentFrame, 0, 0);
  graphics.drawBitmap(0, 0, background.getWidth(),
    background.getHeight(), background, 0, 0);

 }

 // Stop the animation thread when the screen the field is on is
 // popped off of the display stack.
 protected void onUndisplay() {
  _animatorThread.stop();
  super.onUndisplay();
 }

 // A thread to handle the animation.
 private class AnimatorThread extends Thread {
  private AnimatedGIFField _theField;
  private boolean _keepGoing = true;
  private int _totalFrames; // The total number of
  // frames in the image.
  private int _loopCount; // The number of times the
  // animation has looped (completed).
  private int _totalLoops; // The number of times the animation should

  // loop (set in the image).

  public AnimatorThread(AnimatedGIFField theField) {
   _theField = theField;
   _totalFrames = _image.getFrameCount();
   _totalLoops = _image.getIterations();

  }

  public synchronized void stop() {
   _keepGoing = false;
  }

  public void run() {
   while (_keepGoing) {
    // Invalidate the field so that it is redrawn.
    UiApplication.getUiApplication().invokeAndWait(new Runnable() {
     public void run() {
      _theField.invalidate();
     }
    });

    try {
     // Sleep for the current frame delay before
     // the next frame is drawn.
     sleep(_image.getFrameDelay(_currentFrame) * 10);
    } catch (InterruptedException iex) {
    } // Couldn't sleep.

    // Increment the frame.
    ++_currentFrame;

    if (_currentFrame == _totalFrames) {
     // Reset back to frame 0 if we have reached the end.
     _currentFrame = 0;

     ++_loopCount;

     // Check if the animation should continue.
     if (_loopCount == _totalLoops) {
      _keepGoing = false;
     }
    }
   }
  }
 }
}