Graphics und JScrollbar

Sorry Leute, aber es sind 2 fragen die ganz schnell gehen, ist n bissl unnötig 2 threads aufzumachen.
also nummer 1:

wenn man in einer paintComponenet Graphics#drawString aufruft und x=0, y=0 übergibt landet der text ja nicht oben links in der ecke, sondern dadrüber. ich muss immer rumraten mit y, bis es passt, vor allem bei variablen schriftgrößen… wo genau wird der string gezeichnet? lässt sich da was mit den StringBounds berechnen?

nummer 2:

Ich wollte das eine jscrollpane mit jtextarea vertical mitscrollt, wenn bei der textarea neuer inhalt unten dran kommt.
dazu hab ich ganz einfach mit einem einzeiler (scrollbar = scrollpane.getVerticalScrollBar()) scrollbar.setValue(scrollbar.getMaximum()) geschrieben. Machnmal landet der balken dann aber nicht ganz unten, sondern ein paar (geschätzte 5 - 10) pixel da drüber. Wie kann das sein, bzw was übersehe ich?

Bild von 2, nachaufruf des runter scrolls - einzeilers:

wenn man in einer paintComponenet Graphics#drawString aufruft und x=0, y=0 übergibt landet der text ja nicht oben links in der ecke, sondern dadrüber.

Du hast doch bestimmt die paintComponent von deinem JFrame überschrieben oder? Das sollte man nicht machen. Schreib dir eine Klasse die von JPanel ableitet und überschreibe dort die paintComponent. Diese Komponente setzt du dann als contentPane.

Zu 2.
Ich nehme an du hast da eine TextArea die scrollt? Dann mach folgendes:
textArea.setCaretPosition(textArea.getDocument().getLength());

Zu 1 erstmal mein Mantra: Fonts sind immer Kacke. Ansonsten … das passt schon. Der “Ursprung” des Textes ist die “Bassislinie”, also etwa der untere Rand von Buchstaben wie a,b,c,d,e, … f? vielleicht… aber sicher nicht g. Die Verschiebung kann man notfalls über die String bounds ausrechnen, ggf. mit sowas wie http://docs.oracle.com/javase/7/docs/api/java/awt/FontMetrics.html#getStringBounds(java.lang.String,%20java.awt.Graphics)

@EikeB : - Ne, soweit bin ich schon nach 3 jahren, trozdem danke ^^
- verändert sich der wert der scrollbar dann auch? dann versuch ichs mal, danke
@Marco13 : -was meinst du mit kacke? gibts alternativen?
-stimmt jetzt fällts mir auch auf mit den buchstaben grössen… aber egal, dann rate ich halt weiter.

Nein. Ich meinte nur, dass man, immer wenn man mit Fonts hantiert, auf Schwierigkeiten stößt. Von “A” wie “Ausrichtung” über “L” wie “Logische Fonts” und “P” wie “Physische Fonts” (und natürlich “U” wie “Unicode”) bis “Z” wie “Zeichensatz”.

Was spricht dagegen, die Bounds auszurechnen?

Ich hab das gefühl die scrollpane kommt nicht mit den bordern klar.
aber ich weiß auch nicht wie ich das umgehen soll?..
Auch wenn ich eine übertriebene zahl einsetze (zB getMaximum() + 2000) bleibt die scorllbar ab und zu
oberhalb des eigetnlichen maximums hängen…
mit caret position funktioniert es genau deshalb nicht, weil die area nen blank border (oder empty border…) hat.
Hier jedenfalls ein Kskb:

package tests;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;

import javax.imageio.ImageIO;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollBar;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.border.Border;

public class KSKB {
	
	private Font calibri;
	private Border tenPixelSpaceBorder;
	
	private JFrame f;
	private JPanel mainContent;
	private JPanel chatContent;
	private JTextField messageEntryField;
	private JTextArea chatViewArea;
	private JButton sendButton;
	private JScrollBar chatScrollbar;
	
