Вертикальная полоса прокрутки с точками перехода - setVerticalScroll Lock UI - PullRequest
1 голос
/ 09 февраля 2011

У меня есть вопрос о BlackBerry VerticalScrollField и прокрутке, которая, кажется, блокирует или делает пользовательский интерфейс нестабильным. Следующий код представляет собой экран BlackBerry с мирами в виде содержимого слева (в поле прокрутки) и с полосой переключения вправо, которая позволяет щелкать содержимое.

Когда щелкают букву перехода, вызывается метод setVerticalScroll, он выполняет прокрутку, но имеет нежелательный побочный эффект, делая пользовательский интерфейс нестабильным или непригодным для использования. Вызов прокрутки выполняется в потоке пользовательского интерфейса, поэтому неясно, каков источник ошибки. Приложение тестируется в симуляторе 6.0.

Я включил класс, который можно скопировать в BB Eclipse для взлома / тестирования.

Секция, которая запускает прокрутку, находится внизу со следующим кодом:

 UiApplication.getUiApplication().invokeLater(new Runnable(){
     public void run() {
         scroller.setVerticalScroll(y, true);
 }});

Вот полный класс:

package test;</p>

<p>import java.util.Vector;</p>

<p>import net.rim.device.api.system.ApplicationManager;
import net.rim.device.api.ui.Field;
import net.rim.device.api.ui.Font;
import net.rim.device.api.ui.Graphics;
import net.rim.device.api.ui.TouchEvent;
import net.rim.device.api.ui.UiApplication;
import net.rim.device.api.ui.component.LabelField;
import net.rim.device.api.ui.component.Status;
import net.rim.device.api.ui.container.HorizontalFieldManager;
import net.rim.device.api.ui.container.MainScreen;
import net.rim.device.api.ui.container.VerticalFieldManager;</p>

