Эффективное отображение таблицы занятости в Java Swing - PullRequest
1 голос
/ 12 февраля 2012

Прежде всего, примите мои извинения, если этот вопрос является основным, я в основном знаю C #, но вынужден использовать Java для этого конкретного проекта!

Я пытаюсь реализовать графический интерфейс для отображения сетки занятости на основе данных датчика робота. Сетка занятости будет довольно большой, возможно, до 1500x1500 квадратов сетки, представляющих реальную площадь около 10 см2 на ячейку сетки.

Каждый квадрат сетки будет просто хранить статус Enumerable, например:

  • Неизвестно
  • Незанятые
  • Занято
  • Robot

Я просто хотел бы найти лучший способ визуализировать это как сетку, используя разные цветные квадраты, чтобы изобразить различный статус ячейки сетки '.

Я реализовал наивный, базовый алгоритм рисования квадратов и линий сетки, однако он ОЧЕНЬ плохо работает на больших сетках занятости. Другой код в классе перерисовывает окно каждые 0,5 с, когда собираются новые данные датчика. Я подозреваю, что причиной очень низкой производительности является тот факт, что я КАЖДУЮ раздачу КАЖДУЮ ячейку. Есть ли простой способ, которым я могу выборочно визуализировать эти ячейки, должен ли я обернуть каждую ячейку в наблюдаемый класс?

Моя текущая реализация:

@Override
public void paint(Graphics g) {
    Graphics2D g2 = (Graphics2D) g;

    int width = getSize().width;
    int height = getSize().height;

    int rowHeight = height / (rows);
    int colWidth = width / (columns);

    //Draw Squares
    for (int row = 0; row < rows; row++) {
        for (int col = 0; col < columns; col++) {
            switch (this.grid[row][col]) {
                case Unexplored:
                    g.setColor(Color.LIGHT_GRAY);
                    break;
                case Empty:
                    g.setColor(Color.WHITE);
                    break;
                case Occupied:
                    g.setColor(Color.BLACK);
                    break;
                case Robot:
                    g.setColor(Color.RED);
                    break;
            }

            g.drawRect(col * colWidth, height - ((row + 1) * rowHeight), colWidth,     rowHeight);
            g.fillRect(col * colWidth, height - ((row + 1) * rowHeight), colWidth,     rowHeight);
        }
    }

    int k;
    if (outline) {
        g.setColor(Color.black);
        for (k = 0; k < rows; k++) {
            g.drawLine(0, k * rowHeight, width, k * rowHeight);
        }

        for (k = 0; k < columns; k++) {
            g.drawLine(k * colWidth, 0, k * colWidth, height);
        }
    }

}


 private void setRefresh() {
    Action updateUI = new AbstractAction() {
        boolean shouldDraw = false;

        public void actionPerformed(ActionEvent e) {
            repaint();
        }
    };

    new Timer(updateRate, updateUI).start();
}

Пожалуйста, помогите! Заранее спасибо.

Ответы [ 4 ]

2 голосов
/ 13 февраля 2012

Необходимо соблюдать прямоугольник клипа при рисовании (при условии, что ваша сетка находится в JScrollPane) и использовать JComponent # repaint (Rectangle) соответствующим образом.

См. Этот пример программы (хотя это связано с загрузкой значенияклетка лениво, на ней также есть окантовка клипа):

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;

import javax.swing.*;


public class TilePainter extends JPanel implements Scrollable {

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                JFrame frame = new JFrame("Tiles");
                frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
                frame.getContentPane().add(new JScrollPane(new TilePainter()));
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    private final int TILE_SIZE = 50;
    private final int TILE_COUNT = 1000;
    private final int visibleTiles = 10;
    private final boolean[][] loaded;
    private final boolean[][] loading;
    private final Random random;

    public TilePainter() {
        setPreferredSize(new Dimension(
                TILE_SIZE * TILE_COUNT, TILE_SIZE * TILE_COUNT));
        loaded = new boolean[TILE_COUNT][TILE_COUNT];
        loading = new boolean[TILE_COUNT][TILE_COUNT];
        random = new Random();
    }

    public boolean getTile(final int x, final int y) {
        boolean canPaint = loaded[x][y];
        if(!canPaint && !loading[x][y]) {
            loading[x][y] = true;
            Timer timer = new Timer(random.nextInt(500),
                    new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    loaded[x][y] = true;
                    repaint(x * TILE_SIZE, y * TILE_SIZE, TILE_SIZE, TILE_SIZE);
                }
            });
            timer.setRepeats(false);
            timer.start();
        }
        return canPaint;
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);

        Rectangle clip = g.getClipBounds();
        int startX = clip.x - (clip.x % TILE_SIZE);
        int startY = clip.y - (clip.y % TILE_SIZE);
        for(int x = startX; x < clip.x + clip.width; x += TILE_SIZE) {
            for(int y = startY; y < clip.y + clip.height; y += TILE_SIZE) {
                if(getTile(x / TILE_SIZE, y / TILE_SIZE)) {
                    g.setColor(Color.GREEN);
                }
                else {
                    g.setColor(Color.RED);
                }
                g.fillRect(x, y, TILE_SIZE, TILE_SIZE);
            }
        }
    }

    @Override
    public Dimension getPreferredScrollableViewportSize() {
        return new Dimension(visibleTiles * TILE_SIZE, visibleTiles * TILE_SIZE);
    }

    @Override
    public int getScrollableBlockIncrement(
            Rectangle visibleRect, int orientation, int direction) {
        return TILE_SIZE * Math.max(1, visibleTiles - 1);
    }

    @Override
    public boolean getScrollableTracksViewportHeight() {
        return false;
    }

    @Override
    public boolean getScrollableTracksViewportWidth() {
        return false;
    }

    @Override
    public int getScrollableUnitIncrement(
            Rectangle visibleRect, int orientation, int direction) {
        return TILE_SIZE;
    }
}
2 голосов
/ 12 февраля 2012