	public KSKB(){
		buildGui();
	}
	
	private void buildGui(){
		calibri = new Font("Calibri", Font.PLAIN, 16);
		tenPixelSpaceBorder = BorderFactory.createEmptyBorder(10, 10, 10, 10);
		
		buildMainGui();
		buildChatGui();
		
		mainContent.add(chatContent);
		f.add(mainContent);
		f.setVisible(true);
	}
	
	private void buildMainGui(){
		f = new JFrame();
		f.setAlwaysOnTop(true);
		f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		f.setSize(600, 400);
		f.setResizable(true);
		f.setLocationRelativeTo(null);
		f.setLayout(new BorderLayout());
		mainContent = new JPanel();
		mainContent.setLayout(new BorderLayout());
	}
	
	
	private void buildChatGui(){
		Border chatViewEtchedBorder = BorderFactory.createEtchedBorder(Color.LIGHT_GRAY, Color.DARK_GRAY);
		Border chatAreaBorder = BorderFactory.createCompoundBorder(chatViewEtchedBorder, tenPixelSpaceBorder);
		chatContent = new JPanel();
		chatContent.setLayout(new BorderLayout());
		JPanel chatContentWest = new JPanel();
		chatContentWest.setLayout(new BorderLayout());
		JTabbedPane chatWestTabbedPane = new JTabbedPane(JTabbedPane.TOP, JTabbedPane.WRAP_TAB_LAYOUT);
		chatViewArea = new JTextArea();
		chatViewArea.setAutoscrolls(true);
		chatViewArea.setBorder(chatAreaBorder);
		chatViewArea.setEditable(false);
		chatViewArea.setLineWrap(true);
		chatViewArea.setWrapStyleWord(true);
		chatViewArea.setFont(calibri);
		JScrollPane chatViewContent = new JScrollPane(chatViewArea, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
		chatScrollbar = chatViewContent.getVerticalScrollBar();
		chatViewContent.setBorder(chatViewEtchedBorder);
		chatWestTabbedPane.addTab("Chat", chatViewContent);
		addCloseButtonsToTabbedPane(chatWestTabbedPane);
		chatContentWest.add(chatWestTabbedPane, BorderLayout.CENTER);
		JPanel sendContent = new JPanel();
		sendContent.setPreferredSize(new Dimension(0, 50));
		messageEntryField = new JTextField(10);
		messageEntryField.addActionListener(sendMessageListener);
		sendContent.add(messageEntryField);
		sendButton = new JButton("send");
		sendButton.addActionListener(sendMessageListener);
		sendContent.add(sendButton);
		chatContentWest.add(sendContent, BorderLayout.SOUTH);
		chatContent.add(chatContentWest, BorderLayout.CENTER);
		JPanel userPanel = new JPanel();
		userPanel.setLayout(new FlowLayout(FlowLayout.CENTER, 0, 0));
		userPanel.setBackground(Color.GRAY);
		userPanel.setPreferredSize(new Dimension(50, 0));
		String userPanelCommands[] = new String[]{"pm", "profile", "settings", "debug"};
		for(int i = 0; i < userPanelCommands.length; i++){
			JButton b = new JButton(new ImageIcon(new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB)));
			b.setPreferredSize(new Dimension(50, 50));
			userPanel.add(b);
		}
		chatContent.add(userPanel, BorderLayout.EAST);
	}
	
	private void addCloseButtonsToTabbedPane(JTabbedPane tabbedPane){
		for(int i = 0; i < tabbedPane.getTabCount(); i++){
			final String title = tabbedPane.getTitleAt(i);
			JPanel closePanel = new JPanel(){
				public void paintComponent(Graphics g){
					Graphics2D g2d = (Graphics2D) g;
					g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
					g2d.setFont(calibri.deriveFont(Font.BOLD, 18f));
					g2d.setColor(new Color(40, 40, 40));
					g2d.drawString(title, 0, 18);
				}
			};
			closePanel.setPreferredSize(new Dimension(100, 20));
			tabbedPane.setTabComponentAt(i, closePanel);
		}
	}
	
