【问题标题】:Refresh widget properly?正确刷新小部件?
【发布时间】:2014-02-24 11:34:59
【问题描述】:

我目前正在尝试使我们的应用程序的 UI 变得轻量级,因此我正在尝试将重量级的服务器调用转移到它们自己的线程中。然后我使用Display.getDefault.asyncExec() 更新小部件,效果很好。

我想在服务器调用返回时向用户显示动画。到那时,我正在使用另一个线程,它在另一个Display.getDefault.asyncExec() 中显示动画。这也有效......但以一种奇怪的方式。当我运行该应用程序时,小部件闪烁得很厉害,有时屏幕保持未更新状态。问题是,当我运行原型版本时,即没有主应用程序,只是一个简单的 SWT 应用程序,它可以完美运行。任何想法为什么会发生这种情况?

基本工作流程::

showAnimation = new Thread(new Runnable() {

            @Override
            public void run() {
                while(true){
                    synchronized (this) {
                        try {                       
                            Thread.sleep(1000);
                            drawCircles(); // basically the animation, also called in a Display.getDefault().asyncExec()
                        } 
                        catch (InterruptedException e) 
                        {
                            // do nothing, because this thread is meant to be interrupted!
                            // and then, break out of the infinite loop                                 
                            System.out.println("Interrupted");
                            break;
                        }
                    }                   
                }                               
            }
        });

invokeLater = new Thread(new Runnable(){
    @Override
    public void run(){
        // do the data base call here
        System.out.println("Got data!");
        if(showAnimation.isAlive())
        {
            showAnimation.interrupt();
        }
    }
});

在我看来 -->

showAnimation.start();
invokeLater.start();

我短暂地想知道我是否应该增加动画线程的优先级,只是为了看看这是否有效。它没有(也没有意义增加没有瓶颈应用程序的线程的优先级)。

有什么想法吗?

P.S:drawCircles() 代码 -->

public void drawCircles(){
        Display.getDefault().asyncExec(new Runnable() {                         
            @Override
            public void run(){
                int radius = 100;
                int maxX = radius ;
                int maxY = radius ;
                int direction=0;                        
                int centerX = table.getBounds().width/2;
                int centerY = table.getBounds().height/2;       
                System.out.println("Drawing");
                Image image = new Image(Display.getDefault(),table.getBounds().width,table.getBounds().height);
                GC gc = new GC(image);                              
                gc.setAntialias(SWT.ON);
                gc.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK));
                //gc.drawLine(0, centerY, shell.getBounds().width, centerY);
                //gc.drawLine(centerX, 0, centerX, shell.getBounds().height);
                // 1st circle
                gc.drawOval(centerX-radius/2, centerY-radius - radius/2, maxX, maxY);
                // 2nd circle
                gc.drawOval(centerX - 2*radius + radius/2, centerY - radius/2, maxX, maxY);
                // 3rd circle
                gc.drawOval(centerX+radius/2, centerY-radius/2, maxX, maxY);
                // 4th circle
                gc.drawOval(centerX-radius/2, centerY + radius/2, maxX, maxY);
                direction++;
                direction %= 4;
                switch(direction){                              
                case 0:                                 
                    gc.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK));
                    gc.fillOval(centerX - 2*radius + radius/2, centerY - radius/2, maxX, maxY);
                    break;
                case 1:                                 
                    gc.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK));
                    gc.fillOval(centerX-radius/2, centerY-radius - radius/2, maxX, maxY);
                    break;                              
                case 2:                                 
                    gc.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK));
                    gc.fillOval(centerX+radius/2, centerY-radius/2, maxX, maxY);
                    break;      
                case 3 :                                    
                    gc.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK));
                    gc.fillOval(centerX-radius/2, centerY + radius/2, maxX, maxY);
                    break;
                }
                table.setBackgroundImage(image);
                table.redraw();
                table.update();         
                image.dispose();
                gc.dispose();   
            }
        });
    }

