0

我正在尝试将自定义绘制的图形与 jSlider 的比例对齐(滑块提供图形的 X 轴比例)。我需要滑块旋钮来设置与图形相关的值,所以它确实需要是一个 jSlider。

麻烦的是,如果刻度标签的长度发生变化,那么刻度的第一个(和最后一个)刻度线的起始位置会不同,这需要我手动更改图形的位置以匹配。

是否可以设置滑块的比例,然后确定第一个刻度线的像素位置,以便我可以设置图表的开始以匹配?

BasicSliderUI 的 trackRect.x 将为我提供正确的 X 点位置,但我无法访问它。我不断使用以下代码在 trackRect 上获得空指针;我显然在这里遗漏了一些非常基本的东西,因为我已经看到 RangeSlider 通过扩展 BasicSliderUI 访问 trackRect.x。我根本没有像它们一样覆盖任何东西:

访问 trackRect.x 值的类:

import javax.swing.JSlider;
import javax.swing.plaf.basic.BasicSliderUI;


public class ExtendedBasicSliderUI  extends BasicSliderUI
{
   public ExtendedBasicSliderUI(JSlider b)
   {
       super(b);
   }

   public int getScaleStartPosition()
   {
       return trackRect.x;
   }

}

在包含滑块的视图的构造函数中:

initComponents();
extendedUI = new ExtendedBasicSliderUI(jSlider1);
jSlider1.setUI(extendedUI);

criteriaModel.setGraphAlignmentIntervalRatio(extendedUI.getScaleStartPosition());
4

1 回答 1

0

我应该在很久以前就回答这个问题,因为我最终找到了解决办法。
我从这里修改了范围滑块的代码:http: //ernienotes.wordpress.com/2010/12/27/creating-a-java-swing-range-slider/ 抱歉,我不得不删除 Java Doc 注释以适应它在 3000 个字符的限制内。

滑块类代码是这样的:

import javax.swing.JSlider;

public class RangeSlider extends JSlider
{
    RangeSliderUI rangeUI;

    public RangeSlider()
    {
        initSlider();
    }

    public RangeSlider(int min, int max)
    {
        super(min, max);
        initSlider();
    }

    private void initSlider()
    {
        setOrientation(HORIZONTAL);
    }

    @Override
    public void updateUI()
    {
        rangeUI = new RangeSliderUI(this);
        setUI(rangeUI);
        // Update UI for slider labels.  This must be called after updating the
        // UI of the slider.  Refer to JSlider.updateUI().
        updateLabelUIs();
    }

    @Override
    public int getValue()
    {
        return super.getValue();
    }

    public RangeSliderUI getRangeUI()
    {
        return rangeUI;
    }

    @Override
    public void setValue(int value)
    {
        int oldValue = getValue();
        if (oldValue == value)
        {
            return;
        }

        // Compute new value and extent to maintain upper value.
        int oldExtent = getExtent();
        int newValue = Math.min(Math.max(getMinimum(), value), oldValue + oldExtent);
        int newExtent = oldExtent + oldValue - newValue;

        // Set new value and extent, and fire a single change event.
        getModel().setRangeProperties(newValue, newExtent, getMinimum(),
            getMaximum(), getValueIsAdjusting());
    }

    public int getUpperValue()
    {
        return getValue() + getExtent();
    }

    /**
     * Sets the upper value in the range.
     */
    public void setUpperValue(int value)
    {
        // Compute new extent.
        int lowerValue = getValue();
        int newExtent = Math.min(Math.max(0, value - lowerValue), getMaximum() - lowerValue);

        // Set extent to set upper value.
        setExtent(newExtent);
    }

    public boolean isRangeSlider()
    {
        return rangeUI.isRangeSlider();
    }

    /**
     * Sets whether this is currently a range slider or not. (true is range slider, false is not range slider)
     */
    public void setRangeSlider(boolean rangeSlider)
    {
        this.rangeUI.setRangeSlider(rangeSlider);
    }
}

UI类是这样的:

package YourPackage;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.event.MouseEvent;

import javax.swing.JComponent;
import javax.swing.JSlider;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.plaf.basic.BasicSliderUI;

/**
 * UI delegate for the RangeSlider component.  RangeSliderUI paints two thumbs,
 * one for the lower value and one for the upper value.
 * 
 * Setting rangeSlider = false; causes the slider to revert to being a normal slider
 * rather than a range slider. 
 */