	public void writeLine(String text){
		chatViewArea.setText(chatViewArea.getText() + "
" + text);
		chatScrollbar.setValue(chatScrollbar.getMaximum());
//		chatScrollbar.setValue(chatScrollbar.getMaximum() + 2000);
//		chatViewArea.setCaretPosition(chatViewArea.getDocument().getLength());
	}
	
	public ActionListener sendMessageListener = new ActionListener(){
		public void actionPerformed(ActionEvent arg0) {
			writeLine(messageEntryField.getText());
			messageEntryField.setText("");
		}
	};
	
	public BufferedImage loadImage(String name){
		try{
			return ImageIO.read(getClass().getClassLoader().getResource("img/" + name));
		}
		catch(Exception e){
			e.printStackTrace();
			return null;
		}
	}
	
	public static void main(String[] args) {
		new KSKB();
	}
}

Meine Theorie:
Der setText-Aufruf führt dazu, dass das Maximum von chatScrollbar neu berechnet wird und dass automatisch gescrollt wird. Das läuft über ein Event und passiert deshalb nach dem setValue-Aufruf.

Glücklich bin ich damit nicht, aber auf die Schnelle eine Möglichkeit:
chatScrollbar.setValue(chatScrollbar.getMaximum()); ans Ende der EventQueue packen.

        chatViewArea.setText(chatViewArea.getText() + "
" + text);

        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                chatScrollbar.setValue(chatScrollbar.getMaximum());
            }
        });
    }```

Das erste K ist wohl Ironie :wink:

Versuch mal, ob es hiermit funktioniert:
chatViewArea.scrollRectToVisible(new Rectangle(0, chatViewArea.getHeight(), chatViewArea.getWidth(), 100));
Alternativ vielleicht die Border unten schmäler halten/weg lassen, dann fällt es nicht auf :wink:

" + text);```

Die TextArea bietet auch die Methode append(String) um Inhalte anzuhängen.

" + text);```

Okay ich versuche das mit der eventqueue gleich, klappt das dann bei dir papoy?

haha ja michael, ist wohl eher ein LSKB :smiley:
sry hatte keine zeit mehr das zu kürzen gestern ^^
hm, ich hätte die border schon gerne, weil ich da ein bisschen platz haben will. (sonst ist es so eng am rand)
ich versuch das gleich mit dem scrollrecttovisible, wo ist denn da der unterschied zu setValue() ?
danke für append, aber macht das nicht genau das selbe? :wink:

*** Edit ***

Hab mal nachgeschaut, im java src ist append so programmiert:

    public void append(String str) {
        Document doc = getDocument();
        if (doc != null) {
            try {
                doc.insertString(doc.getLength(), str, null);
            } catch (BadLocationException e) {
            }
        }
    }

wozu mit document, und nicht direkt über die area? o.O

*** Edit ***

Mit scrollrecttovisible ist es schon besser, aber es bleibt trozdem ungefähr alle 10 nachrichten ein paar pixel über maximum hängen…

Ja, die Variante von _Michael aber auch.

Weil der Weg über die Area (den Du in etwa mit Deinem Vorgehen beschreitest) ein unnötiger Umweg wäre. Die Area ist ja nur die „Hülle“ die den Text darstellt, dieser ist aber Inhalt des Document

achso ok.
siehe oben, es klappt leider immer noch nicht so toll…
bleibt noch die event queue variante.

*** Edit ***

es hatte tatsächlich mit dem zeitpunkt des aufrufs zu tun.
mit invoke later klappt es perfekt.
[OT]
Mal so ein kleiner Einschub: wenn ich jetzt blutiger total anfänger wäre, solche foren wie hier nicht kennen würde, usw…
auf sowas kommt man doch nicht… ich mein wenn da steht “scrolle zum maximalwert” und das ding einfach nicht bis zum maximal wert scrollt, das ist schon demotivierend…
[/OT]