質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.37%
Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Swing

SwingはJavaに標準で付属するグラフィック関連のクラスライブラリを指します。

Q&A

解決済

2回答

1078閲覧

JavaのSwingにて、JPanelの内容を継承クラスに書きたいがぬるぽが発生してしまう

perfectibility

総合スコア46

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Swing

SwingはJavaに標準で付属するグラフィック関連のクラスライブラリを指します。

2グッド

0クリップ

投稿2024/01/04 04:27

実現したいこと

JPanelをアコーディオン風に展開 こちらのサイトを参考にして設計しています。

ラベルをクリックするとラベル下部のパネルの表示・非表示が切り替わる仕組みになっていて、書く内容を何パターンかに分けて3つほどの継承クラスを作成し、配列などで一気に作成できるようにしたいです。

java

1class Main { 2 public static void main(String[] args){ 3 JFrame f = new JFrame(); 4 JPanel p = new JPanel(); 5 CheckBox_Accordion instance = new CheckBox_Accordion("mockTitle", "chk1", "chk2", "chk3"); 6 f.add(p); 7 p.add(instance); 8 f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 9 f.setBounds(100, 100, 640, 400); 10 f.setVisible(true); 11 } 12} 13class CheckBox_Accordion extends Abstract_Accordion { 14 List<String> list = new LinkedList<>(); 15 16 CheckBox_Accordion(String i_title, String... chkbox_title){ 17 super(i_title); 18 Stream.of(chkbox_title).forEach(list::add); 19 } 20 21 JPanel makePanel(){ 22 JPanel panel = new JPanel(); 23 panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); 24 list.stream().map(JCheckBox::new).forEach(panel::add); 25 26 return panel; 27 } 28} 29abstract class Abstract_Accordion extends JPanel { 30 private JPanel panel = makePanel(); 31 32 Abstract_Accordion(String i_title){ 33 JLabel label = new JLabel(i_title); 34 label.addMouseListener(/* 省略 */); 35 36 add(label, BorderLayout.NORTH); 37 add(panel, BorderLayout.CENTER); 38 } 39 40 abstract JPanel makePanel(); 41}

発生している問題・分からないこと

しかし継承先のmakePanel()メソッドでpanelの内容を書こうとすると実行順の影響でぬるぽが発生してしまいます。
今はぬるぽを回避するために継承クラスのコンストラクタ全てにmakePanel()を書かなくてはならず、面倒ですしコーディングミスも起きやすいですし何よりきれいじゃないと思います。

該当のソースコード

java

1Exception in thread "main" java.lang.NullPointerException 2 at CheckBox_Accordion.makePanel(Main.java:33) 3 at Abstract_Accordion.<init>(Main.java:40) 4 at CheckBox_Accordion.<init>(Main.java:26) 5 at Main.main(Main.java:13) 6

試したこと・調べたこと

  • teratailやGoogle等で検索した
  • ソースコードを自分なりに変更した
  • 知人に聞いた
  • その他
上記の詳細・結果

テンプレートパターンが最適かと思っているのですが上手く組み立てられていません。

補足

特になし

TN8001😄を押しています
TN8001🎉を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

jimbe

2024/01/04 06:54

>書く内容を何パターンかに分けて3つほどの継承クラスを作成し、配列などで一気に作成 >しかし継承先のmakePanel()メソッドでpanelの内容を書こうとすると実行順の影響でぬるぽが発生 ということですが、ご提示のコードでご提示のエラーが発生しているのでしょうか。そうは見えないのですが・・・。 パターン別の3つの継承クラスは見当たりませんし、配列で何を作ろうと言われているのかもよく分かりません。makePanel はそれぞれ中身が違くなるはずですので書かなければならないのは当然のように思いますが…恐らく私がイメージしたこととが違うのでしょうね。 ですので、その辺り言葉での説明よりも実際に書きたい(動作させると例外の出る)コードをご提示頂けないでしょうか。
TN8001

2024/01/08 13:26

