我有一个关于BlackBerry VerticalScrollField和滚动的问题,这似乎会锁定或使UI不稳定。以下代码是BlackBerry屏幕,左侧是世界内容(在滚动字段中),右侧是跳转栏,允许单击内容。
当单击跳转字母时,会调用setVerticalScroll方法,它会执行滚动,但会产生令人不快的副作用,即呈现UI不稳定或不可用。滚动调用是在UI线程上完成的,因此不清楚错误的来源是什么。该应用程序正在6.0模拟器中进行测试。
我已经包含了可以复制到BB Eclipse中进行黑客攻击/测试的类。
可以使用以下代码向底部找到滚动的部分:
UiApplication.getUiApplication().invokeLater(new Runnable(){
public void run() {
scroller.setVerticalScroll(y, true);
}});
这是完整的课程:
package test;import java.util.Vector;
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;
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;
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); } }
}
答案 0 :(得分:1)
您在已经在UI事件线程中的一些地方使用UiApplication.invokeLater,因此这些是冗余的 - keyChar中的调试代码和来自touchEvent处理程序的setVerticalScroll调用。当您从UI线程执行invokeLater时,Runnable是同步执行的,没有指定延迟。
您确定要明确设置滚动吗?一个选项是通过调用setFocus()将焦点设置在您感兴趣的WordField上,然后操作系统将执行滚动事件以在屏幕上移动该字段。
如果您确实需要明确设置垂直滚动,则问题可能是触摸事件已经导致滚动,因此再次设置会导致问题。你可以通过为invokeLater(...)指定一毫秒的延迟来解决这个问题。这意味着您的Runnable将被添加到事件队列中,而不是同步执行。这样,滚动不会在另一个事件调用堆栈的中间更改。
答案 1 :(得分:0)
最后追踪问题 - 如果字母标签字段的touchEvent返回true,则它会锁定主滚动字段,但是如果调用返回super.touchEvent(message)则滚动发生且滚动字段仍然可以单击屏幕上下滚动。
这可能是BlackBerry OS或模拟器中的错误。 6.0的Field.touchEvent()文档建议如果方法使用该事件则返回true;但是这样做(至少在上面的代码中)导致另一个UI字段失去检测触摸事件的能力,这会导致它滚动。