我想更改scrollPane的内部大小。
我尝试过各种方法,但都做不到。我希望每次创建任务时,ScrollPane 的长度都会发生变化。 (它就像感知 JPanel 是否已满然后滚动条的长度增加)。
我有一个任务类(它只是生成任务的类)
这是我的列表类(将任务放在列表中):
package com.chri_stw.todolist;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.GridLayout;
import javax.swing.JPanel;
public class List extends JPanel {
private int cornerRadius; // Allows customization of the corner radius
List() {
this.cornerRadius = 30; // Default corner radius
GridLayout layout = new GridLayout(10, 1);
layout.setVgap(5); // Vertical gap between components
this.setLayout(layout);
this.setPreferredSize(new Dimension(400, 400)); // Preferred size
this.setBackground(Color.cyan);
add(new Task());
setOpaque(false); // Set the panel to be non-opaque to show rounded corners
}
// Override paintComponent to add rounded edges
@Override
protected void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g.create();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); // Smooth edges
g2.setColor(getBackground());
g2.fillRoundRect(0, 0, getWidth(), getHeight(), cornerRadius, cornerRadius); // Draw rounded background
super.paintComponent(g2);
g2.dispose();
}
public void updateNumbers() {
Component[] listItems = this.getComponents();
for (int i = 0; i < listItems.length; i++) {
if (listItems[i] instanceof Task) {
((Task) listItems[i]).changeIndex(i + 1); // Update task index
}
}
revalidate(); // Ensure layout recalculation
repaint(); // Ensure the panel is redrawn
}
public void removeCompletedTasks() {
for (Component c : getComponents()) {
if (c instanceof Task) {
if (((Task) c).getState()) {
remove(c); // Remove completed tasks
}
}
}
updateNumbers(); // Update task indices
revalidate(); // Recalculate layout after removing tasks
repaint(); // Redraw the panel
}
// Method to add tasks to the list
public void addTask(Task task) {
this.add(task); // Add task to the panel
revalidate(); // Ensure that layout updates after adding the task
repaint(); // Redraw the panel to reflect the new task
}
// Force setting the size explicitly, in case revalidation does not update it
@Override
public Dimension getPreferredSize() {
return new Dimension(350, 560); // Ensure preferred size
}
}
这是 AppFrame(完成所有 UI):
package com.chri_stw.todolist;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollBar;
import javax.swing.JScrollPane;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;
import javax.swing.plaf.basic.BasicScrollBarUI;
public class AppFrame extends JFrame {
private TitleBar title;
private Footer footer;
private List list;
private JButton newTask;
private JButton clear;
AppFrame() {
// Set default frame properties
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setResizable(false);
// Initialize components
title = new TitleBar();
footer = new Footer();
list = new List();
JScrollPane scrollPane = new JScrollPane(list, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
JScrollBar scroll = scrollPane.getVerticalScrollBar();
scrollPane.setBorder(null);
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
scroll.setUI(new BasicScrollBarUI() {
@Override
protected void configureScrollBarColors() {
this.thumbColor = Color.gray;
}
});
scroll.setUnitIncrement(1); // Smooth scrolling
scroll.setForeground(Color.gray);
add(scrollPane);
// Add components to the frame
this.setLayout(new BorderLayout());
this.add(title, BorderLayout.NORTH);
this.add(footer, BorderLayout.SOUTH);
this.add(scrollPane, BorderLayout.CENTER);
// Initialize footer buttons
newTask = footer.getNewTask();
clear = footer.getClear();
// Add listeners to buttons
addListeners();
// Adjust frame size and make it visible
this.setPreferredSize(new Dimension(400, 700));
this.pack(); // Adjust size based on preferred sizes
this.setLocationRelativeTo(null); // Center the frame on the screen
this.setVisible(true);
}
public void addListeners() {
// Listener for "New Task" button
newTask.addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
Task task = new Task();
list.add(task);
list.updateNumbers();
task.getDone().addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
task.toggleState(); // Toggle the task's state
list.updateNumbers();
revalidate();
}
});
}
});
// Listener for "Clear" button
clear.addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
list.removeCompletedTasks(); // Remove completed tasks
repaint();
}
});
}
}
出现此问题是因为
JScrollPane
依赖于其视口的首选大小(在您的情况下为 List
面板)来计算可滚动区域。如果首选大小未动态更新,可滚动区域将保持固定,导致滚动条行为不正确。
根据
List
面板包含的任务数量修改其首选大小。将以下方法添加到您的 List
类中:
public void updatePreferredSize() {
int taskHeight = 50;
int gap = 5;
int totalHeight = (taskHeight + gap) * this.getComponentCount() - gap;
this.setPreferredSize(new Dimension(400, Math.max(totalHeight, 400)));
}
每当添加、删除或更新任务时调用
updatePreferredSize()
方法:
public void addTask(Task task) {
this.add(task);
updatePreferredSize();
revalidate();
repaint();
}
public void removeCompletedTasks() {
for (Component c : getComponents()) {
if (c instanceof Task) {
if (((Task) c).getState()) {
remove(c);
}
}
}
updatePreferredSize();
revalidate();
repaint();
}
public void updateNumbers() {
Component[] listItems = this.getComponents();
for (int i = 0; i < listItems.length; i++) {
if (listItems[i] instanceof Task) {
((Task) listItems[i]).changeIndex(i + 1);
}
}
updatePreferredSize();
revalidate();
repaint();
}
避免在
List
中为 AppFrame
面板设置固定的首选尺寸。让动态更新来处理它。