「グッド」をいただいておりますが、質問は解決したのでしょうか? 解決したのであれば質問を「解決済」にしてください(「意見交換」と違って「Q&A」は、質問者自ら閉じる必要があります) [ヘルプ|質問を解決済みにしたい](https://teratail.com/help#resolve-question) 解決していないのであれば、「回答の〇〇が理解できない」・「望んだ回答でない」等々解決していない理由をコメントしてください。 回答を確認する時間がないということなら、目安の期間をコメントしてください。
perfectibility

2024/01/09 08:38

すみません。 すこし忙しくてグッドを付けるしか出来ておりませんでした。 直ぐに解決済みまたはコメントします。
TN8001

2024/01/09 08:51

せかす意図はありませんのでゆっくり確認されて結構です。 回答に不明点や疑問点があればお気軽にコメントください^^
perfectibility

2024/01/09 09:47

お二方どちらのコードもとても勉強になりました! そもそもAccordionと自分が実装したいものの概念の名称は違うのですね、そこも勉強になりました… TN8001さんの仰る通りabstractでないExpanderクラスを作り、そのコンストラクタで内容を渡すのが簡単かつ分かりやすいですね。 データが多いので配列で渡して一気に作成するのですが、それこそ内容のみのクラスに分けた方がコーディング時も分かり易いのでとても良いと思いました! ベストアンサーを選ぶのはとても迷いましたが、 jimbeさんのコードは知らない事が多く理解するのに時間が掛かりましたが、その分多くのことを学べたので今回はjimbeさんとさせていただきました! 本当にありがとうございました!
guest

回答2

0

アコーディオンというと「開くのはひとつだけ」というイメージがあります(だから「風」なんでしょうが^^;
展開・折り畳みが個別にできるようなので、Expanderとかのほうがあっていそうに思います(以後Expanderと呼称します)

参考サイトの作りは(個人的には)あまりいい作りに思えません(サンプルを作りやすい構造にしたかった等の都合があったのかもしれません)

Expanderはヘッダーのテキストとなんらかのコンテンツを持ち、ヘッダーのクリックでコンテンツの展開・折り畳みをするコンポーネントですね?

Expanderはコンテンツがなんであるかには興味はなく、単に渡されたコンテンツをそのまま使うだけでいいはずです。
なのでabstractである必要はないですし、継承する必要もありません。

パターンごとにクラスを作りたければ、「コンテンツ」として好きに作ればいいでしょう^^

java

1import javax.swing.*; 2import java.awt.*; 3import java.awt.event.*; 4import java.util.stream.Stream; 5 6 7public class Main extends JFrame { 8 public static void main(String[] args) { 9 new Main().setVisible(true); 10 } 11 12 private final JLabel label = new JLabel(" "); 13 14 public Main() { 15 setDefaultCloseOperation(EXIT_ON_CLOSE); 16 setSize(300, 200); 17 setLocationRelativeTo(null); 18 19 add(label, BorderLayout.NORTH); 20 21 var box = Box.createVerticalBox(); 22 23 var cp = new CheckBoxPanel("111", "222"); 24 box.add(new Expander("CheckBox", cp)); 25 box.add(Box.createVerticalStrut(5)); 26 27 var rp = new RadioButtonPanel("aaa", "bbb", "ccc"); 28 box.add(new Expander("RadioButton", rp)); 29 box.add(Box.createVerticalStrut(5)); 30 31 box.add(Box.createVerticalGlue()); 32 add(new JScrollPane(box)); 33 } 34 35 class CheckBoxPanel extends JPanel { 36 CheckBoxPanel(String... texts) { 37 setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); 38 Stream.of(texts).map(JCheckBox::new).forEach(b -> { 39 add(b); 40 b.addActionListener(e -> label.setText((b.isSelected() ? "selected " : "unselected ") + b.getText())); 41 }); 42 } 43 } 44 45 class RadioButtonPanel extends JPanel { 46 RadioButtonPanel(String... texts) { 47 setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); 48 var bg = new ButtonGroup(); 49 Stream.of(texts).map(JRadioButton::new).forEach(b -> { 50 b.setSelected(getComponentCount() == 0); 51 add(b); 52 bg.add(b); 53 54 b.addItemListener(e -> { 55 if (e.getStateChange() == ItemEvent.SELECTED) { 56 label.setText("selected " + b.getText()); 57 } 58 }); 59 }); 60 } 61 } 62} 63 64class Expander extends JPanel { 65 Expander(String header, JComponent content) { 66 super(new BorderLayout()); 67 68 var label = new JLabel("▼ " + header); 69 label.addMouseListener(new MouseAdapter() { 70 @Override public void mousePressed(MouseEvent e) { 71 content.setVisible(!content.isVisible()); 72 label.setText(String.format("%s %s", content.isVisible() ? "△" : "▼", header)); 73 revalidate(); 74 EventQueue.invokeLater(() -> scrollRectToVisible(content.getBounds())); 75 } 76 }); 77 add(label, BorderLayout.NORTH); 78 79 content.setVisible(false); 80 add(content, BorderLayout.CENTER); 81 } 82 83 @Override public Dimension getMaximumSize() { 84 var d = getPreferredSize(); 85 d.width = Short.MAX_VALUE; 86 return d; 87 } 88}

