自定义控件

package my;
import javax.swing.*;
public class Swing2 {
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
createGUI();
}
});
}
private static void createGUI() {
// System.out.println( System.getProperty("java.class.path"));
JFrame frame = new MyFrame("Swing Demo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(500, 300);
frame.setVisible(true);
}
}
package my;
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.LinearGradientPaint;
import java.awt.Paint;
import java.awt.RenderingHints;
import java.awt.Stroke;
import java.awt.geom.Point2D;
import java.util.Vector;
import javax.swing.BorderFactory;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JToggleButton;
import javax.swing.SwingConstants;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
public class MyFrame extends JFrame
{
public MyFrame(String title)
{
super(title);
// Content Pane
JPanel root = new JPanel();
this.setContentPane(root);
root.setLayout(new FlowLayout());
JCheckBox checkbox = new JCheckBox("喜欢");
root.add(checkbox);
JComboBox<String> combo = new JComboBox<>();
combo.addItem("红");
combo.addItem("绿");
combo.addItem("蓝");
root.add(combo);
checkbox.setPreferredSize(new Dimension(100,30));
combo.setPreferredSize(new Dimension(100,30));
}
}
ToggleButton显示

package my;
import javax.swing.*;
import java.awt.*;
public class MyFrame extends JFrame
{
public MyFrame(String title)
{
super(title);
// Content Pane
JPanel root = new JPanel();
this.setContentPane(root);
root.setLayout(new FlowLayout());
JLabel label = new JLabel("流量监控");
AfToggleButton toggle = new AfToggleButton();
root.add(label);
root.add(toggle);
toggle.setPreferredSize(new Dimension(180, 100));
}
}
package my;
import javax.swing.*;
import java.awt.*;
import java.awt.geom.Arc2D;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Path2D;
public class AfToggleButton extends JPanel
{
// 绘制参数设定
public Color bgColor = new Color(0xFFFFFF); // 主背景
public Color lineColor = new Color(0xDEDEDE); // 描边
public Color darkColor = new Color(0xE1E1E1); // 灰色填充
public Color lightColor = new Color(0x33B4FF); // 高亮填充
public int padding = 2; // 轮廓线与内部圆的距离
@Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
int width = getWidth();
int height = getHeight();
Graphics2D g2d = (Graphics2D) g;
// 平滑绘制 ( 反锯齿 )
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// 算出一个2:1的最大矩形
// Rectangle r = new Rectangle(0,0, 100, 50);
int w = width;
int h = width / 2;
if( h > height)
{
h = height;
w = height * 2;
}
Rectangle r = new Rectangle((width-w)/2, (height-h)/2, w, h);
// 里面两个并排的圆,外层轮郭为拼接曲线(不规则形状的绘制:参考4.3讲)
// 左半区
Rectangle r1 = new Rectangle(r.x,r.y, r.width/2, r.height);
// 右半区
Rectangle r2 = new Rectangle(r.x+r.width/2,r.y, r.width/2, r.height);
// 绘制外部轮廓线
Shape arc1 = new Arc2D.Double (r1, 90, 180, Arc2D.OPEN);
Shape arc2 = new Arc2D.Double (r2, 270, 180, Arc2D.OPEN);
Path2D outline = new Path2D.Double(); // 外轮廓,使用拼装路径
outline.append(arc1.getPathIterator(null), false); // 左半圆弧
outline.append(arc2.getPathIterator(null), true); // 右半圆弧
outline.closePath();
g2d.setPaint(lineColor);
g2d.draw(outline);
g2d.setPaint(bgColor);
g2d.fill(outline);
// 内部画两个圆
drawCircleInside(g2d, r1, padding, lineColor, darkColor);
drawCircleInside(g2d, r2, padding, lineColor, lightColor);
}
// 画内部的小圆
private void drawCircleInside(Graphics2D g2d, Rectangle rect,
int deflate, Paint lineColor, Paint fillColor)
{
Rectangle r = new Rectangle(rect); // 做一个备份,不会修改传入的rect
r.x += deflate;
r.y += deflate;
r.width -= (deflate * 2);
r.height -= (deflate* 2);
Shape shape = new Ellipse2D.Double(r.x, r.y, r.width,r.height);
// 描边
g2d.setPaint(lineColor);
g2d.draw(shape);
// 填充
g2d.setPaint(fillColor);
g2d.fill( shape );
}
}
ToggleButton数据