class RangeSliderUI extends BasicSliderUI
{

    /** Color of selected range. */
    private Color rangeColor = Color.darkGray;
    /** Location and size of thumb for upper value. */
    private Rectangle upperThumbRect;
    /** Indicator that determines whether upper thumb is selected. */
    private boolean upperThumbSelected;
    /** Indicator that determines whether lower thumb is being dragged. */
    private transient boolean lowerDragging;
    /** Indicator that determines whether upper thumb is being dragged. */
    private transient boolean upperDragging;
    // Range/single slider (true is range slider/ false is single slider)
    private boolean rangeSlider = false;

    public RangeSliderUI(RangeSlider b)
    {
        super(b);
    }

    public int getTrackRect()
    {
        return trackRect.x;
    }

    public boolean isRangeSlider()
    {
        return rangeSlider;
    }

    public void setRangeSlider(boolean rangeSlider)
    {
        this.rangeSlider = rangeSlider;
    }

    @Override
    public void installUI(JComponent c)
    {
        upperThumbRect = new Rectangle();
        super.installUI(c);
    }

    @Override
    protected TrackListener createTrackListener(JSlider slider)
    {
        return new RangeTrackListener();
    }

    @Override
    protected ChangeListener createChangeListener(JSlider slider)
    {
        return new ChangeHandler();
    }

    @Override
    protected void calculateThumbSize()
    {
        // Call superclass method for lower thumb size.
        super.calculateThumbSize();

        // Set upper thumb size.
        upperThumbRect.setSize(thumbRect.width, thumbRect.height);
    }

    @Override
    protected void calculateThumbLocation()
    {
        // Call superclass method for lower thumb location.
        super.calculateThumbLocation();

        // Adjust upper value to snap to ticks if necessary.
        if (slider.getSnapToTicks())
        {
            int upperValue = slider.getValue() + slider.getExtent();
            int snappedValue = upperValue;
            int majorTickSpacing = slider.getMajorTickSpacing();
            int minorTickSpacing = slider.getMinorTickSpacing();
            int tickSpacing = 0;

            if (minorTickSpacing > 0)
            {
                tickSpacing = minorTickSpacing;
            }
            else if (majorTickSpacing > 0)
            {
                tickSpacing = majorTickSpacing;
            }

            if (tickSpacing != 0)
            {
                // If it's not on a tick, change the value
                if ((upperValue - slider.getMinimum()) % tickSpacing != 0)
                {
                    float temp = (float) (upperValue - slider.getMinimum()) / (float) tickSpacing;
                    int whichTick = Math.round(temp);
                    snappedValue = slider.getMinimum() + (whichTick * tickSpacing);
                }

                if (snappedValue != upperValue)
                {
                    slider.setExtent(snappedValue - slider.getValue());
                }
            }
        }

        // Calculate upper thumb location.  The thumb is centered over its value on the track.
        if (slider.getOrientation() == JSlider.HORIZONTAL)
        {
            int upperPosition = xPositionForValue(slider.getValue() + slider.getExtent());
            upperThumbRect.x = upperPosition - (upperThumbRect.width / 2);
            upperThumbRect.y = trackRect.y;
        }
        else
        {
            int upperPosition = yPositionForValue(slider.getValue() + slider.getExtent());
            upperThumbRect.x = trackRect.x;
            upperThumbRect.y = upperPosition - (upperThumbRect.height / 2);
        }
    }

    @Override
    protected Dimension getThumbSize()
    {
        return new Dimension(12, 12);
    }

    @Override
    public void paint(Graphics g, JComponent c)
    {
        super.paint(g, c);

    }