アプリ動画

投稿2024/01/04 09:35

TN8001

総合スコア9807

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

ベストアンサー

質問のコードは makePanel でフィールドを初期化していますが、そこまで行っていればあと少しで「コンストラクタパラメータで渡してしまえば」と気付けたように思います。
回答としては TN8001 さんと同じく「コンテンツの生成をリンク先のようにメソッドで持つ必要は無い」ということになります。

java

1class Main { 2 public static void main(String[] args){ 3 JFrame f = new JFrame(); 4 JPanel p = new JPanel(); 5 Accordion instance = new Accordion("mockTitle", new CheckBoxContents("chk1", "chk2", "chk3")); 6 f.add(p); 7 p.add(instance); 8 f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 9 f.setBounds(100, 100, 640, 400); 10 f.setVisible(true); 11 } 12} 13//CheckBox_Accordion の makePanel で作っていたヤツ 14class CheckBoxContents extends JPanel { 15 CheckBoxContents(String... chkboxTitle) { 16 super(null); 17 setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); 18 Stream.of(chkboxTitle).map(JCheckBox::new).forEach(this::add); 19 } 20} 21//makePanel で作るのでは無くコンストラクタパラメータで貰う 22class Accordion extends JPanel { 23 Accordion(String iTitle, JPanel panel){ 24 JLabel label = new JLabel(iTitle); 25 label.addMouseListener(/* 省略 */); 26 27 add(label, BorderLayout.NORTH); 28 add(panel, BorderLayout.CENTER); 29 } 30}

リンク先の GitHub のコードを弄ってみました。
コンテンツクラスを流用ということで CheckPanel を 2 回使っていますが、配列にしてというのは必要か分からなかったのでやっていません。
また、ラベルとコンテンツパネルの組のクラスを土台のパネルに並べるよりも(ラベルとコンテンツの組は複数有るのが通常でしょうから)土台に直接ラベルとパネルを並べたほうが階層が減って良いと思います。

ついでに、タイトルにはラベルで無くチェックボックスを使えば操作はマウス以外でも出来るようになります(チェック部分の画像は差し替えられます)し、 BoxLayout で無く GridBagLayout なら大きさの調整の為にメソッドをオーバーライドする必要もありません。

java

