Проблемы с пониманием Graphics.getClipBounds () и области просмотра - PullRequest
1 голос
/ 17 апреля 2020

Я пытался лучше понять области просмотра, поэтому я создал вертикальную временную шкалу, которую можно поместить в представление заголовка строки JScrollPane. Это работает, но когда я продолжил исследование, оно появляется при прокрутке вниз по aws областям компонента, которые не видны. Я только ожидал, что он нарисует видимую область на основе Graphics.getClipBounds (), но по мере того, как я продолжаю прокручивать его вниз, все больше и больше компонента * * * увеличивается до тех пор, пока внизу распечатки не покажут, что я рисую всю высоту. компонент.

Кажется, что-то не так с моим вычислением topMillis, но endMillis (который основан на topMillis) выглядит правильно.

Чтобы увидеть проблему, запустите программу и обратите внимание на Разница между topMillis и endMillis увеличивается при прокрутке вниз. Я ожидал, что разница останется прежней, так как это должна быть область, видимая пользователю.

На заметку о том, есть ли еще более эффективный способ нарисовать этот компонент? Простой способ - рисовать весь компонент каждый раз. Это становится непомерно большим, если временной диапазон становится слишком большим. То, как я это делаю, должно быть более эффективным, поскольку мы рисуем только то, что видно пользователю, независимо от того, насколько велик временной диапазон. Но мой подход напрямую связан с размером панели данных, что кажется проблематичным c. Можно ли сделать так, чтобы мой компонент соответствовал размеру области просмотра заголовка строки, но при этом точно отображал то, что пользователь прокручивает на панели прокрутки?

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.TimeUnit;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.ScrollPaneConstants;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;

public class TimeBarTest {

   private static final int DATA_HEIGHT = 1000;

   private final JScrollPane mScrollPane;

   private TimeBar mRowHeaderView;

   TimeBarTest() {

      JPanel dataPanel = new JPanel();
      dataPanel.setPreferredSize(new Dimension(0, DATA_HEIGHT));

      mScrollPane = new JScrollPane(dataPanel);
      mScrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
      mScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);

      JButton cornerButton = new JButton("Hi");
      cornerButton.addActionListener(pEvent -> {
         mRowHeaderView.setTime(System.currentTimeMillis());
         mScrollPane.getVerticalScrollBar().setValue(0);
      });
      cornerButton.setPreferredSize(new Dimension(20, 20));

      JPanel columnHeader = new JPanel(new BorderLayout());
      columnHeader.setPreferredSize(new Dimension(0, 30));
      columnHeader.setBorder(BorderFactory.createLineBorder(Color.GRAY));
      columnHeader.add(new JLabel("Column Header", SwingConstants.CENTER), BorderLayout.CENTER);

      mRowHeaderView = new TimeBar(DATA_HEIGHT);

      mScrollPane.setCorner(ScrollPaneConstants.UPPER_LEFT_CORNER, cornerButton);
      mScrollPane.setRowHeaderView(mRowHeaderView);
      mScrollPane.setColumnHeaderView(columnHeader);

      JPanel contentPane = new JPanel(new BorderLayout());
      contentPane.setPreferredSize(new Dimension(500, 475));
      contentPane.add(mScrollPane, BorderLayout.CENTER);

      JFrame frame = new JFrame();
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.setContentPane(contentPane);

      frame.pack();
      frame.setLocationRelativeTo(null);
      frame.setVisible(true);
   }

   public static void main(String... args) {
      SwingUtilities.invokeLater(() -> new TimeBarTest());
   }

   private class TimeBar extends JComponent {

      private final int MAJOR_TICK_LENGTH = 8;
      private final int MINOR_TICK_LENGTH = 4;

      private final int MINUTES_PER_MAJOR = 5;
      private final int MINUTES_PER_MINOR = 1;
      private final long MILLIS_PER_PIXEL = 4000;

      private final long MILLIS_PER_MAJOR = TimeUnit.MINUTES.toMillis(MINUTES_PER_MAJOR);
      private final long MILLIS_PER_MINOR = TimeUnit.MINUTES.toMillis(MINUTES_PER_MINOR);

      private final SimpleDateFormat mSimpleDateFormat = new SimpleDateFormat("hh:mm");

      private long mTime;

      TimeBar(int pHeight) {
         setPreferredSize(new Dimension(50, pHeight));

         mTime = System.currentTimeMillis();
      }

      public void setTime(long pTime) {
         mTime = pTime;
         repaint();
      }

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

         pGraphics.setColor(Color.black);

         Rectangle clipBounds = pGraphics.getClipBounds();
         Rectangle visibleRect = getVisibleRect();

         // Determine the start and end time based on the visible area.
         long topMillis = mTime - (clipBounds.y * MILLIS_PER_PIXEL);
         long endMillis = topMillis - ((clipBounds.y + clipBounds.height) * MILLIS_PER_PIXEL);

         // Determine where we should start drawing the ticks.
         long startMillis = topMillis - (topMillis % MILLIS_PER_MINOR);

         System.out.println("    clipBounds=" + clipBounds);
         System.out.println("   visibleRect=" + visibleRect);
         SimpleDateFormat dateFormat = new SimpleDateFormat("hh:mm:ss");
         System.out.println("      origTime=" + dateFormat.format(new Date(mTime)));
         System.out.println("       topTime=" + dateFormat.format(new Date(topMillis)));
         System.out.println("   startMillis=" + dateFormat.format(new Date(startMillis)));
         System.out.println("       endTime=" + dateFormat.format(new Date(endMillis)));
         System.out.println("     topMillis=" + topMillis);
         System.out.println("   startMillis=" + startMillis);
         System.out.println("     endMillis=" + endMillis);
         System.out.println("millisPerMajor=" + MILLIS_PER_MAJOR);
         System.out.println("millisPerMinor=" + MILLIS_PER_MINOR);

         // Draw the ticks and labels backwards through time.
         for (long i = startMillis; i >= endMillis; i -= MILLIS_PER_MINOR) {
            int pixel = (int) ((topMillis - i) / MILLIS_PER_PIXEL);
            System.out.println("pixel=" + pixel);

            if (i % MILLIS_PER_MAJOR == 0) {
               String text = mSimpleDateFormat.format(new Date(i));
               pGraphics.drawString(text, 1, (int) (pixel + 4));
               pGraphics.drawLine(clipBounds.width, (int) pixel, clipBounds.width - MAJOR_TICK_LENGTH, (int) pixel);
            } else {
               pGraphics.drawLine(clipBounds.width, (int) pixel, clipBounds.width - MINOR_TICK_LENGTH, (int) pixel);
            }
         }
      }
   }
}

1 Ответ

0 голосов
/ 17 апреля 2020

Я думаю, что первая проблема, это просто алгебра c.

Время запуска, которое вы отобразите, равно mTime - clipBounds.y*MILLIS_PER_PIXEL, а последнее время - mTime - (clipBounds.y + clipBounds.height)*MILLIS_PER_PIXEL. Если вы хотите написать это как функцию времени начала.

endMillis = topMillis - clipBounds.height*MILLIS_PER_PIXEL;

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

Таким образом, ваша координата будет основана на mTime, а не на времени начала.

int pixel = (int) ((mTime-i) / MILLIS_PER_PIXEL);

Если бы вы не обрезали, вы бы go от mTime до конца времени, и это не изменится.

...