ROS - операционная система робота от willowgarage имеет реализацию сетки занятости в Java: http://code.google.com/p/rosjava/source/browse/android_honeycomb_mr2/src/org/ros/android/views/map/OccupancyGrid.java?spec=svn.android.88c9f4af5d62b5115bfee9e4719472c4f6898665&repo=android&name=88c9f4af5d&r=88c9f4af5d62b5115bfee9e4719472c4f6898665

Вы можете использовать его или получить от него идеи.

1 голос
/ 13 февраля 2012

Создание прямоугольников, вероятно, слишком медленно.Вместо этого, почему бы вам не создать растровое изображение, каждый пиксель которого является ячейкой сетки, вы можете затем масштабировать его до любого желаемого размера.

Следующий класс берет матрицу целых чисел и сохраняет ее в растровом файле.

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class BMP {
    private final static int BMP_CODE = 19778;

    byte [] bytes;

    public void saveBMP(String filename, int [][] rgbValues){
        try {
            FileOutputStream fos = new FileOutputStream(new File(filename));

            bytes = new byte[54 + 3*rgbValues.length*rgbValues[0].length + getPadding(rgbValues[0].length)*rgbValues.length];

            saveFileHeader();
            saveInfoHeader(rgbValues.length, rgbValues[0].length);
            saveRgbQuad();
            saveBitmapData(rgbValues);

            fos.write(bytes);

            fos.close();

        } catch (FileNotFoundException e) {

        } catch (IOException e) {

        }

    }

    private void saveFileHeader() {
        byte[]a=intToByteCouple(BMP_CODE);
        bytes[0]=a[1];
        bytes[1]=a[0];

        a=intToFourBytes(bytes.length);
        bytes[5]=a[0];
        bytes[4]=a[1];
        bytes[3]=a[2];
        bytes[2]=a[3];

        //data offset
        bytes[10]=54;
    }

    private void saveInfoHeader(int height, int width) {
        bytes[14]=40;

        byte[]a=intToFourBytes(width);
        bytes[22]=a[3];
        bytes[23]=a[2];
        bytes[24]=a[1];
        bytes[25]=a[0];

        a=intToFourBytes(height);
        bytes[18]=a[3];
        bytes[19]=a[2];
        bytes[20]=a[1];
        bytes[21]=a[0];

        bytes[26]=1;

        bytes[28]=24;
    }

    private void saveRgbQuad() {

    }

    private void saveBitmapData(int[][]rgbValues) {
        int i;

        for(i=0;i<rgbValues.length;i++){
            writeLine(i, rgbValues);
        }

    }

    private void writeLine(int row, int [][] rgbValues) {
        final int offset=54;
        final int rowLength=rgbValues[row].length;
        final int padding = getPadding(rgbValues[0].length);
        int i;

        for(i=0;i<rowLength;i++){
            int rgb=rgbValues[row][i];
            int temp=offset + 3*(i+rowLength*row) + row*padding;

            bytes[temp]    = (byte) (rgb>>16);
            bytes[temp +1] = (byte) (rgb>>8);
            bytes[temp +2] = (byte) rgb;
        }
        i--;
        int temp=offset + 3*(i+rowLength*row) + row*padding+3;

        for(int j=0;j<padding;j++)
            bytes[temp +j]=0;

    }

    private byte[] intToByteCouple(int x){
        byte [] array = new byte[2];

        array[1]=(byte)  x;
        array[0]=(byte) (x>>8);

        return array;
    }

    private byte[] intToFourBytes(int x){
        byte [] array = new byte[4];

        array[3]=(byte)  x;
        array[2]=(byte) (x>>8);
        array[1]=(byte) (x>>16);
        array[0]=(byte) (x>>24);

        return array;
    }

    private int getPadding(int rowLength){

        int padding = (3*rowLength)%4;
        if(padding!=0)
            padding=4-padding;


        return padding;
    }

}

С этим классом вы можете просто сделать:

new BMP().saveBMP(fieName, myOccupancyMatrix);

Генерация матрицы целых чисел (myOccupancyMatrix) очень проста.Простой трюк, позволяющий избежать использования оператора Switch, - присвоить значения цвета для перечисления Occupancy:

public enum Occupancy {
        Unexplored(0x333333), Empty(0xFFFFFF), Occupied(0x000000), Robot(0xFF0000);
}

Как только вы сохраните это на диске, BMP можно отобразить в апплете и легко масштабировать:

public class Form1 extends JApplet {
    public void paint(Graphics g) {
        Image i = ImageIO.read(new URL(getCodeBase(), "fileName.bmp"));
        g.drawImage(i,0,0,WIDTH,HEIGHT,Color.White,null);
    }
}

Надеюсь, это поможет!

1 голос
/ 12 февраля 2012

Рендеринг даже поднабора из 2 250 000 ячеек не является тривиальным делом. Вам понадобятся два шаблона: Model-View-Controller , обсуждается здесь и мухи , для которых JTable может быть полезно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...