Как "правильно" обнаружить DPI дисплея с Java?
У меня есть следующее приложение, которое рисует правило:
public class Rule extends JComponent
{
public static final long serialVersionUID=26362862L;
// public static final int INCH=Toolkit.getDefaultToolkit().getScreenResolution();
public static final int INCH=(int)(Toolkit.getDefaultToolkit().getScreenResolution()*1.15); // Auto adjust this 1.15 ?
public static final int HORIZONTAL=0;
public static final int VERTICAL=1;
public static final int SIZE=35;
public int orientation;
public boolean isMetric;
private int increment;
private int units;
// private Color rule_color=new Color(0,135,235);
private Color rule_color=new Color(120,170,230);
static JFrame frame=new JFrame("Rule");
static Dimension screenSize=Toolkit.getDefaultToolkit().getScreenSize(); // 1600 x 1200 , 1024 x 768
static JPanel rulerPanel=new JPanel(new BorderLayout());
public Rule(int o,boolean m)
{
orientation=o;
isMetric=m;
setIncrementAndUnits();
}
public void setIsMetric(boolean isMetric)
{
this.isMetric=isMetric;
setIncrementAndUnits();
repaint();
}
private void setIncrementAndUnits()
{
if (isMetric)
{
units=(int)((double)INCH/(double)2.54); // dots per centimeter
increment=units;
}
else
{
units=INCH;
increment=units/2;
}
}
public boolean isMetric() { return this.isMetric; }
public int getIncrement() { return increment; }
public void setPreferredHeight(int ph) { setPreferredSize(new Dimension(SIZE,ph)); }
public void setPreferredWidth(int pw) { setPreferredSize(new Dimension(pw,SIZE)); }
public void setColor(Color color) { rule_color=color; }
public void paintComponent(Graphics g)
{
Rectangle drawHere=g.getClipBounds();
// Fill clipping area with blue-gray.
g.setColor(rule_color);
g.fillRect(drawHere.x,drawHere.y,drawHere.width,drawHere.height);
// Do the ruler labels in a small font that black.
g.setFont(new Font("SansSerif",Font.PLAIN,10));
g.setColor(Color.black);
// Some vars we need.
int end=0;
int start=0;
int tickLength=0;
String text=null;
// Use clipping bounds to calculate first tick and last tick location.
if (orientation==HORIZONTAL)
{
start=(drawHere.x/increment)*increment;
end=(((drawHere.x+drawHere.width)/increment)+1)*increment;
}
else
{
start=(drawHere.y/increment)*increment;
end=(((drawHere.y+drawHere.height)/increment)+1)*increment;
}
// Make a special case of 0 to display the number within the rule and draw a units label.
if (start==0)
{
text=Integer.toString(0)+(isMetric?" cm":" in");
tickLength=10;
if (orientation==HORIZONTAL)
{
g.drawLine(0,SIZE-1,0,SIZE-tickLength-1);
g.drawString(text,2,21);
}
else
{
g.drawLine(SIZE-1,0,SIZE-tickLength-1,0);
g.drawString(text,9,10);
}
text=null;
start=increment;
}
// ticks and labels
for (int i=start;i<end;i+=increment)
{
if (i%units==0)
{
tickLength=10;
text=Integer.toString(i/units);
}
else
{
tickLength=5;
text=null;
}
if (tickLength!=0)
{
if (orientation==HORIZONTAL)
{
g.drawLine(i,SIZE-1,i,SIZE-tickLength-1);
if (text!=null) g.drawString(text,i-3,21);
}
else
{
g.drawLine(SIZE-1,i,SIZE-tickLength-1,i);
if (text!=null) g.drawString(text,9,i+3);
}
}
}
}
// Create the GUI and show it. For thread safety, this method should be invoked from the event-dispatching thread.
static void createAndShowGUI()
{
rulerPanel.setPreferredSize(new Dimension(570,78));
Rule cmView=new Rule(Rule.HORIZONTAL,true);
int H=35;
cmView.setPreferredHeight(H);
cmView.setColor(new Color(128,200,235));
JScrollPane cmScrollPane=new JScrollPane();
cmScrollPane.setViewportBorder(BorderFactory.createLineBorder(Color.black));
cmScrollPane.setColumnHeaderView(cmView);
rulerPanel.add("North",cmScrollPane);
Rule inchView=new Rule(Rule.HORIZONTAL,true);
inchView.setPreferredHeight(H);
inchView.setColor(new Color(168,200,235)); //238,238,238
inchView.setIsMetric(false);
JScrollPane inchScrollPane=new JScrollPane();
inchScrollPane.setViewportBorder(BorderFactory.createLineBorder(Color.black));
inchScrollPane.setColumnHeaderView(inchView);
rulerPanel.add("South",inchScrollPane);
frame.getContentPane().add(rulerPanel);
frame.addWindowListener(new WindowAdapter()
{
public void windowActivated(WindowEvent e) { }
public void windowClosed(WindowEvent e) { }
public void windowClosing(WindowEvent e) { System.exit(0); }
public void windowDeactivated(WindowEvent e) { }
public void windowDeiconified(WindowEvent e) { rulerPanel.repaint(); }
public void windowGainedFocus(WindowEvent e) { rulerPanel.repaint(); }
public void windowIconified(WindowEvent e) { }
public void windowLostFocus(WindowEvent e) { }
public void windowOpening(WindowEvent e) { rulerPanel.repaint(); }
public void windowOpened(WindowEvent e) { }
public void windowResized(WindowEvent e) { rulerPanel.repaint(); }
public void windowStateChanged(WindowEvent e) { rulerPanel.repaint(); }
});
frame.pack();
frame.setBounds((screenSize.width-rulerPanel.getWidth())/2,(screenSize.height-rulerPanel.getHeight())/2-19,rulerPanel.getWidth()+20,rulerPanel.getHeight()+38);
frame.setVisible(true);
}
public static void main(String[] args)
{
// Schedule a job for the event-dispatching thread : creating and showing this application GUI.
SwingUtilities.invokeLater(new Runnable() { public void run() { createAndShowGUI(); } });
}
}
Он отлично работал на моих старых 17 "и 20" дисплеях, теперь я заметил на своем новом 27-дюймовом ЖК-дисплее, это неточно, поэтому мне нужно изменить 4-ю строчку, чтобы быть более точным, это не Toolkit.getDefaultToolkit().getScreenResolution() должен получить точный DPI, почему это не так, для моего приложения для работы на других машинах с разными размерами дисплея и DPI, как автоматически настроить 1.15, который я вручную установил?
PS: Не только мой Java-приложение DPI неточно, но также когда я смотрел на несколько других приложений в Windows 7, таких как paint.exe или paint.net, их дюймы и см тоже верны. Вы можете попробовать их на своей машине.
Ответы
Ответ 1
Проблема с драйвером.
Реальный DPI известен только драйверу, который сообщает об этом ОС, который сообщает об этом Java и другим приложениям. Поскольку не только Java имеет неправильный масштаб, он должен быть драйвером.
Это проблема старых времен. Многие графические приложения имеют глобальную настройку "экран DPI", где пользователи могут настраивать свой фактический монитор.
Кроме того, поскольку вы пишете на Java, имейте в виду, что некоторые ОС не имеют возможности сообщать Java реальный DPI (потому что они сами не знают). Таким образом, необходимость настройки конфигурации DPI становится еще более понятной.