    @Override
    public void paintTrack(Graphics g)
    {
        // Draw track.
        super.paintTrack(g);

        Rectangle trackBounds = trackRect;

        if (rangeSlider)
        {
            if (slider.getOrientation() == JSlider.HORIZONTAL)
            {
                // Determine position of selected range by moving from the middle
                // of one thumb to the other.
                int lowerX = thumbRect.x + (thumbRect.width / 2);
                int upperX = upperThumbRect.x + (upperThumbRect.width / 2);

                // Determine track position.
                int cy = (trackBounds.height / 2) - 2;

                // Save color and shift position.
                Color oldColor = g.getColor();
                g.translate(trackBounds.x, trackBounds.y + cy);

                // Draw selected range.
                g.setColor(rangeColor);
                for (int y = 0; y <= 3; y++)
                {
                    g.drawLine(lowerX - trackBounds.x, y, upperX - trackBounds.x, y);
                }

                // Restore position and color.
                g.translate(-trackBounds.x, -(trackBounds.y + cy));
                g.setColor(oldColor);

            }
            else
            {
                // Determine position of selected range by moving from the middle
                // of one thumb to the other.
                int lowerY = thumbRect.x + (thumbRect.width / 2);
                int upperY = upperThumbRect.x + (upperThumbRect.width / 2);

                // Determine track position.
                int cx = (trackBounds.width / 2) - 2;

                // Save color and shift position.
                Color oldColor = g.getColor();
                g.translate(trackBounds.x + cx, trackBounds.y);

                // Draw selected range.
                g.setColor(rangeColor);
                for (int x = 0; x <= 3; x++)
                {
                    g.drawLine(x, lowerY - trackBounds.y, x, upperY - trackBounds.y);
                }

                // Restore position and color.
                g.translate(-(trackBounds.x + cx), -trackBounds.y);
                g.setColor(oldColor);
            }
        }

        // Shifted the knob painting to here so it is painted with the track or not.
        Rectangle clipRect = g.getClipBounds();
        if (upperThumbSelected)
        {
            // Paint lower thumb first, then upper thumb.
            if (clipRect.intersects(thumbRect))
            {
                paintLowerThumb(g);
            }
            if (clipRect.intersects(upperThumbRect))
            {
                paintUpperThumb(g);
            }

        }
        else
        {
            // Paint upper thumb first, then lower thumb.
            if (clipRect.intersects(upperThumbRect))
            {
                paintUpperThumb(g);
            }
            if (clipRect.intersects(thumbRect))
            {
                paintLowerThumb(g);
            }
        }
    }

    @Override
    public void paintThumb(Graphics g)
    {
    }

    private void paintLowerThumb(Graphics g)
    {
        Rectangle knobBounds = thumbRect;
        int w = knobBounds.width;
        int h = knobBounds.height;

        // Create graphics copy.
        Graphics2D g2d = (Graphics2D) g.create();

        // Create default thumb shape.
        Shape thumbShape = createThumbShape(w - 1, h - 1);

        // Draw thumb.
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.translate(knobBounds.x, knobBounds.y);

        g2d.setColor(Color.CYAN);
        g2d.fill(thumbShape);

        g2d.setColor(Color.BLUE);
        g2d.draw(thumbShape);

        // Dispose graphics.
        g2d.dispose();
    }

    private void paintUpperThumb(Graphics g)
    {
        if (rangeSlider)
        {
            Rectangle knobBounds = upperThumbRect;
            int w = knobBounds.width;
            int h = knobBounds.height;

            // Create graphics copy.
            Graphics2D g2d = (Graphics2D) g.create();

            // Create default thumb shape.
            Shape thumbShape = createThumbShape(w - 1, h - 1);

            // Draw thumb.
            g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
            g2d.translate(knobBounds.x, knobBounds.y);

            g2d.setColor(Color.orange);
            g2d.fill(thumbShape);

            g2d.setColor(Color.orange.darker().darker());
            g2d.draw(thumbShape);

            // Dispose graphics.
            g2d.dispose();
        }
    }

    private Shape createThumbShape(int width, int height)
    {
        Point p1 = new Point(0, 0);
        Point p2 = new Point(width, 0);
        Point p3 = new Point(width, (height / 2) + 1);
        Point p4 = new Point((width / 2) + 1, height);
        Point p5 = new Point(0, (height / 2) + 1);


        int[] xs =
        {
            p1.x, p2.x, p3.x, p4.x, p5.x
        };
        int[] ys =
        {
            p1.y, p2.y, p3.y, p4.y, p5.y
        };

        Polygon thumb = new Polygon(xs, ys, xs.length);

        return thumb;
    }

    private void setUpperThumbLocation(int x, int y)
    {
        Rectangle upperUnionRect = new Rectangle();
        upperUnionRect.setBounds(upperThumbRect);

        upperThumbRect.setLocation(x, y);

        SwingUtilities.computeUnion(upperThumbRect.x, upperThumbRect.y, upperThumbRect.width, upperThumbRect.height, upperUnionRect);
        slider.repaint(upperUnionRect.x, upperUnionRect.y, upperUnionRect.width, upperUnionRect.height);
    }