package my;
import javax.swing.*;
import java.awt.*;
public class MyFrame extends JFrame
{
public MyFrame(String title)
{
super(title);
// Content Pane
JPanel root = new JPanel();
this.setContentPane(root);
root.setLayout(new FlowLayout());
JLabel label = new JLabel("流量监控");
AfToggleButton toggle = new AfToggleButton();
root.add(label);
root.add(toggle);
toggle.setPreferredSize(new Dimension(180, 100));
toggle.setSelected(true);
}
}
package my;
import javax.swing.*;
import java.awt.*;
import java.awt.geom.Arc2D;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Path2D;
public class AfToggleButton extends JPanel
{
private boolean selected = true; // 开关, ON/OFF
// 绘制参数设定
public Color bgColor = new Color(0xFFFFFF); // 主背景
public Color lineColor = new Color(0xDEDEDE); // 描边
public Color darkColor = new Color(0xE1E1E1); // 灰色填充
public Color lightColor = new Color(0x33B4FF); // 高亮填充
public int padding = 2; // 轮廓线与内部圆的距离
// 获取数据
public boolean isSelected()
{
return this.selected;
}
// 设置数据
public void setSelected(boolean selected)
{
this.selected = selected;
repaint();
}
// 切换开关
public void toggle()
{
this.selected = ! this.selected;
repaint();
}
@Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
int width = getWidth();
int height = getHeight();
Graphics2D g2d = (Graphics2D) g;
// 平滑绘制 ( 反锯齿 )
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// 算出一个2:1的最大矩形
// Rectangle r = new Rectangle(0,0, 100, 50);
int w = width;
int h = width / 2;
if( h > height)
{
h = height;
w = height * 2;
}
Rectangle r = new Rectangle((width-w)/2, (height-h)/2, w, h);
// 里面两个并排的圆,外层轮郭为拼接曲线(不规则形状的绘制:参考4.3讲)
// 左半区
Rectangle r1 = new Rectangle(r.x,r.y, r.width/2, r.height);
// 右半区
Rectangle r2 = new Rectangle(r.x+r.width/2,r.y, r.width/2, r.height);
// 绘制外部轮廓线
Shape arc1 = new Arc2D.Double (r1, 90, 180, Arc2D.OPEN);
Shape arc2 = new Arc2D.Double (r2, 270, 180, Arc2D.OPEN);
Path2D outline = new Path2D.Double(); // 外轮廓,使用拼装路径
outline.append(arc1.getPathIterator(null), false); // 左半圆弧
outline.append(arc2.getPathIterator(null), true); // 右半圆弧
outline.closePath();
g2d.setPaint(lineColor);
g2d.draw(outline);
g2d.setPaint(bgColor);
g2d.fill(outline);
// 内部画两个圆
if(selected)
{
drawCircleInside(g2d, r2, padding, lineColor, lightColor);
}
else
{
drawCircleInside(g2d, r1, padding, lineColor, darkColor);
}
}
// 画内部的小圆
private void drawCircleInside(Graphics2D g2d, Rectangle rect,
int deflate, Paint lineColor, Paint fillColor)
{
Rectangle r = new Rectangle(rect); // 做一个备份,不会修改传入的rect
r.x += deflate;
r.y += deflate;
r.width -= (deflate * 2);
r.height -= (deflate* 2);
Shape shape = new Ellipse2D.Double(r.x, r.y, r.width,r.height);
// 描边
g2d.setPaint(lineColor);
g2d.draw(shape);
// 填充
g2d.setPaint(fillColor);
g2d.fill( shape );
}
}
ToggleButton行为

