使用 headers 缩放 JScrollPane
Zoom on a JScrollPane with headers
我有一个包含 table 行和列 header 的 JFrame。
我的 table 是由 3 个面板(行 header、列 header 和网格)组成的自定义组件。
这些面板是常规的 JPanel,在 MigLayout 中包含 JButton 或 JLabel。
我在 JScrollPane 中显示此组件,以便同时滚动我的网格和我的 headers。
这部分工作正常。
现在,用户应该可以缩放我的组件了。
我尝试使用 pbjar JXLayer,但如果我将整个 JScrollPane 放入层中,所有内容都会缩放,触发滚动条。
我尝试使用 3 个 JXLayer,一个用于我的 JScrollPane 的每个视口。但是这个解决方案只是弄乱了我的布局,因为视口内的面板居中而不是 top-left 对齐。
import org.jdesktop.jxlayer.JXLayer;
import org.pbjar.jxlayer.demo.TransformUtils;
import org.pbjar.jxlayer.plaf.ext.transform.DefaultTransformModel;
public class Matrix extends JScrollPane {
private Grid grid;
private Header rowHeader;
private Header columnHeader;
private DefaultTransformModel zoomTransformModel;
private double zoom = 1;
public Matrix() {
super(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
this.zoomTransformModel1 = new DefaultTransformModel();
this.zoomTransformModel1.setScaleToPreferredSize(true);
this.zoomTransformModel1.setScale(1);
this.zoomTransformModel2 = new DefaultTransformModel();
this.zoomTransformModel2.setScaleToPreferredSize(true);
this.zoomTransformModel2.setScale(1);
this.zoomTransformModel3 = new DefaultTransformModel();
this.zoomTransformModel3.setScaleToPreferredSize(true);
this.zoomTransformModel3.setScale(1);
this.grid = new Grid();
this.setViewportView(TransformUtils.createTransformJXLayer(this.grid,
zoomTransformModel1););
this.matrixRowHeader = new Header(Orientation.VERTICAL);
this.setRowHeader(new JViewport(
TransformUtils.createTransformJXLayer(
this.rowHeader, zoomTransformModel2)));
this.matrixColumnHeader = new Header(Orientation.HORIZONTAL);
this.setColumnHeader(new JViewport(
TransformUtils.createTransformJXLayer(
this.columnHeader, zoomTransformModel2)));
}
public void setScale(double scale) {
this.zoomTransformModel1.setScale(scale);
this.zoomTransformModel2.setScale(scale);
this.zoomTransformModel3.setScale(scale);
}
}
我如何处理 JScrollPane 的缩放而不缩放 scrollBars 并且不弄乱我的布局?
首先,MigLayout 似乎与JXLayer 不兼容。当同时使用两者时,使用 MigLayout 的面板中的组件具有不可预测的行为。
然后,原始的 pbjar JXLayer 只允许您将组件放在 Layer 窗格的中心。
可以下载 Pbjar 源 on github。请注意,这不是官方的 Piet Blok 存储库。
我找到的解决方案是修改TransformLayout
、TransformUI
和TranformModel
类:
Alignment
enum 给出层中组件的可能对齐方式。
public enum Alignment {
TOP,
BOTTOM,
LEFT,
RIGHT,
CENTER
}
在TransformLayout
中:
@Override
public void layoutContainer(Container parent) {
JXLayer<?> layer = (JXLayer<?>) parent;
LayerUI<?> layerUI = layer.getUI();
if (layerUI instanceof CustomTransformUI) {
JComponent view = (JComponent) layer.getView();
JComponent glassPane = layer.getGlassPane();
if (view != null) {
Rectangle innerArea = new Rectangle();
SwingUtilities.calculateInnerArea(layer, innerArea);
view.setSize(view.getPreferredSize());
Rectangle viewRect = new Rectangle(0, 0, view.getWidth(), view
.getHeight());
int x;
int y;
Alignment alignX = ((CustomTransformUI) layerUI).getAlignX();
Alignment alignY = ((CustomTransformUI) layerUI).getAlignY();
if(alignX == Alignment.LEFT) {
x = (int) (innerArea.getX() - viewRect.getX());
} else if(alignX == Alignment.RIGHT) {
x = (int) (innerArea.getX()+innerArea.getWidth()-viewRect.getWidth()-viewRect.getX());
} else {
x = (int) Math.round(innerArea.getCenterX()
- viewRect.getCenterX());
}
if(alignY == Alignment.TOP) {
y = (int) (innerArea.getY() - viewRect.getY());
} else if(alignY == Alignment.BOTTOM) {
y = (int) (innerArea.getY()+innerArea.getHeight()-viewRect.getHeight()-viewRect.getY());
} else {
y = (int) Math.round(innerArea.getCenterY()
- viewRect.getCenterY());
}
viewRect.translate(x, y);
view.setBounds(viewRect);
}
if (glassPane != null) {
glassPane.setLocation(0, 0);
glassPane.setSize(layer.getWidth(), layer.getHeight());
}
return;
}
super.layoutContainer(parent);
}
在TransformUI
中:
private Alignment alignX; // horizontal alignment
private Alignment alignY; // verticalalignment
public TransformUI(TransformModel model, Alignment alignX, Alignment alignY) {
super();
this.setModel(model);
this.alignX = alignX;
this.alignY = alignY;
}
public Alignment getAlignX() {
return alignX;
}
public Alignment getAlignY() {
return alignY;
}
在TransformModel
中:
private Alignment alignX = Alignment.CENTER;
private Alignment alignY = Alignment.CENTER;
public CustomTransformModel(Alignment alignX, Alignment alignY) {
super();
this.alignX = alignX;
this.alignY = alignY;
}
@Override
public AffineTransform getTransform(JXLayer<? extends JComponent> layer) {
JComponent view = (JComponent)layer.getView();
/*
* Set the current actual program values in addition to the user
* options.
*/
this.setValue(Type.LayerWidth, layer == null ? 0 : layer.getWidth());
this.setValue(Type.LayerHeight, layer == null ? 0 : layer.getHeight());
this.setValue(Type.ViewWidth, view == null ? 0 : view.getWidth());
this.setValue(Type.ViewHeight, view == null ? 0 : view.getHeight());
/*
* If any change to previous values, recompute the transform.
*/
if (!Arrays.equals(this.prevValues, this.values)) {
System.arraycopy(this.values, 0, this.prevValues, 0, this.values.length);
this.transform.setToIdentity();
if (view != null) {
double scaleX;
double scaleY;
double centerX;
if(this.alignX == Alignment.LEFT) {
centerX = 0.0;
} else if (this.alignX == Alignment.RIGHT){
centerX = layer == null ? 0.0 : (double)layer.getWidth();
} else {
centerX = layer == null ? 0.0 : (double)layer.getWidth() / 2.0;
}
double centerY;
if(this.alignY == Alignment.TOP) {
centerY = 0.0;
} else if(this.alignY == Alignment.BOTTOM){
centerY = layer == null ? 0.0 : (double)layer.getHeight();
} else {
centerY = layer == null ? 0.0 : (double)layer.getHeight() / 2.0;
}
AffineTransform nonScaledTransform = this.transformNoScale(centerX, centerY);
if (((Boolean)this.getValue(Type.ScaleToPreferredSize)).booleanValue()) {
scaleY = scaleX = ((Double)this.getValue(Type.PreferredScale)).doubleValue();
} else {
Area area = new Area(new Rectangle2D.Double(0.0, 0.0, view.getWidth(), view.getHeight()));
area.transform(nonScaledTransform);
Rectangle2D bounds = area.getBounds2D();
scaleX = layer == null ? 0.0 : (double)layer.getWidth() / bounds.getWidth();
scaleY = layer == null ? 0.0 : (double)layer.getHeight() / bounds.getHeight();
if (((Boolean)this.getValue(Type.PreserveAspectRatio)).booleanValue()) {
scaleY = scaleX = Math.min(scaleX, scaleY);
}
}
this.transform.translate(centerX, centerY);
this.transform.scale((Boolean)this.getValue(Type.Mirror) != false ? - scaleX : scaleX, scaleY);
this.transform.translate(- centerX, - centerY);
this.transform.concatenate(nonScaledTransform);
}
}
return this.transform;
}
您现在可以使用以下方式创建具有可配置对齐方式的可缩放面板:
TransformModel model = new TransformModel(Alignment.LEFT, Alignment.TOP);
TransformUI ui = new TransformUI(model, Alignment.LEFT, Alignment.TOP);
new JXLayer((Component)component, (LayerUI)ui)
请注意,这是一个快速修复。它可能可以改进。
我有一个包含 table 行和列 header 的 JFrame。 我的 table 是由 3 个面板(行 header、列 header 和网格)组成的自定义组件。 这些面板是常规的 JPanel,在 MigLayout 中包含 JButton 或 JLabel。 我在 JScrollPane 中显示此组件,以便同时滚动我的网格和我的 headers。 这部分工作正常。
现在,用户应该可以缩放我的组件了。
我尝试使用 pbjar JXLayer,但如果我将整个 JScrollPane 放入层中,所有内容都会缩放,触发滚动条。
我尝试使用 3 个 JXLayer,一个用于我的 JScrollPane 的每个视口。但是这个解决方案只是弄乱了我的布局,因为视口内的面板居中而不是 top-left 对齐。
import org.jdesktop.jxlayer.JXLayer;
import org.pbjar.jxlayer.demo.TransformUtils;
import org.pbjar.jxlayer.plaf.ext.transform.DefaultTransformModel;
public class Matrix extends JScrollPane {
private Grid grid;
private Header rowHeader;
private Header columnHeader;
private DefaultTransformModel zoomTransformModel;
private double zoom = 1;
public Matrix() {
super(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
this.zoomTransformModel1 = new DefaultTransformModel();
this.zoomTransformModel1.setScaleToPreferredSize(true);
this.zoomTransformModel1.setScale(1);
this.zoomTransformModel2 = new DefaultTransformModel();
this.zoomTransformModel2.setScaleToPreferredSize(true);
this.zoomTransformModel2.setScale(1);
this.zoomTransformModel3 = new DefaultTransformModel();
this.zoomTransformModel3.setScaleToPreferredSize(true);
this.zoomTransformModel3.setScale(1);
this.grid = new Grid();
this.setViewportView(TransformUtils.createTransformJXLayer(this.grid,
zoomTransformModel1););
this.matrixRowHeader = new Header(Orientation.VERTICAL);
this.setRowHeader(new JViewport(
TransformUtils.createTransformJXLayer(
this.rowHeader, zoomTransformModel2)));
this.matrixColumnHeader = new Header(Orientation.HORIZONTAL);
this.setColumnHeader(new JViewport(
TransformUtils.createTransformJXLayer(
this.columnHeader, zoomTransformModel2)));
}
public void setScale(double scale) {
this.zoomTransformModel1.setScale(scale);
this.zoomTransformModel2.setScale(scale);
this.zoomTransformModel3.setScale(scale);
}
}
我如何处理 JScrollPane 的缩放而不缩放 scrollBars 并且不弄乱我的布局?
首先,MigLayout 似乎与JXLayer 不兼容。当同时使用两者时,使用 MigLayout 的面板中的组件具有不可预测的行为。
然后,原始的 pbjar JXLayer 只允许您将组件放在 Layer 窗格的中心。 可以下载 Pbjar 源 on github。请注意,这不是官方的 Piet Blok 存储库。
我找到的解决方案是修改TransformLayout
、TransformUI
和TranformModel
类:
Alignment
enum 给出层中组件的可能对齐方式。
public enum Alignment {
TOP,
BOTTOM,
LEFT,
RIGHT,
CENTER
}
在TransformLayout
中:
@Override
public void layoutContainer(Container parent) {
JXLayer<?> layer = (JXLayer<?>) parent;
LayerUI<?> layerUI = layer.getUI();
if (layerUI instanceof CustomTransformUI) {
JComponent view = (JComponent) layer.getView();
JComponent glassPane = layer.getGlassPane();
if (view != null) {
Rectangle innerArea = new Rectangle();
SwingUtilities.calculateInnerArea(layer, innerArea);
view.setSize(view.getPreferredSize());
Rectangle viewRect = new Rectangle(0, 0, view.getWidth(), view
.getHeight());
int x;
int y;
Alignment alignX = ((CustomTransformUI) layerUI).getAlignX();
Alignment alignY = ((CustomTransformUI) layerUI).getAlignY();
if(alignX == Alignment.LEFT) {
x = (int) (innerArea.getX() - viewRect.getX());
} else if(alignX == Alignment.RIGHT) {
x = (int) (innerArea.getX()+innerArea.getWidth()-viewRect.getWidth()-viewRect.getX());
} else {
x = (int) Math.round(innerArea.getCenterX()
- viewRect.getCenterX());
}
if(alignY == Alignment.TOP) {
y = (int) (innerArea.getY() - viewRect.getY());
} else if(alignY == Alignment.BOTTOM) {
y = (int) (innerArea.getY()+innerArea.getHeight()-viewRect.getHeight()-viewRect.getY());
} else {
y = (int) Math.round(innerArea.getCenterY()
- viewRect.getCenterY());
}
viewRect.translate(x, y);
view.setBounds(viewRect);
}
if (glassPane != null) {
glassPane.setLocation(0, 0);
glassPane.setSize(layer.getWidth(), layer.getHeight());
}
return;
}
super.layoutContainer(parent);
}
在TransformUI
中:
private Alignment alignX; // horizontal alignment
private Alignment alignY; // verticalalignment
public TransformUI(TransformModel model, Alignment alignX, Alignment alignY) {
super();
this.setModel(model);
this.alignX = alignX;
this.alignY = alignY;
}
public Alignment getAlignX() {
return alignX;
}
public Alignment getAlignY() {
return alignY;
}
在TransformModel
中:
private Alignment alignX = Alignment.CENTER;
private Alignment alignY = Alignment.CENTER;
public CustomTransformModel(Alignment alignX, Alignment alignY) {
super();
this.alignX = alignX;
this.alignY = alignY;
}
@Override
public AffineTransform getTransform(JXLayer<? extends JComponent> layer) {
JComponent view = (JComponent)layer.getView();
/*
* Set the current actual program values in addition to the user
* options.
*/
this.setValue(Type.LayerWidth, layer == null ? 0 : layer.getWidth());
this.setValue(Type.LayerHeight, layer == null ? 0 : layer.getHeight());
this.setValue(Type.ViewWidth, view == null ? 0 : view.getWidth());
this.setValue(Type.ViewHeight, view == null ? 0 : view.getHeight());
/*
* If any change to previous values, recompute the transform.
*/
if (!Arrays.equals(this.prevValues, this.values)) {
System.arraycopy(this.values, 0, this.prevValues, 0, this.values.length);
this.transform.setToIdentity();
if (view != null) {
double scaleX;
double scaleY;
double centerX;
if(this.alignX == Alignment.LEFT) {
centerX = 0.0;
} else if (this.alignX == Alignment.RIGHT){
centerX = layer == null ? 0.0 : (double)layer.getWidth();
} else {
centerX = layer == null ? 0.0 : (double)layer.getWidth() / 2.0;
}
double centerY;
if(this.alignY == Alignment.TOP) {
centerY = 0.0;
} else if(this.alignY == Alignment.BOTTOM){
centerY = layer == null ? 0.0 : (double)layer.getHeight();
} else {
centerY = layer == null ? 0.0 : (double)layer.getHeight() / 2.0;
}
AffineTransform nonScaledTransform = this.transformNoScale(centerX, centerY);
if (((Boolean)this.getValue(Type.ScaleToPreferredSize)).booleanValue()) {
scaleY = scaleX = ((Double)this.getValue(Type.PreferredScale)).doubleValue();
} else {
Area area = new Area(new Rectangle2D.Double(0.0, 0.0, view.getWidth(), view.getHeight()));
area.transform(nonScaledTransform);
Rectangle2D bounds = area.getBounds2D();
scaleX = layer == null ? 0.0 : (double)layer.getWidth() / bounds.getWidth();
scaleY = layer == null ? 0.0 : (double)layer.getHeight() / bounds.getHeight();
if (((Boolean)this.getValue(Type.PreserveAspectRatio)).booleanValue()) {
scaleY = scaleX = Math.min(scaleX, scaleY);
}
}
this.transform.translate(centerX, centerY);
this.transform.scale((Boolean)this.getValue(Type.Mirror) != false ? - scaleX : scaleX, scaleY);
this.transform.translate(- centerX, - centerY);
this.transform.concatenate(nonScaledTransform);
}
}
return this.transform;
}
您现在可以使用以下方式创建具有可配置对齐方式的可缩放面板:
TransformModel model = new TransformModel(Alignment.LEFT, Alignment.TOP);
TransformUI ui = new TransformUI(model, Alignment.LEFT, Alignment.TOP);
new JXLayer((Component)component, (LayerUI)ui)
请注意,这是一个快速修复。它可能可以改进。