【问题讨论】:

  • 当你说闪烁时,听起来就像动画直接在屏幕上完成而没有缓冲。向我们展示您的动画代码。也许是drawCircles?
  • 每次调用 drawCircles 时都会创建一个新线程。这不是很好,但我认为这不是闪烁的原因。 DrawCircles 似乎没有使用任何缓冲。也许这可能对你有帮助? stackoverflow.com/questions/17783525/….
  • 如果禁用抗锯齿功能会有所改善吗?
  • @Baz:我尝试禁用抗锯齿,但没有用。然后我想把代码放在一个paint listener中,并使用动画线程在一段时间后反复通知paint listener。这行得通,但闪烁得像疯了似的,但至少我可以看到正在绘制的圆圈。
  • 我在绘图监听器中使用双缓冲解决了它,并在动画线程中重复调用它。谢谢大家:)

标签: java multithreading swt


【解决方案1】:

为了解决这个问题,我最终使用了双缓冲,即在 GC 中绘制图像,然后稍后更新 paintEvent gc。这大大减少了闪烁,即使打开了抗锯齿。

基本上,创建一个 PaintListener::

final PaintListener paint = new PaintListener() {

            @Override
            public void paintControl(PaintEvent e) {
                int radius = 100;
                int maxX = radius ;
                int maxY = radius ;                             
                int centerX = e.width/2;
                int centerY = e.height/2;
                Image image = new Image(Display.getDefault(),e.width,e.height);
                GC gc = new GC(image);                              
                gc.setAntialias(SWT.ON);
                gc.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK));
                //gc.drawLine(0, centerY, shell.getBounds().width, centerY);
                //gc.drawLine(centerX, 0, centerX, shell.getBounds().height);
                // 1st circle
                gc.drawOval(centerX-radius/2, centerY-radius - radius/2, maxX, maxY);
                // 2nd circle
                gc.drawOval(centerX - 2*radius + radius/2, centerY - radius/2, maxX, maxY);
                // 3rd circle
                gc.drawOval(centerX+radius/2, centerY-radius/2, maxX, maxY);
                // 4th circle
                gc.drawOval(centerX-radius/2, centerY + radius/2, maxX, maxY);
                direction++;
                direction %= 4;
                switch(direction){                              
                case 0:                 
                    gc.setAlpha(255);
                    gc.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK));
                    gc.fillOval(centerX - 2*radius + radius/2, centerY - radius/2, maxX, maxY);
                    break;
                case 1:
                    gc.setAlpha(170);
                    gc.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK));
                    gc.fillOval(centerX-radius/2, centerY-radius - radius/2, maxX, maxY);
                    break;                              
                case 2:             
                    gc.setAlpha(80);
                    gc.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK));
                    gc.fillOval(centerX+radius/2, centerY-radius/2, maxX, maxY);
                    break;      
                case 3 :            
                    gc.setAlpha(20);
                    gc.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK));
                    gc.fillOval(centerX-radius/2, centerY + radius/2, maxX, maxY);
                    break;
                }
                e.gc.drawImage(image, 0, 0);
                image.dispose();
                gc.dispose();
            }
        };

然后动画线程可以写成 ::

showAnimation = new Thread(new Runnable() {

            @Override
            public void run() {
                while(true){
                    synchronized (this) {
                        try {                       
                            Thread.sleep(100);
                            Display.getDefault().asyncExec(new Runnable() {                             
                                @Override
                                public void run() {
                                    table.addPaintListener(paint);
                                    table.redraw();
                                    table.update();
                                    table.removePaintListener(paint);
                                }
                            });

                        } 
                        catch (InterruptedException e) 
                        {
                            // do nothing, because this thread is meant to be interrupted!
                            // and then, break out of the infinite loop
                            break;
                        }
                    }                   
                }                               
            }
        });

我添加和删除了侦听器,因为 notify 出现了一些问题(绘制的次数超出了要求!)。然后,可以像这样在另一个线程中进行数据库调用::

invokeLater = new Thread(new Runnable() {

            @Override
            public void run() {        
              //Fetching records from Service
                try 
                {   
                    // database call here
                    if(showAnimation.isAlive())
                    {
                        showAnimation.interrupt();
                    }

                } 
                        }
             });

这最终对我有用如果有人可以改进这一点,请告诉我:)

【讨论】:

  • 感谢 Baz 和匿名 :)
猜你喜欢
  • 2020-08-27
  • 1970-01-01
  • 2023-03-09
  • 1970-01-01
  • 2022-01-12
  • 2011-08-13
  • 1970-01-01
  • 2020-11-28
  • 2021-11-18
相关资源
最近更新 更多