package my;
import javax.swing.*;
import java.awt.*;
public class MyFrame extends JFrame
{
public MyFrame(String title)
{
super(title);
// Content Pane
JPanel root = new JPanel();
this.setContentPane(root);
root.setLayout(new FlowLayout());
JLabel label = new JLabel("流量监控");
AfToggleButton toggle = new AfToggleButton();
root.add(label);
root.add(toggle);
toggle.setPreferredSize(new Dimension(180, 100));
toggle.setSelected(true);
toggle.setStateListener( new AfToggleButton.StateListener() {
@Override
public void stateChanged(Object source)
{
System.out.println("haha...");
}
});
}
}
package my;
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Arc2D;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Path2D;
public class AfToggleButton extends JPanel
{
private boolean selected = true; // 开关, ON/OFF
// 绘制参数设定
public Color bgColor = new Color(0xFFFFFF); // 主背景
public Color lineColor = new Color(0xDEDEDE); // 描边
public Color darkColor = new Color(0xE1E1E1); // 灰色填充
public Color lightColor = new Color(0x33B4FF); // 高亮填充
public int padding = 2; // 轮廓线与内部圆的距离
public AfToggleButton()
{
this.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e)
{
onMouseClicked();
}
});
}
private void onMouseClicked()
{
toggle(); // 点击时切换状态
// 自定义接口
if(stateListener != null)
{
stateListener.stateChanged(this);
}
}
public interface StateListener // 内部类/接口
{
void stateChanged(Object source);
}
private StateListener stateListener; // 属性
public void setStateListener(StateListener listener)
{
this.stateListener = listener;
}
// 获取数据
public boolean isSelected()
{
return this.selected;
}
// 设置数据
public void setSelected(boolean selected)
{
this.selected = selected;
repaint();
}
// 切换开关
public void toggle()
{
this.selected = ! this.selected;
repaint();
}
@Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
int width = getWidth();
int height = getHeight();
Graphics2D g2d = (Graphics2D) g;
// 平滑绘制 ( 反锯齿 )
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// 算出一个2:1的最大矩形
// Rectangle r = new Rectangle(0,0, 100, 50);
int w = width;
int h = width / 2;
if( h > height)
{
h = height;
w = height * 2;
}
Rectangle r = new Rectangle((width-w)/2, (height-h)/2, w, h);
// 里面两个并排的圆,外层轮郭为拼接曲线(不规则形状的绘制:参考4.3讲)
// 左半区
Rectangle r1 = new Rectangle(r.x,r.y, r.width/2, r.height);
// 右半区
Rectangle r2 = new Rectangle(r.x+r.width/2,r.y, r.width/2, r.height);
// 绘制外部轮廓线
Shape arc1 = new Arc2D.Double (r1, 90, 180, Arc2D.OPEN);
Shape arc2 = new Arc2D.Double (r2, 270, 180, Arc2D.OPEN);
Path2D outline = new Path2D.Double(); // 外轮廓,使用拼装路径
outline.append(arc1.getPathIterator(null), false); // 左半圆弧
outline.append(arc2.getPathIterator(null), true); // 右半圆弧
outline.closePath();
g2d.setPaint(lineColor);
g2d.draw(outline);
g2d.setPaint(bgColor);
g2d.fill(outline);
// 内部画两个圆
if(selected)
{
drawCircleInside(g2d, r2, padding, lineColor, lightColor);
}
else
{
drawCircleInside(g2d, r1, padding, lineColor, darkColor);
}
}
// 画内部的小圆
private void drawCircleInside(Graphics2D g2d, Rectangle rect,
int deflate, Paint lineColor, Paint fillColor)
{
Rectangle r = new Rectangle(rect); // 做一个备份,不会修改传入的rect
r.x += deflate;
r.y += deflate;
r.width -= (deflate * 2);
r.height -= (deflate* 2);
Shape shape = new Ellipse2D.Double(r.x, r.y, r.width,r.height);
// 描边
g2d.setPaint(lineColor);
g2d.draw(shape);
// 填充
g2d.setPaint(fillColor);
g2d.fill( shape );
}
}
开关选项设置(练习)

