【问题标题】:Is it possible to to find the pixel position of the first tick mark on a jSlider scale?是否可以在 jSlider 刻度上找到第一个刻度线的像素位置?
【发布时间】:2013-03-02 07:08:41
【问题描述】:

我正在尝试将自定义绘制的图形与 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());

【问题讨论】:

    标签: jslider


    【解决方案1】:

    我应该在很久以前就回答这个问题,因为我最终找到了解决办法。
    我从这里修改了范围滑块的代码:http://ernienotes.wordpress.com/2010/12/27/creating-a-java-swing-range-slider/ 抱歉,我不得不删除 Java Doc cmets 以使其符合 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;
                }
            }
        }
    }
    

    轨道矩形的 x 位置(也是第一个刻度线的位置),用于将图形与滑块对齐,通过调用获得:

    rangeSlider1.getRangeUI().getTrackRect()
    

    我也不喜欢他们的圆形旋钮,把它们换成更传统的形状。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-02-09
      • 2016-06-03
      • 1970-01-01
      • 2015-02-07
      • 1970-01-01
      • 1970-01-01
      • 2023-04-01
      相关资源
      最近更新 更多