    @Override
    public void scrollByBlock(int direction)
    {
        synchronized (slider)
        {
            int blockIncrement = (slider.getMaximum() - slider.getMinimum()) / 10;
            if (blockIncrement <= 0 && slider.getMaximum() > slider.getMinimum())
            {
                blockIncrement = 1;
            }
            int delta = blockIncrement * ((direction > 0) ? POSITIVE_SCROLL : NEGATIVE_SCROLL);

            if (upperThumbSelected)
            {
                int oldValue = ((RangeSlider) slider).getUpperValue();
                ((RangeSlider) slider).setUpperValue(oldValue + delta);
            }
            else
            {
                int oldValue = slider.getValue();
                slider.setValue(oldValue + delta);
            }
        }
    }

    @Override
    public void scrollByUnit(int direction)
    {
        synchronized (slider)
        {
            int delta = 1 * ((direction > 0) ? POSITIVE_SCROLL : NEGATIVE_SCROLL);

            if (upperThumbSelected)
            {
                int oldValue = ((RangeSlider) slider).getUpperValue();
                ((RangeSlider) slider).setUpperValue(oldValue + delta);
            }
            else
            {
                int oldValue = slider.getValue();
                slider.setValue(oldValue + delta);
            }
        }
    }

    public class ChangeHandler implements ChangeListener
    {

        public void stateChanged(ChangeEvent arg0)
        {
            if (!lowerDragging && !upperDragging)
            {
                calculateThumbLocation();
                slider.repaint();
            }
        }
    }

    public class RangeTrackListener extends TrackListener
    {

        @Override
        public void mousePressed(MouseEvent e)
        {
            if (!slider.isEnabled())
            {
                return;
            }

            currentMouseX = e.getX();
            currentMouseY = e.getY();

            if (slider.isRequestFocusEnabled())
            {
                slider.requestFocus();
            }

            // Determine which thumb is pressed.  If the upper thumb is selected (last one dragged), then check its position first;
            // otherwise check the position of the lower thumb first.
            boolean lowerPressed = false;
            boolean upperPressed = false;

            if (upperThumbSelected)
            {
                // Only enable upper slider movement if in range mode.
                if (upperThumbRect.contains(currentMouseX, currentMouseY) && rangeSlider)
                {
                    upperPressed = true;
                }
                else
                {
                    if (thumbRect.contains(currentMouseX, currentMouseY))
                    {
                        lowerPressed = true;
                    }
                }
            }
            else
            {
                if (thumbRect.contains(currentMouseX, currentMouseY))
                {
                    lowerPressed = true;
                }
                else
                {
                    // Only enable if in range mode.
                    if (upperThumbRect.contains(currentMouseX, currentMouseY) && rangeSlider)
                    {
                        upperPressed = true;
                    }
                }
            }

            // Handle lower thumb pressed.
            if (lowerPressed)
            {
                switch (slider.getOrientation())
                {
                    case JSlider.VERTICAL:
                        offset = currentMouseY - thumbRect.y;
                        break;
                    case JSlider.HORIZONTAL:
                        offset = currentMouseX - thumbRect.x;
                        break;
                }
                upperThumbSelected = false;
                lowerDragging = true;
                return;
            }
            lowerDragging = false;

            // Handle upper thumb pressed.
            if (upperPressed)
            {
                switch (slider.getOrientation())
                {
                    case JSlider.VERTICAL:
                        offset = currentMouseY - upperThumbRect.y;
                        break;
                    case JSlider.HORIZONTAL:
                        offset = currentMouseX - upperThumbRect.x;
                        break;
                }
                upperThumbSelected = true;
                upperDragging = true;
                return;
            }
            upperDragging = false;
        }

        @Override
        public void mouseReleased(MouseEvent e)
        {
            lowerDragging = false;
            upperDragging = false;
            slider.setValueIsAdjusting(false);
            super.mouseReleased(e);
        }

        @Override
        public void mouseDragged(MouseEvent e)
        {
            if (!slider.isEnabled())
            {
                return;
            }

            currentMouseX = e.getX();
            currentMouseY = e.getY();

            if (lowerDragging)
            {
                slider.setValueIsAdjusting(true);
                moveLowerThumb();

            }
            else if (upperDragging)
            {
                slider.setValueIsAdjusting(true);
                moveUpperThumb();
            }
        }

        @Override
        public boolean shouldScroll(int direction)
        {
            return false;
        }

