読者です 読者をやめる 読者になる 読者になる

イベントを伝播するJScrollPane

JScrollPaneを入れ子構造にした場合、マウス位置の最も上にあるJScrollPaneがイベントを消費してしまい、結果スクロール途中のマウスの下に入れ子のJScrollPaneが来てしまうとスクロールを止められてしまいとても不便です。
そんなわで、上もしくは下までスクロールしきったJScrollPaneのイベントを下のコンポーネントにスルーしてくれるMouseWheelListenerを作ったのでメモ。

JScrollPaneをわざわざメンバー変数に保持してるけど、MouseWheelEventから取り出したほうがよかったかも。

import java.awt.Component;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import javax.swing.JScrollBar;
import javax.swing.JScrollPane;

/**
 * JScrollPaneがスクロール方向の限界値までスクロールしている場合に
 * 親コンポーネントへイベントを伝播させるMouseWheelListenerの実装です。
 *
 */
public class JScrollPaneHandler implements MouseWheelListener{

    private JScrollPane mTarget;

    /**
     * MouseWheelListenerExを生成します。
     * @param target このリスナを登録するJScrollPane
     */
    public JScrollPaneHandler( JScrollPane target ){
        mTarget = target;
    }

    /**
     * 登録されているJScrollPaneがスクロール方向の限界値までスクロールしている場合に
     * イベントを親コンポーネントに伝播させます。
     * @param e MouseWheelEvent
     */
    @Override
    public void mouseWheelMoved( MouseWheelEvent e ){
        JScrollBar vBar = mTarget.getVerticalScrollBar();

        int rotation = e.getWheelRotation();
        int current = vBar.getValue();
        int height = mTarget.getHeight();

        if( rotation > 0 ){         //下にスクロール
            int max = vBar.getMaximum() - height;
            if( current >= max ){   //限界値までスクロールしている
                dispatchParent( e );
            }
        }else if( rotation < 0 ){   //上にスクロール
            int min = vBar.getMinimum();
            if( current <= min ){   //限界値までスクロールしている
                dispatchParent( e );
            }
        }
    }

    /**
     * 登録されているJComponentの親コンポーネントにMouseWheelイベントを発行します。
     * 親コンポーネントが存在しない場合は何もしません。
     * @param e 発行するMouseWheelEvent
     */
    private void dispatchParent( MouseWheelEvent e ){
        Component parent = mTarget.getParent();
        if( parent != null ){
            parent.dispatchEvent( e );
        }
    }
}