Java JTextPane防止在父JScrollPane中滚动
我有以下对象的“树”:Java JTextPane防止在父JScrollPane中滚动,java,swing,user-interface,jscrollpane,jtextpane,Java,Swing,User Interface,Jscrollpane,Jtextpane,我有以下对象的“树”: JPanel JScrollPane JPanel JPanel JScrollPane JTextPane 当使用鼠标滚轮在外部JScrollPane上滚动时,我遇到了一个恼人的问题。一旦鼠标光标触及内部JScrollPane,滚动事件似乎就被传递到该JScrollPane中,并且不再由第一个JScrollPane处理。这意味着滚动“父”JScrol
JPanel
JScrollPane
JPanel
JPanel
JScrollPane
JTextPane
当使用鼠标滚轮在外部JScrollPane上滚动时,我遇到了一个恼人的问题。一旦鼠标光标触及内部JScrollPane,滚动事件似乎就被传递到该JScrollPane中,并且不再由第一个JScrollPane处理。这意味着滚动“父”JScrollPane将停止
是否可以仅禁用内部JScrollPane上的鼠标滚轮?或者更好的方法是,如果没有任何内容可以滚动,则禁用滚动(大多数情况下,textpane只包含1-3行文本),但如果有更多内容,则启用滚动?遗憾的是,显而易见的解决方案(JScrollPane.setWheelScrollingEnabled(false))实际上并没有取消MouseweelEvents的注册,因此无法达到您想要的效果 下面是一种完全禁用滚动的粗糙黑客方法,它可以让MouseWheelEvents到达外部JScrollPane:
for (MouseWheelListener mwl : scrollPane.getMouseWheelListeners()) {
scrollPane.removeMouseWheelListener(mwl);
}
如果对内部JScrollPane执行此操作,它将永远不会响应滚轮事件;外部JScrollPane将获取所有这些内容
如果您想“干净地”实现它,您需要实现自己的ScrollPaneUI,并使用setUI()将其设置为JScrollPane的UI。不幸的是,您不能仅仅扩展BasicScrollPaneUI并禁用其鼠标滚轮侦听器,因为相关的成员变量是私有的,并且在ScrollPaneUI安装的鼠标滚轮侦听器上没有任何标志或保护
对于您的“更好”解决方案,您必须比我有时间深入到ScrollPaneUI,找到滚动条可见/不可见的挂钩,并在这些点添加/删除您的MouseWheelListener
希望有帮助 我也遇到了这个恼人的问题,Sbodd的解决方案对我来说是不可接受的,因为我需要能够在表和JTextAreas中滚动。我希望其行为与浏览器相同,在可滚动控件上的鼠标将滚动该控件,直到控件触底,然后继续滚动父滚动窗格,通常是整个页面的滚动窗格 这门课就可以做到这一点。只需使用它来代替常规的JScrollPane。我希望它能帮助你
/**
* A JScrollPane that will bubble a mouse wheel scroll event to the parent
* JScrollPane if one exists when this scrollpane either tops out or bottoms out.
*/
public class PDControlScrollPane extends JScrollPane {
public PDControlScrollPane() {
super();
addMouseWheelListener(new PDMouseWheelListener());
}
class PDMouseWheelListener implements MouseWheelListener {
private JScrollBar bar;
private int previousValue = 0;
private JScrollPane parentScrollPane;
private JScrollPane getParentScrollPane() {
if (parentScrollPane == null) {
Component parent = getParent();
while (!(parent instanceof JScrollPane) && parent != null) {
parent = parent.getParent();
}
parentScrollPane = (JScrollPane)parent;
}
return parentScrollPane;
}
public PDMouseWheelListener() {
bar = PDControlScrollPane.this.getVerticalScrollBar();
}
public void mouseWheelMoved(MouseWheelEvent e) {
JScrollPane parent = getParentScrollPane();
if (parent != null) {
/*
* Only dispatch if we have reached top/bottom on previous scroll
*/
if (e.getWheelRotation() < 0) {
if (bar.getValue() == 0 && previousValue == 0) {
parent.dispatchEvent(cloneEvent(e));
}
} else {
if (bar.getValue() == getMax() && previousValue == getMax()) {
parent.dispatchEvent(cloneEvent(e));
}
}
previousValue = bar.getValue();
}
/*
* If parent scrollpane doesn't exist, remove this as a listener.
* We have to defer this till now (vs doing it in constructor)
* because in the constructor this item has no parent yet.
*/
else {
PDControlScrollPane.this.removeMouseWheelListener(this);
}
}
private int getMax() {
return bar.getMaximum() - bar.getVisibleAmount();
}
private MouseWheelEvent cloneEvent(MouseWheelEvent e) {
return new MouseWheelEvent(getParentScrollPane(), e.getID(), e
.getWhen(), e.getModifiers(), 1, 1, e
.getClickCount(), false, e.getScrollType(), e
.getScrollAmount(), e.getWheelRotation());
}
}
}
/**
*一个JScrollPane,将鼠标滚轮滚动事件冒泡到父级
*JScrollPane,如果此滚动窗格顶出或底出时存在。
*/
公共类PDControlScrollPane扩展了JScrollPane{
公共PDControlScrollPane(){
超级();
addMouseWheelListener(新的PDMouseWheelListener());
}
类PDMouseWheelListener实现MouseWheelListener{
私人酒吧;
private int previousValue=0;
私有JScrollPane parentScrollPane;
私有JScrollPane getParentScrollPane(){
if(parentScrollPane==null){
组件父级=getParent();
而(!(JScrollPane的父实例)&&parent!=null){
parent=parent.getParent();
}
parentScrollPane=(JScrollPane)父级;
}
返回滚动窗格;
}
公共PDMouseWheelListener(){
bar=pdcontrolScrolPane.this.getVerticalScrollBar();
}
已移动公共无效鼠标滚轮(鼠标滚轮事件e){
JScrollPane parent=getParentScrollPane();
如果(父项!=null){
/*
*仅当我们到达上一个滚动条的顶部/底部时发送
*/
如果(如getWheelRotation()<0){
if(bar.getValue()==0&&previousValue==0){
父调度事件(cloneEvent(e));
}
}否则{
if(bar.getValue()==getMax()&&previousValue==getMax()){
父调度事件(cloneEvent(e));
}
}
previousValue=bar.getValue();
}
/*
*如果父滚动窗格不存在,请将其作为侦听器删除。
*我们必须推迟到现在(vs在构造函数中进行)
*因为在构造函数中,此项还没有父项。
*/
否则{
pControlScrolPane.this.removeMouseWheelListener(this);
}
}
私有int getMax(){
返回bar.getMaximum()-bar.getVisibleMount();
}
专用MouseweelEvent cloneEvent(MouseweelEvent e){
返回新的MouseWheelEvent(getParentScrollPane(),e.getID(),e
.getWhen(),e.getModifiers(),1,1,e
.getClickCount(),false,e.getScrollType(),e
.getScrollAmount(),e.getWheelRotation());
}
}
}
@Nemi已经有了一个很好的解决方案
我将其进一步简化,将以下方法放入我的库中:
static public void passMouseWheelEventsToParent(final Component pComponent, final Component pParent) {
pComponent.addMouseWheelListener((final MouseWheelEvent pE) -> {
pParent.dispatchEvent(new MouseWheelEvent(pParent, pE.getID(), pE.getWhen(), pE.getModifiers(), 1, 1, pE.getClickCount(), false, pE.getScrollType(), pE.getScrollAmount(), pE.getWheelRotation()));
});
}
受现有答案的启发,我
- 从中获取代码
- 将其与相结合,以避免冗长地构造
mouseweelevent
- 将侦听器提取到它自己的顶级类中,以便可以在
类无法扩展的上下文中使用它JScrollPane
- 尽可能地内联代码
import java.awt.Component;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import javax.swing.JScrollBar;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
/**
* Passes mouse wheel events to the parent component if this component
* cannot scroll further in the given direction.
* <p>
* This behavior is a little better than Swing's default behavior but
* still worse than the behavior of Google Chrome, which remembers the
* currently scrolling component and sticks to it until a timeout happens.
*
* @see <a href="https://stackoverflow.com/a/53687022">Stack Overflow</a>
*/
public final class MouseWheelScrollListener implements MouseWheelListener {
private final JScrollPane pane;
private int previousValue;
public MouseWheelScrollListener(JScrollPane pane) {
this.pane = pane;
previousValue = pane.getVerticalScrollBar().getValue();
}
public void mouseWheelMoved(MouseWheelEvent e) {
Component parent = pane.getParent();
while (!(parent instanceof JScrollPane)) {
if (parent == null) {
return;
}
parent = parent.getParent();
}
JScrollBar bar = pane.getVerticalScrollBar();
int limit = e.getWheelRotation() < 0 ? 0 : bar.getMaximum() - bar.getVisibleAmount();
if (previousValue == limit && bar.getValue() == limit) {
parent.dispatchEvent(SwingUtilities.convertMouseEvent(pane, e, parent));
}
previousValue = bar.getValue();
}
}
一旦创建了此类的实例并将其绑定到滚动窗格,它就不能再用于其他组件,因为它记住了垂直滚动条的上一个位置。谢谢,这完全符合我的要求。但这段代码不是同时滚动两个组件吗?我发现这相当令人困惑。正如Sbodd、Nemi、exhuma等陈述的,以及这个主题的来源一样,JTextPane拒绝将事件传递给周围的JScrollPane。所以,使用这个,只有JScrollPane会滚动。。。另一方面,其他解决方案更好,即Nemi解决方案和Sbodd解决方案。另请参见
JScrollPane pane = new JScrollPane();
pane.addMouseWheelListener(new MouseWheelScrollListener(pane));