        private void moveLowerThumb()
        {
            int thumbMiddle = 0;

            switch (slider.getOrientation())
            {
                case JSlider.VERTICAL:
                    int halfThumbHeight = thumbRect.height / 2;
                    int thumbTop = currentMouseY - offset;
                    int trackTop = trackRect.y;
                    int trackBottom = trackRect.y + (trackRect.height - 1);
                    int vMax = yPositionForValue(slider.getValue() + slider.getExtent());

                    // Apply bounds to thumb position.
                    if (drawInverted())
                    {
                        trackBottom = vMax;
                    }
                    else
                    {
                        trackTop = vMax;
                    }
                    thumbTop = Math.max(thumbTop, trackTop - halfThumbHeight);
                    thumbTop = Math.min(thumbTop, trackBottom - halfThumbHeight);

                    setThumbLocation(thumbRect.x, thumbTop);

                    // Update slider value.
                    thumbMiddle = thumbTop + halfThumbHeight;
                    slider.setValue(valueForYPosition(thumbMiddle));
                    break;

                case JSlider.HORIZONTAL:
                    int halfThumbWidth = thumbRect.width / 2;
                    int thumbLeft = currentMouseX - offset;
                    int trackLeft = trackRect.x;
                    int trackRight = trackRect.x + (trackRect.width - 1);
                    int hMax = xPositionForValue(slider.getValue() + slider.getExtent());

                    // Apply bounds to thumb position.
                    if (drawInverted())
                    {
                        trackLeft = hMax;
                    }
                    else
                    {
                        trackRight = hMax;
                    }
                    thumbLeft = Math.max(thumbLeft, trackLeft - halfThumbWidth);
                    thumbLeft = Math.min(thumbLeft, trackRight - halfThumbWidth);

                    setThumbLocation(thumbLeft, thumbRect.y);

                    // Update slider value.
                    thumbMiddle = thumbLeft + halfThumbWidth;
                    slider.setValue(valueForXPosition(thumbMiddle));
                    break;

                default:
                    return;
            }
        }

        private void moveUpperThumb()
        {
            int thumbMiddle = 0;

            switch (slider.getOrientation())
            {
                case JSlider.VERTICAL:
                    int halfThumbHeight = thumbRect.height / 2;
                    int thumbTop = currentMouseY - offset;
                    int trackTop = trackRect.y;
                    int trackBottom = trackRect.y + (trackRect.height - 1);
                    int vMin = yPositionForValue(slider.getValue());

                    // Apply bounds to thumb position.
                    if (drawInverted())
                    {
                        trackTop = vMin;
                    }
                    else
                    {
                        trackBottom = vMin;
                    }
                    thumbTop = Math.max(thumbTop, trackTop - halfThumbHeight);
                    thumbTop = Math.min(thumbTop, trackBottom - halfThumbHeight);

                    setUpperThumbLocation(thumbRect.x, thumbTop);

                    // Update slider extent.
                    thumbMiddle = thumbTop + halfThumbHeight;
                    slider.setExtent(valueForYPosition(thumbMiddle) - slider.getValue());
                    break;

                case JSlider.HORIZONTAL:
                    int halfThumbWidth = thumbRect.width / 2;
                    int thumbLeft = currentMouseX - offset;
                    int trackLeft = trackRect.x;
                    int trackRight = trackRect.x + (trackRect.width - 1);
                    int hMin = xPositionForValue(slider.getValue());

                    // Apply bounds to thumb position.
                    if (drawInverted())
                    {
                        trackRight = hMin;
                    }
                    else
                    {
                        trackLeft = hMin;
                    }
                    thumbLeft = Math.max(thumbLeft, trackLeft - halfThumbWidth);
                    thumbLeft = Math.min(thumbLeft, trackRight - halfThumbWidth);

                    setUpperThumbLocation(thumbLeft, thumbRect.y);

                    // Update slider extent.
                    thumbMiddle = thumbLeft + halfThumbWidth;
                    slider.setExtent(valueForXPosition(thumbMiddle) - slider.getValue());
                    break;

                default:
                    return;
            }
        }
    }
}

The x position for the track rectangle (which is also the position of the first tick mark) , to align your graph to the slider, is gotten by calling:

rangeSlider1.getRangeUI().getTrackRect()

I also didn't like their round knobs and changed them to the more traditional shape.

于 2013-10-03T02:29:30.030 回答