Kotlin のバックアップ差分(No.20)
- バックアップ一覧
- 現在との差分 を表示
- 現在との差分 - Visual を表示
- ソース を表示
- バックアップ を表示
- Kotlin へ行く。
- 1 (2017-05-31 (水) 18:35:21)
- 2 (2017-05-31 (水) 19:53:14)
- 3 (2017-05-31 (水) 20:59:47)
- 4 (2017-05-31 (水) 23:55:03)
- 5 (2017-06-01 (木) 10:59:15)
- 6 (2017-06-01 (木) 12:23:00)
- 7 (2017-06-01 (木) 16:03:15)
- 8 (2017-06-01 (木) 17:20:46)
- 9 (2017-06-04 (日) 13:23:23)
- 10 (2017-06-05 (月) 18:47:29)
- 11 (2017-06-08 (木) 14:12:31)
- 12 (2017-06-08 (木) 19:03:39)
- 13 (2017-06-13 (火) 14:53:44)
- 14 (2017-06-26 (月) 15:09:29)
- 15 (2017-09-04 (月) 15:51:42)
- 16 (2017-09-04 (月) 17:12:49)
- 17 (2017-10-03 (火) 14:50:20)
- 18 (2017-11-07 (火) 16:44:56)
- 19 (2017-11-08 (水) 16:37:18)
- 20 (2017-11-13 (月) 18:11:40)
- 21 (2017-11-20 (月) 18:51:07)
- 22 (2017-12-12 (火) 19:07:44)
- 23 (2017-12-12 (火) 20:36:03)
- 24 (2017-12-14 (木) 15:57:09)
- 25 (2017-12-15 (金) 20:15:22)
- 26 (2017-12-15 (金) 21:32:06)
- 27 (2017-12-18 (月) 16:30:34)
- 28 (2017-12-23 (土) 15:30:50)
- 29 (2017-12-25 (月) 13:58:48)
- 30 (2017-12-25 (月) 17:40:04)
- 31 (2017-12-25 (月) 20:12:05)
- 32 (2017-12-28 (木) 22:22:38)
- 33 (2018-01-09 (火) 18:02:52)
- 34 (2018-03-02 (金) 18:20:39)
- 35 (2018-03-05 (月) 17:52:19)
- 36 (2018-03-26 (月) 21:25:15)
- 37 (2018-08-09 (木) 18:17:44)
- 38 (2018-10-30 (火) 17:00:35)
- 39 (2018-10-31 (水) 15:46:41)
- 40 (2018-10-31 (水) 17:07:25)
- 41 (2018-11-05 (月) 18:05:10)
- 42 (2018-11-06 (火) 18:51:01)
- 43 (2018-11-07 (水) 13:55:45)
- 44 (2018-11-07 (水) 19:30:57)
- 45 (2018-11-07 (水) 21:39:08)
- 46 (2018-11-08 (木) 13:58:44)
- 47 (2018-11-08 (木) 14:59:11)
- 48 (2018-11-08 (木) 19:22:44)
- 49 (2018-11-09 (金) 17:05:26)
- 50 (2018-11-13 (火) 14:49:29)
- 51 (2018-11-15 (木) 15:36:23)
- 52 (2018-11-16 (金) 18:22:36)
- 53 (2018-12-07 (金) 19:39:18)
- 54 (2018-12-09 (日) 21:57:42)
- 55 (2018-12-10 (月) 16:52:25)
- 56 (2018-12-10 (月) 18:05:19)
- 57 (2018-12-17 (月) 17:23:20)
- 58 (2018-12-20 (木) 13:56:07)
- 59 (2019-01-07 (月) 16:14:59)
- 60 (2019-01-17 (木) 17:40:32)
- 61 (2019-01-18 (金) 19:53:07)
- 62 (2019-02-01 (金) 21:34:36)
- 63 (2019-02-02 (土) 23:27:16)
- 64 (2019-02-12 (火) 16:09:08)
- 65 (2019-02-26 (火) 19:20:13)
- 66 (2019-02-26 (火) 21:11:09)
- 67 (2019-02-27 (水) 15:48:09)
- 68 (2019-02-27 (水) 19:31:05)
- 69 (2019-02-27 (水) 21:18:44)
- 70 (2019-02-28 (木) 22:09:03)
- 71 (2019-03-11 (月) 15:32:26)
- 72 (2019-03-14 (木) 17:38:07)
- 73 (2019-03-25 (月) 19:57:36)
- 74 (2019-03-27 (水) 20:28:44)
- 75 (2019-04-02 (火) 19:32:48)
- 76 (2019-04-04 (木) 20:13:39)
- 77 (2019-04-05 (金) 14:32:20)
- 78 (2019-04-05 (金) 18:05:49)
- 79 (2019-04-09 (火) 16:53:20)
- 80 (2019-04-17 (水) 16:40:32)
- 81 (2019-04-23 (火) 18:47:09)
- 82 (2019-04-23 (火) 20:15:22)
- 83 (2019-04-24 (水) 18:46:55)
- 84 (2019-05-02 (木) 17:57:41)
- 85 (2019-05-20 (月) 19:24:10)
- 86 (2019-05-22 (水) 17:19:10)
- 87 (2019-05-22 (水) 19:35:38)
- 88 (2019-05-23 (木) 17:54:37)
- 89 (2019-05-27 (月) 18:26:22)
- 90 (2019-05-28 (火) 15:22:38)
- 91 (2019-06-10 (月) 19:46:56)
- 92 (2019-06-11 (火) 19:41:28)
- 93 (2019-06-14 (金) 16:18:02)
- 94 (2019-06-14 (金) 20:35:44)
- 95 (2019-06-24 (月) 14:43:23)
- 96 (2019-06-24 (月) 18:26:16)
- 97 (2019-07-02 (火) 16:23:12)
- 98 (2019-07-29 (月) 16:16:17)
- 99 (2019-08-08 (木) 22:52:05)
- 100 (2019-08-13 (火) 17:47:36)
- 101 (2019-08-15 (木) 16:09:14)
- 102 (2019-08-26 (月) 16:39:57)
- 103 (2019-08-26 (月) 18:12:51)
- 104 (2019-08-26 (月) 20:23:07)
- 105 (2019-08-27 (火) 14:22:16)
- 106 (2019-08-28 (水) 19:38:13)
- 107 (2019-08-29 (木) 19:11:00)
- 108 (2019-09-12 (木) 16:08:13)
- 109 (2019-11-12 (火) 19:55:32)
- 110 (2019-11-13 (水) 13:29:33)
- 111 (2019-12-05 (木) 22:21:26)
- 112 (2019-12-16 (月) 17:39:21)
- 113 (2019-12-17 (火) 14:36:42)
- 114 (2020-01-06 (月) 18:30:19)
- 115 (2020-01-16 (木) 20:26:27)
- 116 (2020-03-19 (木) 20:05:04)
- 117 (2020-08-24 (月) 14:44:31)
- 118 (2020-08-26 (水) 12:18:57)
- 119 (2020-11-12 (木) 10:10:09)
- 120 (2021-09-28 (火) 12:56:54)
- 121 (2021-12-13 (月) 15:11:40)
- 122 (2022-03-29 (火) 15:20:50)
- 123 (2023-07-10 (月) 11:14:56)
- 124 (2024-02-21 (水) 02:08:19)
- 追加された行はこの色です。
- 削除された行はこの色です。
--- keywords: [Kotlin, Swing, Java] description: KotlinでSwingコンポーネントを使用するサンプルと、Javaからの変換に関するメモなど author: aterai pubdate: 2017-05-31 --- #contents * 概要 [#summary] `Kotlin`で`Swing`コンポーネントを使用するサンプルと、`Java`からの変換に関するメモです。 * 実行環境 [#s42e991c] - [http://sdkman.io/ SDKMAN! the Software Development Kit Manager]で`kotlin`をインストール可能 $ curl -s "https://get.sdkman.io" | bash $ sdk install kotlin $ kotlinc -version info: kotlinc-jvm 1.1.4-3 (JRE 1.8.0_141-b15) $ kotlinc hello.kt -include-runtime -d hello.jar && "$JAVA_HOME/bin/java" -jar hello.jar * Swing + Kotlin サンプル [#swing] ** JTable [#JTable] - `Class<?>`は%%`Class<Any>`か`Class<Object>`?%%、`Class<*>`か`Class<out Any>`に変換した方が良いかもしれない -- `IntelliJ`の自動変換では`Class<*>` - `Object#getClass()`は`o.javaClass` -- [https://kotlinlang.org/docs/reference/java-interop.html#getclass getClass() - Calling Java from Kotlin - Kotlin Programming Language] -- `Integer::class.java`は`Class<Integer>`になるので、以下のサンプルで使用すると、`error: type inference failed. Expected type mismatch: inferred type is Class<Integer> but Class<Any> was expected`とエラーになる -- [https://kotlinlang.org/docs/reference/generics.html#star-projections Star-projections - Generics - Kotlin Programming Language] - オーバーライドの方法、`@Override`は`override` - 配列、二次元配列 -- [https://kotlinlang.org/docs/reference/java-interop.html#java-arrays Java Arrays - Calling Java from Kotlin - Kotlin Programming Language] -- `IntelliJ`の自動変換では`arrayOf(arrayOf<Any>("aaa", 12, true), ...` - `apply` - `switch`は`when` #twocolumn `Java` #code{{ import java.awt.*; import javax.swing.*; import javax.swing.table.*; public class JTableExample { public JComponent makeUI() { String[] cn = {"String", "Integer", "Boolean"}; Object[][] data = { {"aaa", 12, true}, {"bbb", 5, false}, {"CCC", 92, true}, {"DDD", 0, false} }; TableModel m = new DefaultTableModel(data, cn) { @Override public Class<?> getColumnClass(int col) { return getValueAt(0, col).getClass(); } }; JTable table = new JTable(m); table.setAutoCreateRowSorter(true); JPanel p = new JPanel(new BorderLayout()); p.add(new JScrollPane(table)); return p; } public static void main(String... args) { EventQueue.invokeLater(() -> { JFrame f = new JFrame(); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.add(new JTableExample().makeUI()); f.setSize(320, 240); f.setLocationRelativeTo(null); f.setVisible(true); }); } } }} #twocolumn `Kotlin` #code{{ import java.awt.* import javax.swing.* import javax.swing.table.* fun makeUI(): JComponent { val cn = arrayOf("String", "Integer", "Boolean") val data = arrayOf( arrayOf("aaa", 12, true), arrayOf("bbb", 5, false), arrayOf("CCC", 92, true), arrayOf("DDD", 0, false)) val m = object: DefaultTableModel(data, cn) { override fun getColumnClass(col: Int): Class<Any> { return getValueAt(0, col).javaClass } } // val m = object: DefaultTableModel(data, cn) { // override fun getColumnClass(col: Int): Class<*> { // return when (col) { // 0 -> String::class.java // 1 -> Integer::class.java // 2 -> Boolean::class.java // else -> Object::class.java // } // } // } val table = JTable(m).apply { autoCreateRowSorter = true } return JPanel(BorderLayout(5, 5)).apply { add(JScrollPane(table)) } } fun main(args: Array<String>) { EventQueue.invokeLater { JFrame("kotlin swing").apply { defaultCloseOperation = JFrame.EXIT_ON_CLOSE add(makeUI()) size = Dimension(320, 240) setLocationRelativeTo(null) setVisible(true) } } } }} #twocolumn ** JTree [#JTree] #twocolumn `Java` #code{{ import java.awt.*; import java.util.*; import javax.swing.*; public class JTreeExample { public JComponent makeUI() { JTree tree = new JTree(); JButton b1 = new JButton("expand"); b1.addActionListener(e -> expandAll(tree)); JButton b2 = new JButton("collapse"); b2.addActionListener(e -> collapseAll(tree)); JPanel pp = new JPanel(new GridLayout(1, 0, 5, 5)); Arrays.asList(b1, b2).forEach(pp::add); JPanel p = new JPanel(new BorderLayout(5, 5)); p.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); p.add(new JScrollPane(tree)); p.add(pp, BorderLayout.SOUTH); return p; } protected static void expandAll(JTree tree) { int row = 0; while (row < tree.getRowCount()) { tree.expandRow(row); row++; } } protected static void collapseAll(JTree tree) { int row = tree.getRowCount() - 1; while (row >= 0) { tree.collapseRow(row); row--; } } public static void main(String[] args) { EventQueue.invokeLater(() -> { JFrame f = new JFrame(); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.add(new JTreeExample().makeUI()); f.setSize(320, 240); f.setLocationRelativeTo(null); f.setVisible(true); }); } } }} #twocolumn `Kotlin` #code{{ import java.awt.* import javax.swing.* fun makeUI(): JComponent { val tree = JTree() val p = JPanel(GridLayout(1, 0, 5, 5)).apply { add(JButton("expand").apply { addActionListener { expandAll(tree) } }) add(JButton("collapse").apply { addActionListener { collapseAll(tree) } }) } return JPanel(BorderLayout(5, 5)).apply { setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)) add(JScrollPane(tree)) add(p, BorderLayout.SOUTH) } } fun expandAll(tree: JTree) { var row = 0 while (row < tree.getRowCount()) { tree.expandRow(row) row++ } } fun collapseAll(tree : JTree) { var row = tree.getRowCount() - 1 while (row >= 0) { tree.collapseRow(row) row-- } } fun main(args: Array<String>) { EventQueue.invokeLater { JFrame("kotlin swing").apply { defaultCloseOperation = JFrame.EXIT_ON_CLOSE add(makeUI()) size = Dimension(320, 240) setLocationRelativeTo(null) setVisible(true) } } } }} #twocolumn ** JCheckBox [#JCheckBox] - `Smart Cast`、キャストで`if`のネストが深くなるのを避けたい -- イベント発生元が`AbstractButton`なのは自明なので`as`を使用する --- `val b = e.getSource() as AbstractButton` -- 以下のような場合は、`apply`でも回避可能 #twocolumn `Java` #code{{ import java.awt.*; import javax.swing.*; public class JCheckBoxExample { public JComponent makeUI() { JCheckBox cb = new JCheckBox("Always On Top", true); cb.addActionListener(e -> { AbstractButton b = (AbstractButton) e.getSource(); Container c = b.getTopLevelAncestor(); if (c instanceof Window) { ((Window) c).setAlwaysOnTop(b.isSelected()); } }); JPanel p = new JPanel(new BorderLayout()); p.add(cb, BorderLayout.NORTH); return p; } public static void main(String[] args) { EventQueue.invokeLater(() -> { JFrame f = new JFrame(); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.add(new JCheckBoxExample().makeUI()); f.setSize(320, 240); f.setLocationRelativeTo(null); f.setVisible(true); }); } } }} #twocolumn `Kotlin` #code{{ import java.awt.* import javax.swing.* fun makeUI(): JComponent { val cb = JCheckBox("Always On Top", true).apply { // addActionListener { e -> // val c = e.getSource() // if (c is AbstractButton) { // val w = c.getTopLevelAncestor() // if (w is Window) { // w.setAlwaysOnTop(c.isSelected()) // } // } addActionListener { e -> val b = e.getSource() as AbstractButton val w = b.getTopLevelAncestor() if (w is Window) { w.setAlwaysOnTop(b.isSelected()) } } // addActionListener { // val w = getTopLevelAncestor() // if (w is Window) { // w.setAlwaysOnTop(isSelected()) // } // } } return JPanel(BorderLayout()).apply { add(cb, BorderLayout.NORTH) } } fun main(args: Array<String>) { EventQueue.invokeLater { JFrame("kotlin swing").apply { defaultCloseOperation = JFrame.EXIT_ON_CLOSE add(makeUI()) size = Dimension(320, 240) setLocationRelativeTo(null) setVisible(true) } } } }} #twocolumn ** インターフェイス、匿名内部クラス、throws [#tb58a862] - `addTreeWillExpandListener(object: TreeWillExpandListener() { ...`と記述すると`error: this class does not have a constructor`とエラーになる - インターフェイスから匿名内部クラスのインスタンスを生成する場合は`()`は不要で、`addTreeWillExpandListener(object: TreeWillExpandListener { ...`と記述する -- [https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SAM Conversions - Calling Java from Kotlin - Kotlin Programming Language] #twocolumn `Java` #code{{ import java.awt.*; import javax.swing.*; import javax.swing.event.*; import javax.swing.plaf.IconUIResource; import javax.swing.tree.*; public class ThrowsExample { public JComponent makeUI() { JPanel p = new JPanel(new GridLayout(1, 0, 5, 5)); p.add(new JScrollPane(new JTree())); p.add(new JScrollPane(makeTree())); p.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); return p; } private static JTree makeTree() { Icon icon = new EmptyIcon(); UIManager.put("Tree.expandedIcon", new IconUIResource(icon)); UIManager.put("Tree.collapsedIcon", new IconUIResource(icon)); JTree tree = new JTree(); tree.setEditable(true); tree.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); int row = 0; while (row < tree.getRowCount()) { tree.expandRow(row++); } tree.addTreeWillExpandListener(new TreeWillExpandListener() { @Override public void treeWillExpand(TreeExpansionEvent e) throws ExpandVetoException { //throw new ExpandVetoException(e, "Tree expansion cancelled"); } @Override public void treeWillCollapse(TreeExpansionEvent e) throws ExpandVetoException { throw new ExpandVetoException(e, "Tree collapse cancelled"); } }); return tree; } public static void main(String[] args) { EventQueue.invokeLater(() -> { JFrame f = new JFrame(); f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); f.getContentPane().add(new ThrowsExample().makeUI()); f.setSize(320, 240); f.setLocationRelativeTo(null); f.setVisible(true); }); } } class EmptyIcon implements Icon { @Override public void paintIcon(Component c, Graphics g, int x, int y) { /* Empty icon */ } @Override public int getIconWidth() { return 0; } @Override public int getIconHeight() { return 0; } } }} #twocolumn `Kotlin` #code{{ import java.awt.* import javax.swing.* import javax.swing.event.* import javax.swing.plaf.IconUIResource import javax.swing.tree.* fun makeUI(): JComponent { val emptyIcon = EmptyIcon() UIManager.put("Tree.expandedIcon", IconUIResource(emptyIcon)) UIManager.put("Tree.collapsedIcon", IconUIResource(emptyIcon)) val tree = JTree().apply { setEditable(true) border = BorderFactory.createEmptyBorder(4, 4, 4, 4) addTreeWillExpandListener(object: TreeWillExpandListener { @Throws(ExpandVetoException::class) override fun treeWillExpand(e: TreeExpansionEvent) { //throw ExpandVetoException(e, "Tree expansion cancelled") } @Throws(ExpandVetoException::class) override fun treeWillCollapse(e: TreeExpansionEvent) { throw ExpandVetoException(e, "Tree collapse cancelled") } }) } var row = 0; while (row < tree.getRowCount()) { tree.expandRow(row++); } return JPanel(GridLayout(1, 0)).apply { setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)) add(JScrollPane(JTree())) add(JScrollPane(tree)) } } class EmptyIcon: Icon { override fun paintIcon(c: Component, g: Graphics , x: Int, y: Int) { /* Empty icon */ } override fun getIconWidth(): Int = 0 override fun getIconHeight(): Int = 0 } fun main(args: Array<String>) { EventQueue.invokeLater { JFrame("kotlin swing").apply { defaultCloseOperation = WindowConstants.EXIT_ON_CLOSE add(makeUI()) size = Dimension(320, 240) setLocationRelativeTo(null) setVisible(true) } } } }} #twocolumn ** Optional [#c0e18689] - `Optional#orElse(...)`は、`let {...} ?: ...`に変換 -- `Integer stepSize = Optional.ofNullable(stepSizeMap.get(calendarField)).orElse(1);` -- `val stepSize = stepSizeMap.get(calendarField).let { it } ?: 1` - 例えば`SpinnerDateModel#getNextValue(...)`メソッドを`override fun getNextValue(): Object = Calendar.getInstance().apply { ...`のように変換すると、`error: type mismatch: inferred type is Date! but Object was expected`とエラーになる - `override fun getNextValue(): Any = Calendar.getInstance().apply { ...`と`Any`を使用するようオーバーライドする必要がある - `Date!`は、`Date`もしくは`Date?`の意味 -- [http://kotlinlang.org/docs/reference/java-interop.html#null-safety-and-platform-types Calling Java from Kotlin - Kotlin Programming Language] -- `T! means "T or T?"` #twocolumn `Java` #code{{ import java.awt.*; import java.text.*; import java.util.*; import javax.swing.*; import javax.swing.text.*; public class OptionalExample { private JComponent makeUI() { SimpleDateFormat format = new SimpleDateFormat( "mm:ss, SSS", Locale.getDefault()); DefaultFormatterFactory factory = new DefaultFormatterFactory( new DateFormatter(format)); Calendar calendar = Calendar.getInstance(); calendar.set(Calendar.HOUR_OF_DAY, 0); calendar.clear(Calendar.MINUTE); calendar.clear(Calendar.SECOND); calendar.clear(Calendar.MILLISECOND); Date d = calendar.getTime(); JSpinner sp1 = new JSpinner(new SpinnerDateModel( d, null, null, Calendar.SECOND)); JSpinner.DefaultEditor ed1 = (JSpinner.DefaultEditor) sp1.getEditor(); ed1.getTextField().setFormatterFactory(factory); HashMap<Integer, Integer> stepSizeMap = new HashMap<>(); stepSizeMap.put(Calendar.HOUR_OF_DAY, 1); stepSizeMap.put(Calendar.MINUTE, 1); stepSizeMap.put(Calendar.SECOND, 30); stepSizeMap.put(Calendar.MILLISECOND, 500); JSpinner sp2 = new JSpinner(new SpinnerDateModel( d, null, null, Calendar.SECOND) { @Override public Object getPreviousValue() { Calendar cal = Calendar.getInstance(); cal.setTime(getDate()); Integer calendarField = getCalendarField(); Integer stepSize = Optional.ofNullable( stepSizeMap.get(calendarField)).orElse(1); cal.add(calendarField, -stepSize); return cal.getTime(); } @Override public Object getNextValue() { Calendar cal = Calendar.getInstance(); cal.setTime(getDate()); Integer calendarField = getCalendarField(); Integer stepSize = Optional.ofNullable( stepSizeMap.get(calendarField)).orElse(1); cal.add(calendarField, stepSize); return cal.getTime(); } }); JSpinner.DefaultEditor ed2 = (JSpinner.DefaultEditor) sp2.getEditor(); ed2.getTextField().setFormatterFactory(factory); JPanel p = new JPanel(); p.add(sp1); p.add(sp2); return p; } public static void main(String... args) { EventQueue.invokeLater(() -> { JFrame f = new JFrame(); f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); f.getContentPane().add(new OptionalExample().makeUI()); f.setSize(320, 240); f.setLocationRelativeTo(null); f.setVisible(true); }); } } }} #twocolumn `Kotlin` #code{{ import java.awt.* import java.text.* import java.util.* import javax.swing.* import javax.swing.text.* fun makeUI(): JComponent { val format = SimpleDateFormat("mm:ss, SSS", Locale.getDefault()) val factory = DefaultFormatterFactory(DateFormatter(format)) val calendar = Calendar.getInstance().apply { set(Calendar.HOUR_OF_DAY, 0) clear(Calendar.MINUTE) clear(Calendar.SECOND) clear(Calendar.MILLISECOND) } val d = calendar.getTime() val sp1 = JSpinner(SpinnerDateModel(d, null, null, Calendar.SECOND)) val ed1 = sp1.getEditor() if (ed1 is JSpinner.DefaultEditor) { ed1.getTextField().setFormatterFactory(factory) } val stepSizeMap: HashMap<Int, Int> = hashMapOf( Calendar.HOUR_OF_DAY to 1, Calendar.MINUTE to 1, Calendar.SECOND to 30, Calendar.MILLISECOND to 500) val sp2 = JSpinner(object: SpinnerDateModel(d, null, null, Calendar.SECOND) { override fun getPreviousValue(): Any = Calendar.getInstance().apply { time = getDate() val stepSize = stepSizeMap.get(calendarField).let { it } ?: 1 add(calendarField, -stepSize) }.getTime() override fun getNextValue(): Any = Calendar.getInstance().apply { setTime(getDate()) val stepSize = stepSizeMap.get(calendarField).let { it } ?: 1 add(calendarField, stepSize) }.getTime() }) val ed2 = sp2.getEditor() if (ed2 is JSpinner.DefaultEditor) { ed2.getTextField().setFormatterFactory(factory) } return JPanel().apply { add(sp1) add(sp2) } } fun main(args: Array<String>) { EventQueue.invokeLater { JFrame("kotlin swing").apply { defaultCloseOperation = WindowConstants.EXIT_ON_CLOSE add(makeUI()) size = Dimension(320, 240) setLocationRelativeTo(null) setVisible(true) } } } }} #twocolumn ** 演算子の優先順位[#precedence] - 論理演算子: 左シフト -- `Java`: `buttons[0].setSelected((i & (1 << 2) ) != 0);` -- `Kotlin`: `buttons[0].setSelected(i and (1 shl 2) != 0)` --- `IntelliJ`の自動変換では括弧が減る(`and`の優先順位が異なる?) --- `IntelliJ`の自動変換では時々`buttons[0].setSelected(i and (1 shl 2 != 0) )`に誤変換される? - `bitwise AND`(`and`の優先順位) -- [https://discuss.kotlinlang.org/t/bitwise-operators-are-not-in-the-precedence-table/2075 Bitwise operators are not in the precedence table - Support - Kotlin Discussions] -- [https://kotlinlang.org/docs/reference/grammar.html#precedence Kotlinの演算子優先順位表] --- [https://kotlinlang.org/docs/reference/basic-types.html#operations Basic Types: Numbers, Strings, Arrays - Kotlin Programming Language] --- `Kotlin`の`bitwise and`は`infix function`のため、`Equality`の`!=`より優先順位が高い -- `Java`の演算子優先順位表: [https://docs.oracle.com/javase/tutorial/java/nutsandbolts/operators.html Operators (The Java™ Tutorials > Learning the Java Language > Language Basics)] --- `Java`は逆で、`equality`(`==`, `!=`)のほうが`bitwise AND`(`&`)より優先順位が高い ---- - `extends`, `implements` -- `IntelliJ`の自動変換では勝手に`open`は付けないようなので、以下のようなエラーが出る場合は、`internal class CheckBoxesPanel : JPanel() {`を`open class CheckBoxesPanel : JPanel() {`に修正する #code{{ MainPanel.kt:81:37: error: this type is final, so it cannot be inherited from internal class CheckBoxesRenderer : CheckBoxesPanel(), TableCellRenderer { }} - `IntelliJ`の自動変換では`static`変数がうまく変換できない? -- 手動で`enum class`にして回避 #code{{ companion object { protected val TITLES = arrayOf("r", "w", "x") } //... MainPanel.kt:45:5: error: property must be initialized or be abstract var buttons: Array<JCheckBox> ^ MainPanel.kt:55:19: error: type mismatch: inferred type is Array<JCheckBox?> but Array<JCheckBox> was expected buttons = arrayOfNulls<JCheckBox>(TITLES.size) }} #twocolumn `Java` #code{{ import java.awt.*; import java.awt.event.*; import java.util.*; import javax.swing.*; import javax.swing.event.*; import javax.swing.plaf.*; import javax.swing.table.*; public final class MainPanel { public JComponent makeUI() { String[] columnNames = {"user", "rwx"}; Object[][] data = { {"owner", 7}, {"group", 6}, {"other", 5} }; TableModel model = new DefaultTableModel(data, columnNames) { @Override public Class<?> getColumnClass(int column) { return getValueAt(0, column).getClass(); } }; JTable table = new JTable(model) { @Override public void updateUI() { super.updateUI(); getColumnModel().getColumn(1).setCellRenderer(new CheckBoxesRenderer()); getColumnModel().getColumn(1).setCellEditor(new CheckBoxesEditor()); } }; table.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE); JPanel p = new JPanel(new BorderLayout()); p.add(new JScrollPane(table)); return p; } public static void main(String[] args) { EventQueue.invokeLater(() -> { JFrame frame = new JFrame(); frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); frame.getContentPane().add(new MainPanel().makeUI()); frame.setSize(320, 240); frame.setLocationRelativeTo(null); frame.setVisible(true); }); } } class CheckBoxesPanel extends JPanel { protected static final String[] TITLES = {"r", "w", "x"}; public JCheckBox[] buttons; @Override public void updateUI() { super.updateUI(); setOpaque(false); setBackground(new Color(0x0, true)); setLayout(new BoxLayout(this, BoxLayout.X_AXIS)); initButtons(); } private void initButtons() { buttons = new JCheckBox[TITLES.length]; for (int i = 0; i < buttons.length; i++) { JCheckBox b = new JCheckBox(TITLES[i]); b.setOpaque(false); b.setFocusable(false); b.setRolloverEnabled(false); b.setBackground(new Color(0x0, true)); buttons[i] = b; add(b); add(Box.createHorizontalStrut(5)); } } protected void updateButtons(Object v) { removeAll(); initButtons(); Integer i = v instanceof Integer ? (Integer) v : 0; buttons[0].setSelected((i & (1 << 2)) != 0); buttons[1].setSelected((i & (1 << 1)) != 0); buttons[2].setSelected((i & (1 << 0)) != 0); } } class CheckBoxesRenderer extends CheckBoxesPanel implements TableCellRenderer { @Override public void updateUI() { super.updateUI(); setName("Table.cellRenderer"); } @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { updateButtons(value); return this; } } class CheckBoxesEditor extends AbstractCellEditor implements TableCellEditor { private final CheckBoxesPanel panel = new CheckBoxesPanel() { @Override public void updateUI() { super.updateUI(); EventQueue.invokeLater(() -> { ActionMap am = getActionMap(); for (int i = 0; i < buttons.length; i++) { String title = TITLES[i]; am.put(title, new AbstractAction(title) { @Override public void actionPerformed(ActionEvent e) { Arrays.stream(buttons) .filter(b -> b.getText().equals(title)) .findFirst() .ifPresent(JCheckBox::doClick); fireEditingStopped(); } }); } InputMap im = getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW); im.put(KeyStroke.getKeyStroke(KeyEvent.VK_R, 0), TITLES[0]); im.put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0), TITLES[1]); im.put(KeyStroke.getKeyStroke(KeyEvent.VK_X, 0), TITLES[2]); }); } }; @Override public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { panel.updateButtons(value); return panel; } @Override public Object getCellEditorValue() { int i = 0; i = panel.buttons[0].isSelected() ? 1 << 2 | i : i; i = panel.buttons[1].isSelected() ? 1 << 1 | i : i; i = panel.buttons[2].isSelected() ? 1 << 0 | i : i; return i; } } }} #twocolumn `Kotlin` #code{{ import java.awt.* import java.awt.event.* import java.util.* import javax.swing.* import javax.swing.event.* import javax.swing.plaf.* import javax.swing.table.* fun makeUI(): JComponent { val columnNames = arrayOf("user", "rwx") val data = arrayOf(arrayOf<Any>("owner", 7), arrayOf<Any>("group", 6), arrayOf<Any>("other", 5)) val model = object : DefaultTableModel(data, columnNames) { override fun getColumnClass(column: Int): Class<*> { return getValueAt(0, column).javaClass } } val table = object : JTable(model) { override fun updateUI() { super.updateUI() getColumnModel().getColumn(1).setCellRenderer(CheckBoxesRenderer()) getColumnModel().getColumn(1).setCellEditor(CheckBoxesEditor()) } } table.putClientProperty("terminateEditOnFocusLost", java.lang.Boolean.TRUE) return JPanel(BorderLayout(5, 5)).apply { add(JScrollPane(table)) } } fun main(args: Array<String>) { EventQueue.invokeLater { JFrame("kotlin swing").apply { defaultCloseOperation = JFrame.EXIT_ON_CLOSE add(makeUI()) size = Dimension(320, 240) setLocationRelativeTo(null) setVisible(true) } } } open class CheckBoxesPanel() : JPanel() { public val buttons = arrayOf(JCheckBox(Permission.READ.toString()), JCheckBox(Permission.WRITE.toString()), JCheckBox(Permission.EXECUTE.toString())) override fun updateUI() { super.updateUI() setOpaque(false) setBackground(Color(0x0, true)) setLayout(BoxLayout(this, BoxLayout.X_AXIS)) EventQueue.invokeLater({ initButtons() }) } private fun initButtons() { for (b in buttons) { b.setOpaque(false) b.setFocusable(false) b.setRolloverEnabled(false) b.setBackground(Color(0x0, true)) add(b) add(Box.createHorizontalStrut(5)) } } public fun updateButtons(v: Any) { removeAll() initButtons() val i = v as ? Int ? : 0 buttons[0].setSelected(i and (1 shl 2) != 0) buttons[1].setSelected(i and (1 shl 1) != 0) buttons[2].setSelected(i and (1 shl 0) != 0) } } enum class Permission(var str: String) { READ("r"), WRITE("w"), EXECUTE("x"); override fun toString() = str } internal class CheckBoxesRenderer : CheckBoxesPanel(), TableCellRenderer { override fun updateUI() { super.updateUI() setName("Table.cellRenderer") } override fun getTableCellRendererComponent(table: JTable, value: Any, isSelected: Boolean, hasFocus: Boolean, row: Int, column: Int): Component { updateButtons(value) return this } } internal class CheckBoxesEditor : AbstractCellEditor(), TableCellEditor { private val panel = object : CheckBoxesPanel() { override fun updateUI() { super.updateUI() EventQueue.invokeLater({ val am = getActionMap() for (i in buttons.indices) { val title = buttons[i].getText() am.put(title, object : AbstractAction(title) { override fun actionPerformed(e: ActionEvent) { Arrays.stream(buttons) .filter({ b -> b.getText() == title }) .findFirst() .ifPresent({ it.doClick() }) fireEditingStopped() } }) } val im = getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW) im.put(KeyStroke.getKeyStroke(KeyEvent.VK_R, 0), Permission.READ.toString()) im.put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0), Permission.WRITE.toString()) im.put(KeyStroke.getKeyStroke(KeyEvent.VK_X, 0), Permission.EXECUTE.toString()) }) } } override fun getTableCellEditorComponent(table: JTable, value: Any, isSelected: Boolean, row: Int, column: Int): Component { panel.updateButtons(value) return panel } override fun getCellEditorValue(): Any { var i = 0 i = if (panel.buttons[0].isSelected()) 1 shl 2 or i else i i = if (panel.buttons[1].isSelected()) 1 shl 1 or i else i i = if (panel.buttons[2].isSelected()) 1 shl 0 or i else i return i } } }} #twocolumn ** Arrays.asList(...) [#vbc11c20] - `IntelliJ`の自動変換では`Arrays.asList(...)`の型パラメータを自動的に変換してくれない -- `Arrays.<Component>asList(...)`と型パラメータを明示しても省略される -- `for (c in Arrays.asList(...)) {`に変換されて`error: none of the following functions can be called with the arguments supplied:`になる -- `for (c: Component in Arrays.asList<Component>(...)) {`、または`for (c in listOf<Component>(...)) {`などに手動で修正(`for (c: Component in Arrays<Component>.asList(...)) {`も可能?) - 型引数 -- `Java`と`Kotlin`では型引数を付ける位置が異なるので注意 -- `Java`: `Arrays.<Component>asList(...)` --- `For static generic methods, the type parameter section must appear ''before'' the method's return type.` --- [https://docs.oracle.com/javase/tutorial/java/generics/methods.html Generic Methods (The Java™ Tutorials > Learning the Java Language > Generics (Updated))] > [https://docs.oracle.com/javase/tutorial/java/generics/methods.html Generic Methods (The Java™ Tutorials > Learning the Java Language > Generics (Updated))] > For static generic methods, the type parameter section must appear ''before'' the method's return type. -- `Kotlin`: `Arrays.asList<Component>(...)` --- `To call a generic function, specify the type arguments at the call site ''after'' the name of the function:` --- [https://kotlinlang.org/docs/reference/generics.html Generics: in, out, where - Kotlin Programming Language] > [https://kotlinlang.org/docs/reference/generics.html Generics: in, out, where - Kotlin Programming Language] > To call a generic function, specify the type arguments at the call site ''after'' the name of the function: -- `Kotlin`で`Java`風に`for (c in Arrays.<Component>asList(...)) {`を使用すると、`hello.kt:97:26: error: expecting an element`とエラーになる #twocolumn - `Java` #code{{ Box box = Box.createVerticalBox(); // for (Component c: Arrays.asList(textField1, textField2, combo3, combo4)) { //でも同じ変換結果になる for (Component c: Arrays.<Component>asList(textField1, textField2, combo3, combo4)) { box.add(c); box.add(Box.createVerticalStrut(5)); } // for (Component c: Arrays.asList(...)) { でも同じ変換結果になる }} #twocolumn - `Kotlin`(誤変換) - `Kotlin`(自動変換) #code{{ val box = Box.createVerticalBox() for (c in Arrays.asList(textField1, textField2, combo3, combo4)) { box.add(c) box.add(Box.createVerticalStrut(5)) } // hello.kt:100:17: error: none of the following functions can be called with the arguments supplied: // public open fun add(p0: Component!): Component! defined in javax.swing.Box // public open fun add(p0: PopupMenu!): Unit defined in javax.swing.Box // box.add(c) // ^ }} - `Kotlin`(手動修正) #code{{ val box = Box.createVerticalBox() for (c in Arrays.asList<Component>(textField1, textField2, combo3, combo4)) { box.add(c); box.add(Box.createVerticalStrut(5)); } // or: // for (c in listOf<Component>(textField1, textField2, combo3, combo4)) { // for (c in Arrays<Component>.asList(textField1, textField2, combo3, combo4)) { }} #twocolumn * コメント [#comment] #comment #comment