package my;
import javax.swing.*;
public class Swing2
{
private static void createGUI()
{
// JFrame指一个窗口,构造方法的参数为窗口标题
// 语法:因为MyFrame是JFrame的子类,所以可以这么写
JFrame frame = new MyFrame("Swing Demo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// 设置窗口的其他参数,如窗口大小
frame.setSize(500, 300);
// 显示窗口
frame.setVisible(true);
}
public static void main(String[] args)
{
// 此段代码间接地调用了 createGUI(),具体原理在 Swing高级篇 里讲解
// 初学者先照抄此代码框架即可
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run()
{
createGUI();
}
});
}
}
package my;
import javax.swing.*;
import java.awt.*;
public class MyFrame extends JFrame
{
AfToggleButton toggle1 = new AfToggleButton();
AfToggleButton toggle2 = new AfToggleButton();
AfToggleButton toggle3 = new AfToggleButton();
public MyFrame(String title)
{
super(title);
// Content Pane
AfPanel root = new AfPanel();
this.setContentPane(root);
root.setLayout( new AfColumnLayout(10));
root.padding(10);
//
if(true)
{
AfPanel row = new AfPanel();
root.add(row, "30px");
row.setLayout(new AfRowLayout(10));
row.add(new JLabel("流量监控"), "1w");
row.add(toggle1, "100px");
}
if(true)
{
AfPanel row = new AfPanel();
root.add(row, "30px");
row.setLayout(new AfRowLayout(10));
row.add(new JLabel("音量键上下翻页"), "1w");
row.add(toggle2, "100px");
}
if(true)
{
AfPanel row = new AfPanel();
root.add(row, "30px");
row.setLayout(new AfRowLayout(10));
row.add(new JLabel("退出前确认"), "1w");
row.add(toggle3, "100px");
}
}
}
package my;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.util.Calendar;
import javax.swing.JPanel;
public class MyPanel extends JPanel
{
public MyPanel()
{
}
@Override
protected void paintComponent(Graphics g)
{
int width = getWidth();
int height = getHeight();
Graphics2D g2d = (Graphics2D) g;
// 平滑绘制 ( 反锯齿 )
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setPaint(Color.WHITE);
g2d.fillRect(0, 0, width, height);
int x = 0,y = 0;
int x_size = 40; // 单元格宽度
int y_size = 40; // 单元格高度
// 第一行:一、二、三、四、五、六、日
g2d.setPaint( Color.GRAY);
g2d.drawLine(x,y, x + x_size*7, y );
g2d.drawLine(x,y+y_size, x + x_size*7, y + y_size);
String[] cc = {"一", "二", "三", "四", "五", "六", "日" };
g2d.setPaint( Color.GRAY);
g2d.setFont(new Font("宋体", 0,20));
for(int i=0; i<7; i++)
{
Rectangle rect = new Rectangle(x+ i*x_size , y, x_size, y_size);
drawText(g2d, cc[i], rect);
}
// 计算本月第一天是星期几
Calendar cal = Calendar.getInstance();
int theMonth = cal.get(Calendar.MONTH); // 当前是几月
int theDay = cal.get(Calendar.DAY_OF_MONTH); // 当前是几号
cal.set(Calendar.DAY_OF_MONTH, 1);// 向前推到本月第一天
int weekday = cal.get(Calendar.DAY_OF_WEEK); // 计算本月第一天是周几
System.out.println("week:" + weekday);
// 往前推N天
// Calendar里规定Sunday=1(周日), Monday=2, ..., Saturday=7(周六)
// 它是周一,则前推0天,周二,推1天,周六,推5天,周日,推6天
int start = weekday - Calendar.MONDAY;
if(start <0) start = 6;
cal.add(Calendar.DAY_OF_MONTH, 0 - start); // 前推,从上月开始显示
// 绘制五行
// 每月最多31天,可分五行显示
x = 0;
y += y_size;
for(int i=0; i<5; i++) // 5行
{
for(int j=0; j<7; j++) // 7列
{
// 判断月份是否相同
if(cal.get(Calendar.MONTH) == theMonth)
{
g2d.setPaint(Color.BLACK);
}
else
{
g2d.setPaint(Color.GRAY);
}
int day = cal.get(Calendar.DAY_OF_MONTH);
Rectangle rect = new Rectangle(x+x_size*j, y+y_size*i, x_size, y_size);
drawText(g2d, String.valueOf(day), rect);
// 日期 +1
cal.add(Calendar.DAY_OF_MONTH, 1);
}
}
}
// 将str绘制在矩形rect中,居中显示
private void drawText(Graphics2D g2d, String str, Rectangle rect)
{
FontMetrics fm = g2d.getFontMetrics(g2d.getFont());
int fontSize = fm.getHeight(); // 字高
int textWidth = fm.stringWidth(str);
int leading = fm.getLeading();
int ascent = fm.getAscent(); // top -> baseline 的高度
int descent = fm.getDescent(); // bottom->baseline 的高度
// System.out.println(fontSize + "," + leading + "," + ascent + "," + descent);
int x = 0, y = 0;
x = rect.x + (rect.width - textWidth)/2; // 水平居中
y = rect.y + rect.height /2 + (fontSize-leading)/2 - descent; // 竖直居中
g2d.drawString(str, x, y);
}
}
package my;
import java.awt.Dimension;
import java.awt.LayoutManager;
import javax.swing.JPanel;
import af.swing.border.AfBorder;
/* Swing入门篇 7.5 节 */
public class AfPanel extends JPanel
{
public AfPanel layout( LayoutManager manager)
{
this.setLayout(manager);
return this;
}
///////////// padding /////////////
public AfPanel padding( int size)
{
return padding(size, size, size, size);
}
public AfPanel padding( int top, int left, int bottom, int right)
{
AfBorder.addPadding(this, top,left, bottom,right);
return this;
}
////////////// margin ////////////////
public AfPanel margin( int size)
{
return margin(size, size, size, size);
}
public AfPanel margin( int top, int left, int bottom, int right)
{
AfBorder.addMargin(this, top,left, bottom,right);
return this;
}
//////////// max size ///////////
public AfPanel maxSize(int w, int h)
{
this.setMaximumSize(new Dimension(w, h));
return this;
}
public AfPanel maxWidth(int w)
{
Dimension size = this.getMaximumSize();
if(size == null)
size = new Dimension(0,0);
size.width = w;
this.setMaximumSize( size);
return this;
}
public AfPanel maxHeight(int h)
{
Dimension size = this.getMaximumSize();
if(size == null)
size = new Dimension(0,0);
size.height = h;
this.setMaximumSize( size);
return this;
}
//////////// perferred size ///////////
public AfPanel preferredSize(int w, int h)
{
this.setPreferredSize(new Dimension(w, h));
return this;
}
public AfPanel preferredWidth(int w)
{
Dimension size = this.getPreferredSize();
if(size == null)
size = new Dimension(0,0);
size.width = w;
this.setPreferredSize( size);
return this;
}
public AfPanel preferredHeight(int h)
{
Dimension size = this.getPreferredSize();
if(size == null)
size = new Dimension(0,0);
size.height = h;
this.setPreferredSize( size);
return this;
}
//////////// min size ///////////
public AfPanel minSize(int w, int h)
{
this.setMinimumSize(new Dimension(w, h));
return this;
}
public AfPanel minWidth(int w)
{
Dimension size = this.getMinimumSize();
if(size == null)
size = new Dimension(0,0);
size.width = w;
this.setMinimumSize( size);
return this;
}
public AfPanel minHeight(int h)
{
Dimension size = this.getMinimumSize();
if(size == null)
size = new Dimension(0,0);
size.height = h;
this.setMinimumSize( size);
return this;
}
}
package my;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Insets;
import java.awt.LayoutManager;
import java.awt.LayoutManager2;
import java.awt.Rectangle;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.swing.JPanel;
/* Swing入门篇 7.4节 ( 并在 7.5 节最终优化 )*/
public class AfRowLayout implements LayoutManager2
{
private List<Item> items = new ArrayList<>();
private int gap = 2;
private boolean usePerferredSize = false; // 竖立方向是否占满
public AfRowLayout()
{
}
public AfRowLayout(int gap)
{
this.gap = gap; // 控件之间的间距
}
public AfRowLayout(int gap, boolean usePerferredSize)
{
this.gap = gap;
this.usePerferredSize = usePerferredSize;
}
@Override
public void addLayoutComponent(String name, Component comp)
{
Item item = new Item();
item.comp = comp;
item.constraints = "auto";
items.add(item);
}
@Override
public void removeLayoutComponent(Component comp)
{
Iterator<Item> iter = items.iterator();
while(iter.hasNext())
{
Item item = iter.next();
if(item.comp == comp)
{
iter.remove();
}
}
}
@Override
public void addLayoutComponent(Component comp, Object constraints)
{
Item item = new Item();
item.comp = comp;
item.constraints = (String) constraints;
items.add(item);
}
@Override
public Dimension preferredLayoutSize(Container parent)
{
return new Dimension(30,30);
}
@Override
public Dimension minimumLayoutSize(Container parent)
{
return new Dimension(30,30);
}
@Override
public Dimension maximumLayoutSize(Container target)
{
return new Dimension(30,30);
}
@Override
public void layoutContainer(Container parent)
{
// 得到内矩形
Rectangle rect = new Rectangle(parent.getWidth(), parent.getHeight());
//Rectangle rect = parent.getBounds();
Insets insets = parent.getInsets();
rect.x += insets.left;
rect.y += insets.top;
rect.width -= (insets.left + insets.right);
rect.height -= (insets.top + insets.bottom);
// 第一轮:过滤到无效的 Item ( 有些控件是隐藏的 )
List<Item> validItems = new ArrayList<>();
for(Item it: items )
{
if(it.comp.isVisible())
validItems.add(it);
}
// 第二轮处理:百分比,像素,auto的,直接计算出结果; 权重的,在第三轮计算
int totalGapSize = gap * (validItems.size() - 1);// 间距大小
int validSize = rect.width - totalGapSize;
int totalSize = 0;
int totalWeight = 0;
for(Item it : validItems)
{
Dimension preferred = it.comp.getPreferredSize();
it.width = preferred.width;
it.height = usePerferredSize ? preferred.height : rect.height;
it.weight = 0;
// 计算宽度
String cstr = it.constraints;
if( cstr == null || cstr.length() == 0)
{
//System.out.println("(AfRowLayout) Warn: Must define constraints when added to container!");
}
else if( cstr.equals("auto"))
{
}
else if(cstr.endsWith("%")) // 按百分比
{
int num = Integer.valueOf(cstr.substring(0,cstr.length()-1));
it.width = validSize * num / 100;
}
else if(cstr.endsWith("w")) // 按权重
{
int num = Integer.valueOf(cstr.substring(0,cstr.length()-1));
it.width = 0;
it.weight = num;
}
else if(cstr.endsWith("px")) // 按权重
{
int num = Integer.valueOf(cstr.substring(0,cstr.length()-2));
it.width = num;
}
else // 按像素
{
int num = Integer.valueOf(cstr);
it.width = num;
}
totalSize += it.width;
totalWeight += it.weight;
//System.out.println("计算值:width=" + it.width + ",weight=" + it.weight);
}
// 第三轮: 剩余空间按权重分配
if( totalWeight > 0)
{
int remainSize = validSize - totalSize;
double unit = (double) remainSize / totalWeight;
for(Item it : validItems)
{
if(it.weight > 0)
{
it.width = (int)( unit * it.weight );
}
}
}
//System.out.println("总宽度: " + rect.width);
// 第四轮: 按宽度和高度布局
int x = 0;
for(Item it : validItems)
{
int y = (rect.height - it.height)/2;
if(x + it.width > rect.width)
it.width = rect.width - x;
if(it.width <= 0) break;
it.comp.setBounds(rect.x + x, rect.y + y, it.width, it.height);
//System.out.println("宽度: " + it.width);
x += it.width;
x += gap; // 间距
}
}
@Override
public float getLayoutAlignmentX(Container target)
{
return 0;
}
@Override
public float getLayoutAlignmentY(Container target)
{
return 0;
}
@Override
public void invalidateLayout(Container target)
{
}
///////////////////////
private static class Item
{
Component comp;
String constraints = "auto";
int width = 0;
int height = 0;
int weight = 0; // 权重
}
}
package my;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Insets;
import java.awt.LayoutManager;
import java.awt.LayoutManager2;
import java.awt.Rectangle;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.swing.JPanel;
/* Swing入门篇 7.5 节 */
/* ChangeLog
* 2019-01-23: 修改bug, int validSize = rect.height ..里的width变成height
*/
public class AfColumnLayout implements LayoutManager2
{
private List<Item> items = new ArrayList<>();
private int gap = 2;
private boolean usePerferredSize = false; // 竖立方向是否占满
public AfColumnLayout()
{
}
public AfColumnLayout(int gap)
{
this.gap = gap; // 控件之间的间距
}
public AfColumnLayout(int gap, boolean usePerferredSize)
{
this.gap = gap;
this.usePerferredSize = usePerferredSize;
}
@Override
public void addLayoutComponent(String name, Component comp)
{
Item item = new Item();
item.comp = comp;
item.constraints = "auto";
items.add(item);
}
@Override
public void removeLayoutComponent(Component comp)
{
Iterator<Item> iter = items.iterator();
while(iter.hasNext())
{
Item item = iter.next();
if(item.comp == comp)
{
iter.remove();
}
}
}
@Override
public void addLayoutComponent(Component comp, Object constraints)
{
Item item = new Item();
item.comp = comp;
item.constraints = (String) constraints;
items.add(item);
}
@Override
public Dimension preferredLayoutSize(Container parent)
{
return new Dimension(30,30);
}
@Override
public Dimension minimumLayoutSize(Container parent)
{
return new Dimension(30,30);
}
@Override
public Dimension maximumLayoutSize(Container target)
{
return new Dimension(30,30);
}
@Override
public void layoutContainer(Container parent)
{
// 得到内矩形
Rectangle rect = new Rectangle(parent.getWidth(), parent.getHeight());
//Rectangle rect = parent.getBounds();
Insets insets = parent.getInsets();
rect.x += insets.left;
rect.y += insets.top;
rect.width -= (insets.left + insets.right);
rect.height -= (insets.top + insets.bottom);
// 第一轮:过滤到无效的 Item ( 有些控件是隐藏的 )
List<Item> validItems = new ArrayList<>();
for(Item it: items )
{
if(it.comp.isVisible())
validItems.add(it);
}
// 第二轮处理:百分比,像素,auto的,直接计算出结果; 权重的,在第三轮计算
int totalGapSize = gap * (validItems.size() - 1);// 间距大小
int validSize = rect.height - totalGapSize;
int totalSize = 0;
int totalWeight = 0;
for(Item it : validItems)
{
Dimension preferred = it.comp.getPreferredSize();
it.width = usePerferredSize ? preferred.width : rect.width;
it.height = preferred.height;
it.weight = 0;
// 计算宽度
String cstr = it.constraints;
if( cstr == null || cstr.length() == 0)
{
//System.out.println("(AfColumnLayout) Warn: Must define constraints when added to container!");
}
else if( cstr.equals("auto"))
{
}
else if(cstr.endsWith("%")) // 按百分比
{
int num = Integer.valueOf(cstr.substring(0,cstr.length()-1));
it.height = validSize * num / 100;
}
else if(cstr.endsWith("w")) // 按权重
{
int num = Integer.valueOf(cstr.substring(0,cstr.length()-1));
it.height = 0;
it.weight = num;
}
else if(cstr.endsWith("px")) // 按权重
{
int num = Integer.valueOf(cstr.substring(0,cstr.length()-2));
it.height = num;
}
else // 按像素
{
int num = Integer.valueOf(cstr);
it.height = num;
}
totalSize += it.height;
totalWeight += it.weight;
//System.out.println("计算值:width=" + it.width + ",weight=" + it.weight);
}
// 第三轮: 剩余空间按权重分配
if( totalWeight > 0)
{
int remainSize = validSize - totalSize;
double unit = (double) remainSize / totalWeight;
for(Item it : validItems)
{
if(it.weight > 0)
{
it.height = (int)( unit * it.weight );
}
}
}
//System.out.println("总宽度: " + rect.width);
// 第四轮: 按宽度和高度布局
int y = 0;
for(Item it : validItems)
{
int x = 0; // 水平靠左
if(y + it.height > rect.height)
it.height = rect.height - y;
if(it.height <= 0) break;
it.comp.setBounds(rect.x + x, rect.y + y, it.width, it.height);
//System.out.println("宽度: " + it.width);
y += it.height;
y += gap; // 间距
}
}
@Override
public float getLayoutAlignmentX(Container target)
{
return 0;
}
@Override
public float getLayoutAlignmentY(Container target)
{
return 0;
}
@Override
public void invalidateLayout(Container target)
{
}
///////////////////////
private static class Item
{
Component comp;
String constraints = "auto";
int width = 0;
int height = 0;
int weight = 0; // 权重
}
}
package my;
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Arc2D;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Path2D;
public class AfToggleButton extends JPanel
{
private boolean selected = true; // 开关, ON/OFF
// 绘制参数设定
public Color bgColor = new Color(0xFFFFFF); // 主背景
public Color lineColor = new Color(0xDEDEDE); // 描边
public Color darkColor = new Color(0xE1E1E1); // 灰色填充
public Color lightColor = new Color(0x33B4FF); // 高亮填充
public int padding = 2; // 轮廓线与内部圆的距离
public AfToggleButton()
{
this.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e)
{
onMouseClicked();
}
});
}
private void onMouseClicked()
{
toggle(); // 点击时切换状态
// 自定义接口
if(stateListener != null)
{
stateListener.stateChanged(this);
}
}
public interface StateListener // 内部类/接口
{
void stateChanged(Object source);
}
private StateListener stateListener; // 属性
public void setStateListener(StateListener listener)
{
this.stateListener = listener;
}
// 获取数据
public boolean isSelected()
{
return this.selected;
}
// 设置数据
public void setSelected(boolean selected)
{
this.selected = selected;
repaint();
}
// 切换开关
public void toggle()
{
this.selected = ! this.selected;
repaint();
}
@Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
int width = getWidth();
int height = getHeight();
Graphics2D g2d = (Graphics2D) g;
// 平滑绘制 ( 反锯齿 )
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// 算出一个2:1的最大矩形
// Rectangle r = new Rectangle(0,0, 100, 50);
int w = width;
int h = width / 2;
if( h > height)
{
h = height;
w = height * 2;
}
Rectangle r = new Rectangle((width-w)/2, (height-h)/2, w, h);
// 里面两个并排的圆,外层轮郭为拼接曲线(不规则形状的绘制:参考4.3讲)
// 左半区
Rectangle r1 = new Rectangle(r.x,r.y, r.width/2, r.height);
// 右半区
Rectangle r2 = new Rectangle(r.x+r.width/2,r.y, r.width/2, r.height);
// 绘制外部轮廓线
Shape arc1 = new Arc2D.Double (r1, 90, 180, Arc2D.OPEN);
Shape arc2 = new Arc2D.Double (r2, 270, 180, Arc2D.OPEN);
Path2D outline = new Path2D.Double(); // 外轮廓,使用拼装路径
outline.append(arc1.getPathIterator(null), false); // 左半圆弧
outline.append(arc2.getPathIterator(null), true); // 右半圆弧
outline.closePath();
g2d.setPaint(lineColor);
g2d.draw(outline);
g2d.setPaint(bgColor);
g2d.fill(outline);
// 内部画两个圆
if(selected)
{
drawCircleInside(g2d, r2, padding, lineColor, lightColor);
}
else
{
drawCircleInside(g2d, r1, padding, lineColor, darkColor);
}
}
// 画内部的小圆
private void drawCircleInside(Graphics2D g2d, Rectangle rect,
int deflate, Paint lineColor, Paint fillColor)
{
Rectangle r = new Rectangle(rect); // 做一个备份,不会修改传入的rect
r.x += deflate;
r.y += deflate;
r.width -= (deflate * 2);
r.height -= (deflate* 2);
Shape shape = new Ellipse2D.Double(r.x, r.y, r.width,r.height);
// 描边
g2d.setPaint(lineColor);
g2d.draw(shape);
// 填充
g2d.setPaint(fillColor);
g2d.fill( shape );
}
}
自定义事件