1import java.awt.*; 2 3import javax.swing.*; 4import javax.swing.event.AncestorEvent; 5import javax.swing.event.AncestorListener; 6 7public class MainFrame extends JFrame { 8 public static void main(String[] args) { 9 SwingUtilities.invokeLater(() -> new MainFrame().setVisible(true)); 10 } 11 12 MainFrame() { 13 super("ExpandableContentsPanel"); 14 setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); 15 16 add(new JScrollPane(new ExpandableContentsPanel())); 17 18 setSize(320, 400); 19 //pack(); 20 setLocationRelativeTo(null); 21 } 22} 23 24class ExpandableContentsPanel extends JPanel { 25 private static final AncestorListener visibleToScroll = new AncestorListener() { 26 @Override 27 public void ancestorRemoved(AncestorEvent event) { /*no process*/ } 28 @Override 29 public void ancestorMoved(AncestorEvent event) { /*no process*/ } 30 @Override 31 public void ancestorAdded(AncestorEvent event) { scrollRectToVisible(event.getComponent()); } 32 }; 33 34 private static void scrollRectToVisible(JComponent c) { 35 ((JComponent)c.getParent()).scrollRectToVisible(c.getBounds()); 36 } 37 38 ExpandableContentsPanel() { 39 super(new GridBagLayout()); 40 setBackground(new Color(180, 180, 255)); 41 setBorder(BorderFactory.createEmptyBorder(10, 5, 10, 5)); 42 43 GridBagConstraints gbc = new GridBagConstraints(); 44 gbc.gridx = 0; 45 gbc.fill = GridBagConstraints.HORIZONTAL; 46 gbc.weightx = 1; 47 add("System Tasks", new CheckPanel("1111","222222"), gbc); 48 add("User Tasks", new CheckPanel("33333","444","55"), gbc); 49 add("Other Places", new LabelPanel("Desktop","My Network Places","My Documents","Shared Documents"), gbc); 50 add("Details", new RadioPanel("aaa","bbb","ccc","ddd"), gbc); 51 52 gbc.weighty = 1; 53 add(Box.createVerticalStrut(0), gbc); 54 55 KeyboardFocusManager.getCurrentKeyboardFocusManager().addPropertyChangeListener("focusOwner", e -> { 56 if(!(e.getNewValue() instanceof JComponent)) return; 57 JComponent focused = (JComponent)e.getNewValue(); 58 if(this.isAncestorOf(focused)) scrollRectToVisible(focused); 59 }); 60 } 61 62 private void add(String titleText, JComponent contents, GridBagConstraints gbc) { 63 Title title = new Title(titleText); 64 title.addItemListener(e -> contents.setVisible(title.isSelected())); 65 contents.setVisible(title.isSelected()); 66 contents.addAncestorListener(visibleToScroll); 67 68 if(getComponentCount() > 0) add(Box.createVerticalStrut(5), gbc); 69 add(title, gbc); 70 add(contents, gbc); 71 } 72} 73 74class Title extends JCheckBox { 75 private static Icon icon = new ExpandedStateIcon(); 76 77 Title(String text) { 78 super(text, icon); 79 setOpaque(false); 80 setForeground(Color.BLUE); 81 setBorder(BorderFactory.createEmptyBorder(2, 5, 2, 2)); 82 } 83 84 @Override 85 protected void paintComponent(Graphics g) { 86 Graphics2D g2 = (Graphics2D)g; 87 g2.setPaint(new GradientPaint(50, 0, Color.WHITE, getWidth(), getHeight(), new Color(200, 200, 255))); 88 g2.fillRect(0, 0, getWidth(), getHeight()); 89 super.paintComponent(g); 90 } 91} 92 93class ExpandedStateIcon implements Icon { 94 private final int iconWidth, iconHeight; 95 private final int dx, dy; //位置補正 96 private final Polygon selectedPolygon, polygon; 97 98 ExpandedStateIcon() { 99 Icon orgIcon = UIManager.getIcon("CheckBox.icon"); 100 iconWidth = orgIcon.getIconWidth(); 101 iconHeight = orgIcon.getIconHeight(); 102 int pw = (int)(iconWidth * 0.8); //大きさはテキトウ 103 int ph = (int)(iconHeight * 0.6); // 〃 104 dx = (iconWidth-pw) / 2; 105 dy = (iconHeight-ph) / 2; 106 selectedPolygon = new Polygon(new int[]{pw/2,0,pw}, new int[]{0,ph,ph}, 3); //上向き三角 107 polygon = new Polygon(new int[]{0,pw,pw/2}, new int[]{0,0,ph}, 3); //下向き三角 108 } 109 110 @Override 111 public void paintIcon(Component c, Graphics g, int x, int y) { 112 Graphics2D g2 = (Graphics2D)g.create(); 113 g2.translate(x+dx, y+dy); 114 g2.setColor(c.isEnabled() ? c.getForeground() : Color.GRAY); 115 g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 116 if(c instanceof AbstractButton && ((AbstractButton)c).isSelected()) { 117 g2.draw(selectedPolygon); 118 } else { 119 g2.fill(polygon); 120 } 121 g2.dispose(); 122 } 123 @Override 124 public int getIconWidth() { return iconWidth; } 125 @Override 126 public int getIconHeight() { return iconHeight; } 127} 128 129class Contents extends JPanel { 130 Contents(LayoutManager layout) { 131 super(layout); 132 setBackground(new Color(240, 240, 255)); 133 setBorder(BorderFactory.createCompoundBorder( 134 BorderFactory.createMatteBorder(0, 2, 2, 2, Color.WHITE), 135 BorderFactory.createEmptyBorder(10, 10, 10, 10))); 136 } 137} 138 139class CheckPanel extends Contents { 140 CheckPanel(String... labels) { 141 super(new GridLayout(0, 1)); 142 143 for(String s : labels) { 144 JCheckBox b = new JCheckBox(s); 145 b.setOpaque(false); 146 add(b); 147 } 148 } 149} 150 151class LabelPanel extends Contents { 152 LabelPanel(String... labels) { 153 super(new GridLayout(0, 1)); 154 155 for(String s : labels) { 156 add(new JLabel(s)); 157 } 158 } 159} 160 161class RadioPanel extends Contents { 162 RadioPanel(String... labels) { 163 super(new GridLayout(0, 1)); 164 165 ButtonGroup bg = new ButtonGroup(); 166 for(String s : labels) { 167 JRadioButton b = new JRadioButton(s); 168 b.setOpaque(false); 169 b.setSelected(getComponentCount() == 0); 170 add(b); 171 bg.add(b); 172 } 173 } 174}

スクリーンショット - Tasks をオープンしてチェック

投稿2024/01/06 10:07

編集2024/01/09 04:06
jimbe

総合スコア13168

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.37%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問