<p>public class Startup extends UiApplication {
    private int[] jump;
    static final String[] words = new String[]{
        "auto", "apple", "bear", "car", "farm", "ferret", "gold", 
                "green", "garden", "hedge", "happy", "igloo", "infrared", 
                "jelly", "kangaroo", "lemon", "lion", "marble", "moon", 
                "nine", "opera", "orange", "people", "puppy", "pear", 
        "quince", "race", "run", "sunset", "token", "willow", "zebra"
    };
    private final static String[] alphabet = new String[]{"A","B","C","D","E",
                "F","G","H","I","J","K","L","M","N","O","P","Q","R",
                "S","T","U","V","W","X","Y","Z","#"};
    private VerticalFieldManager scroller;</p>

<code>public Startup() {
    UiApplication.getUiApplication().invokeLater(new Runnable() {
        public void run() {
            UiApplication.getUiApplication().pushScreen(new ScrollScreen());
        }
    });
}

public static void main(String[] args) {
    ApplicationManager app = ApplicationManager.getApplicationManager();
    while (app.inStartup()) {
        try { Thread.sleep(200); } catch (Throwable e) {}
    }

    Startup startup = new Startup();
    startup.enterEventDispatcher();
}

/**
 * Screen with content in a scrollbar left and a letters on the right that
 * can be used to jump into the content.
 */
class ScrollScreen extends MainScreen {
    public ScrollScreen() {
        super(NO_HORIZONTAL_SCROLL | NO_VERTICAL_SCROLL);

        HorizontalFieldManager hfm = new HorizontalFieldManager(USE_ALL_HEIGHT | NO_VERTICAL_SCROLL | NO_HORIZONTAL_SCROLL){
            protected void sublayout(int maxWidth, int maxHeight) {
                Field scroll = getField(0);
                Field alpha = getField(1);
                layoutChild(alpha, maxWidth, maxHeight);
                layoutChild(scroll, maxWidth-alpha.getWidth(), maxHeight);
                setPositionChild(scroll, 0, 0);
                setPositionChild(alpha, maxWidth-alpha.getWidth(), 0);
                setExtent(maxWidth, maxHeight);
            }
        };

        hfm.add(createScrollContent());
        hfm.add(createAlphabetJumpBar());

        add(hfm);
    }

    private Field createScrollContent() {
        Vector vocabulary = new Vector();
        for (int ii=0; ii<alphabet.length; ii++) 
            vocabulary.addElement(alphabet[ii]);

        scroller = new VerticalFieldManager(VERTICAL_SCROLL | USE_ALL_WIDTH) {
            protected void sublayout(int maxWidth, int maxHeight) {
                // Record the jump offsets
                int y = 0;
                for (int ii=0; ii<getFieldCount(); ii++) {
                    Field field = getField(ii);
                    layoutChild(field, maxWidth, maxHeight);
                    setPositionChild(field, 0, y);
                    if (field instanceof WordField) {
                        WordField object = (WordField)field;;
                        char character = object.getWord().toLowerCase().charAt(0);
                        int offset = ((int)character)-(int)alphabet[0].toLowerCase().charAt(0);
                        if (offset < 0 || offset > jump.length)
                            offset = jump.length-1;
                        while (offset >= 0 && offset < jump.length && jump[offset] == 0) {
                            jump[offset] = y;
                            offset--;
                        } 
                    }
                    y += field.getHeight();
                }
                int offset = jump.length-1;
                do {
                    jump[offset] = y;
                    offset--;
                } while (offset >= 0 && jump[offset] == 0);
                setExtent(maxWidth, maxHeight);
                setVirtualExtent(maxWidth, y+10);
            }
        };

        jump = new int[alphabet.length];
        Font largeFont = Font.getDefault().derive(Font.PLAIN, 46);
        for (int ii=0; ii<words.length; ii++) {
            WordField wordField = new WordField(words[ii]);
            wordField.setFont(largeFont);
            scroller.add(wordField);
        }

        return scroller;
    }

    private Field createAlphabetJumpBar() {
        VerticalFieldManager vfm = new VerticalFieldManager() {
            protected void sublayout(int maxWidth, int maxHeight) {
                int y = 0;
                int width = 0;
                double allowedAlphaHeight = (double)maxHeight / (double)getFieldCount();
                for (int ii=0; ii<getFieldCount(); ii++) {
                    WordField field = (WordField)getField(ii);
                    layoutChild(field, maxWidth, (int)allowedAlphaHeight);
                    setPositionChild(field, 0, y);
                    y += field.getHeight();
                    double paddedY = Math.floor(allowedAlphaHeight*(ii+1));
                    if (y < paddedY) y = (int)paddedY;
                    width = Math.max(width, field.getWidth());
                }
                setExtent(width, maxHeight);
            }
        };
        for (int ii=0; ii<alphabet.length; ii++) {
            vfm.add(new AlphaField(alphabet[ii]){
                protected boolean touchEvent(TouchEvent message) {
                    if (message.getEvent() == TouchEvent.UP) {
                        int startOffset = (int)alphabet[0].charAt(0);
                        int offset = ((int)getWord().charAt(0)) - startOffset;
                        final int y = offset == 0 ? 0 : jump[offset - 1];
                        UiApplication.getUiApplication().invokeLater(new Runnable(){
                            public void run() {
                                scroller.setVerticalScroll(y, true);
                        }});
                    }
                    return true;
                }
            });
        }
        return vfm;
    }

    class WordField extends LabelField {
        private final String word;
        public WordField(String word) {
            super(word);
            this.word = word;
        }
        public String getWord() { return word; }
    }


    Font alphaFont = null;
    class AlphaField extends WordField {
        public AlphaField(String word) {
            super(word);
        }
        protected void layout(int width, int height) {
            if (alphaFont == null)
                alphaFont = Font.getDefault().derive(Font.PLAIN, height);
            setExtent(alphaFont.getAdvance(getWord()), alphaFont.getHeight());
        }
        protected void paint(Graphics graphics) {
            graphics.setFont(alphaFont);
            graphics.drawText(getWord(), 0, 0);
        }
    }

    /**
     * For debugging.
     * @see net.rim.device.api.ui.Screen#keyChar(char, int, int)
     */
    protected boolean keyChar(char c, int status, int time) {
        if ('o' == c) { // shows the jump offsets into the scroll field
            UiApplication.getUiApplication().invokeLater(new Runnable(){
                public void run() {
                    StringBuffer buf = new StringBuffer();
                    for (int ii=0; ii<jump.length; ii++) {
                        buf.append(alphabet[ii]+"="+jump[ii]);
                        if (ii<jump.length-1)
                            buf.append(",");
                    }
                    Status.show("offsets="+buf.toString());
                }});
        }
        return super.keyChar(c, status, time);
    }
}
</code>

}

Ответы [ 2 ]

1 голос
/ 09 февраля 2011

Вы используете UiApplication.invokeLater в нескольких местах, где вы уже находитесь в потоке событий пользовательского интерфейса, поэтому они являются избыточными - отладочный код в keyChar и вызов setVerticalScroll из обработчика touchEvent. Runnable выполняется синхронно, когда вы выполняете invokeLater из потока пользовательского интерфейса, без указанной задержки.

Вы уверены, что хотите установить прокрутку явно? Один из вариантов - установить фокус на интересующее вас WordField, вызвав setFocus (), после чего ОС выполнит события прокрутки, чтобы переместить это поле на экран для вас.

Если вам действительно необходимо явно установить вертикальную прокрутку, ваша проблема может заключаться в том, что событие касания уже вызывает прокрутку, поэтому повторная его установка вызывает проблемы. Вы можете обойти это, указав задержку в одну миллисекунду для invokeLater (...). Это означает, что ваш Runnable будет добавлен в очередь событий вместо выполнения синхронно. Таким образом, прокрутка не будет изменена в середине другого стека вызовов.

0 голосов
/ 10 февраля 2011

Наконец, выследили проблему - если touchEvent для поля метки алфавита возвращает true, тогда оно блокирует главное поле прокрутки, если, однако, return super.touchEvent (message) называется прокруткой, и поле прокрутки все ещепрокручивайте вверх и вниз, нажимая на экран.

Это может быть ошибка в ОС BlackBerry или просто симулятор.Документация Field.touchEvent () для 6.0 рекомендует возвращать значение true, если метод использует событие;однако при этом (по крайней мере, в приведенном выше коде) другое поле пользовательского интерфейса теряет способность обнаруживать события касания, которые могут вызвать его прокрутку.

...