package my;
import javax.swing.*;
public class Swing2
{
private static void createGUI()
{
// JFrame指一个窗口,构造方法的参数为窗口标题
// 语法:因为MyFrame是JFrame的子类,所以可以这么写
JFrame frame = new MyFrame("Swing Demo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// 设置窗口的其他参数,如窗口大小
frame.setSize(500, 300);
// 显示窗口
frame.setVisible(true);
}
public static void main(String[] args)
{
// 此段代码间接地调用了 createGUI(),具体原理在 Swing高级篇 里讲解
// 初学者先照抄此代码框架即可
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run()
{
createGUI();
}
});
}
}
package my;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class MyFrame extends JFrame
{
JTextField textField = new JTextField(20);
JButton okButton = new JButton("OK");
ToggleButton toggleButton = new ToggleButton();
public MyFrame(String title)
{
super(title);
// Content Pane
JPanel root = new JPanel();
this.setContentPane(root);
root.setLayout(new FlowLayout());
root.add(textField);
root.add(okButton);
// 第一种写法: MouseEvent
// okButton.addMouseListener(new MouseAdapter() {
// @Override
// public void mouseClicked(MouseEvent e)
// {
// textField.setText("2019-02-14");
// }
// });
// 第二种写法:ActionEvent
okButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e)
{
textField.setText("2019-02-14 5555");
}
});
// 这也是ActionEvent
textField.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e)
{
System.out.println("在文本框里输入了回车!");
}
});
//////////////////////////////
// 自定义事件
root.add(toggleButton);
toggleButton.setPreferredSize(new Dimension(60,30));
toggleButton.setStateListener(new ToggleButton.StateListener() {
@Override
public void stateChanged(StateEvent e)
{
System.out.println("当前选中状态为:" + e.state);
}
});
}
}
package my;
import java.awt.AWTEvent;
/* 模仿ActionEvent,自写一个事件对象
* 仅作为演示用途
*/
public class StateEvent extends AWTEvent
{
public boolean state;
public StateEvent(Object source, int id)
{
super(source, id);
}
}
package my;
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Arc2D;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Path2D;
public class ToggleButton extends JPanel
{
private boolean selected = true; // 开关, ON/OFF
// 绘制参数设定
public Color bgColor = new Color(0xFFFFFF); // 主背景
public Color lineColor = new Color(0xDEDEDE); // 描边
public Color darkColor = new Color(0xE1E1E1); // 灰色填充
public Color lightColor = new Color(0x33B4FF); // 高亮填充
public int padding = 2; // 轮廓线与内部圆的距离
public ToggleButton()
{
this.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e)
{
onMouseClicked();
}
});
}
private void onMouseClicked()
{
toggle(); // 点击时切换状态
// 自定义接口
if(stateListener != null)
{
// 使用自定义事件
StateEvent event = new StateEvent(this, 0);
event.state = this.selected;
stateListener.stateChanged(event);
}
}
public interface StateListener // 内部类/接口
{
void stateChanged(StateEvent e);
}
private StateListener stateListener; // 属性
public void setStateListener(StateListener listener)
{
this.stateListener = listener;
}
// 获取数据
public boolean isSelected()
{
return this.selected;
}
// 设置数据
public void setSelected(boolean selected)
{
this.selected = selected;
repaint();
}
// 切换开关
public void toggle()
{
this.selected = ! this.selected;
repaint();
}
@Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
int width = getWidth();
int height = getHeight();
Graphics2D g2d = (Graphics2D) g;
// 平滑绘制 ( 反锯齿 )
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// 算出一个2:1的最大矩形
// Rectangle r = new Rectangle(0,0, 100, 50);
int w = width;
int h = width / 2;
if( h > height)
{
h = height;
w = height * 2;
}
Rectangle r = new Rectangle((width-w)/2, (height-h)/2, w, h);
// 里面两个并排的圆,外层轮郭为拼接曲线(不规则形状的绘制:参考4.3讲)
// 左半区
Rectangle r1 = new Rectangle(r.x,r.y, r.width/2, r.height);
// 右半区
Rectangle r2 = new Rectangle(r.x+r.width/2,r.y, r.width/2, r.height);
// 绘制外部轮廓线
Shape arc1 = new Arc2D.Double (r1, 90, 180, Arc2D.OPEN);
Shape arc2 = new Arc2D.Double (r2, 270, 180, Arc2D.OPEN);
Path2D outline = new Path2D.Double(); // 外轮廓,使用拼装路径
outline.append(arc1.getPathIterator(null), false); // 左半圆弧
outline.append(arc2.getPathIterator(null), true); // 右半圆弧
outline.closePath();
g2d.setPaint(lineColor);
g2d.draw(outline);
g2d.setPaint(bgColor);
g2d.fill(outline);
// 内部画两个圆
if(selected)
{
drawCircleInside(g2d, r2, padding, lineColor, lightColor);
}
else
{
drawCircleInside(g2d, r1, padding, lineColor, darkColor);
}
}
// 画内部的小圆
private void drawCircleInside(Graphics2D g2d, Rectangle rect,
int deflate, Paint lineColor, Paint fillColor)
{
Rectangle r = new Rectangle(rect); // 做一个备份,不会修改传入的rect
r.x += deflate;
r.y += deflate;
r.width -= (deflate * 2);
r.height -= (deflate* 2);
Shape shape = new Ellipse2D.Double(r.x, r.y, r.width,r.height);
// 描边
g2d.setPaint(lineColor);
g2d.draw(shape);
// 填充
g2d.setPaint(fillColor);
g2d.fill( shape );
}
}
2484



被折叠的 条评论
为什么被折叠?



