Javaアプリケーションのインターフェースの改善

こんにちは、ガーディアン!

インターネット上でSwingとグラフィックスを操作するトピック、およびJavaアプリケーションインターフェイスのトピックには、非常に多くの断片化された情報が存在します。 Javaは時代遅れであり、Javaデスクトップアプリケーションは意味をなさないと誰かが主張し、泡を口にした人はその反対を証明します。 同時に、作業が進行中で、アプリケーションが作成されており、他の問題が発生しています。 前回の記事で、例外的なケースに役立つ便利なライブラリのリストを既に示しましたが、サードパーティのライブラリでは必要なことを実行できないことがよくあります。 このような瞬間に、独自のコンポーネントを作成する必要性について考える必要があります。

したがって、この投稿では、Swingとグラフィックスの操作に関する私の意見で最も重要で重要な瞬間を述べようとしました-コンポーネントを作成する方法、やりがいのないインターフェースを様式化する方法など...


雲にねじれた


「あなたのアプリケーションのデザインを明るく思い出深いものにする方法」と「Javaでそれを可能にする方法」という質問によって、多くの人々がJavaでの研究や開発を妨げられていると思います。少なくとも部分的に状況を明らかにします。

したがって、今日はこれらの質問に答えて、「グレー」インターフェース、コンポーネントの欠如、「怖い」デザイン、および開発者がおそらく遭遇するその他のことについての幻想を払拭しようとします。 確かに、十分な創意工夫や答えを見つけたいという欲求を持っていない-それは何も起こりません。 しかし、これはまさにこの言語に私を惹きつけるものです。なぜなら、十分な知識と、さらに重要なこととして、欲求があれば、あなたに必要なものを素早く簡単に実現することができるからです。

何がありますか?


おそらく基本から始めて、「より重要な事項」について議論することから始める価値があるでしょう。

SwingやLaFのようなものとそれらがどのように配置されているかをよく知っている場合は、無駄に時間を無駄にせず、すぐにトピックの次の章に進むことをお勧めします。

そのため、Javaアプリケーションインターフェイスの構築はSwingライブラリに基づいています。このライブラリには、さまざまなオペレーティングシステムに含まれるすべての基本コンポーネントが含まれています。 それらは異なって見えるかもしれず、わずかに異なる機能的特徴を持っているかもしれません(異なるオペレーティングシステム上で)が、本質的には、同じ目的を果たします。 これらは、ボタン(JButton)、テキストフィールド(JTextField、JPasswordField)、ラベル(JLabel)、テキスト領域(JTextArea、JEditorPane)、スクロール(JScrollBar、JScrollPane)などのコンポーネントです。

「基本構成」でのスイングにより、複数の異なるLaF(ルックアンドフィール、「スイングスキン」と呼ぶこともできます)を使用して、アプリケーションのすべてのコンポーネントの外観を「一度に」変更できます-Metal LaF(標準Javaスタイル)、Nimbus LaF(個別の特定のスタイル)、System LaF(OSのスタイル)。 ただし、すべてのプロジェクトで簡単かつ迅速に使用できる完全なライブラリで装飾された多数の個別に作成されたLaFがあります。
個々のコンポーネントはそれぞれ、レンダリング方法を決定するUIクラスを指定することができます。場合によっては、どの機能を使用するかは、すべて実装に依存します。 LaF自体は、アプリケーションで使用可能なコンポーネントのツリー全体のUIをすぐに定義します。 LaFを使用したり、UIを設定して個々のコンポーネントのスタイルを変更したりできます。 特定のLaF(システムなど)を設定し、必要に応じて個々の要素のUIを変更することもできます。

さまざまなカスタムLaFを備えたリソースをいくつか紹介します。これらのリソースは、すぐに使用に適応できます。

最も有名で精巧なスタイルのいくつか

Tiny LaF -Windows XPのクラシックスタイルの類似物


Quaqua LaF-コンポーネントをカスタマイズするための多くの機能を備えたMac OS Xインターフェースの類似物


サブスタンスラフ


シンセティカラフ


アロイラフ
画像

Jgoodies


複数のスタイルセット

セット#1-有名なさまざまなLaF for Javaアプリケーション専用のサイト
セット#2-さまざまなLaFのリストを含む小さな記事
セット#3-別のLaF記事

すべてのLaFがどのOSでも使用できるわけではないことに注意することも重要です。 Quaqua LaFなど、特定のオペレーティングシステムに特化したものもあります。 もちろん、WindowsまたはLinuxで実行でき、一見問題はありませんが、一部のコンポーネントはJVMクラッシュまでの操作中にエラーを引き起こす可能性があります。 他のスタイルの中には、意図しないOSで起動しないものもあります。 一部のスタイルは、ライセンス契約に基づいて特定のオペレーティングシステムで使用することを禁じられています。

サードパーティの開発


実践が示すように、すべてが完全にゼロから行われた場合、すべてのコンポーネントを順番に最新の状態に開発および維持するための十分なエネルギーが資金から得られません。 したがって、何らかの方法で、サードパーティのライブラリの助けに頼らなければなりません。 使用可能な機能に完全に依存できる場合もあれば、完全に自分に合っている場合は、ライブラリの最も成功した部分を自分の目的に使用するのが簡単な場合もあります。

したがって、インターフェイス要素の記述/変更の手書きの説明に進む前に、利用可能でサポートされ、かなり人気のあるライブラリを提供することは論理的であると考えます。 それでは、この分野でどのような開発が存在するのかを見てみましょう(これらのサイトにアクセスして確認することをお勧めします)。

SWTサイト )( マニュアル/ドック )( )( ダウンロード
ライセンス:EPL
サポートされているプラ​​ットフォーム:Windows / Linux / MacOSX / Solarisなど
SWTは、さまざまなオペレーティングシステムのネイティブコンポーネントを便利に使用するためのラッパーです。 SwingコンポーネントとSWTコンポーネントを直接組み合わせることはできませんが、このために設計されたいくつかのライブラリがあります(たとえば、 これ )。

SwingXサイト )( マニュアル/ドック )( ダウンロード
ライセンス:LGPL 2.1
サポートされるプラットフォーム:すべてのJavaサポートプラットフォーム
Swingコンポーネントの拡張セットを備えた、かなり古くて有名なライブラリ。 現時点では、プロジェクトは何らかの移行期にあります。

Jideサイト )( マニュアル/ドック )( デモをダウンロード
ライセンス:商用/ GPL、クラスパス例外あり
サポートされるプラットフォーム:すべてのJavaサポートプラットフォーム
幅広いさまざまなコンポーネントを提供する最高の商用ライブラリの1つ。

オプショナル

おそらく、 以前の記事で言及したライブラリは、より複雑な要素(フラッシュ/ビデオなど)を使用してアプリケーションを開発するのに役立つでしょう。 私はいくつかのプロジェクトで使用した別のライブラリ-JavaLayer-のみを追加できます。これにより、mp3ファイルとmp3-streamsを再生できます。 使いやすく、ネイティブなものを必要とせず、クロスプラットフォームで、マシンのリソースを消費しません。

もちろん、他にもいくつかのライブラリ(商用と無料の両方)がありますが、それらは古くなっているか、有用なものがほとんど含まれていません。 だから、あなたはすでにLaFを使用できるかを見て、それは異なるライブラリに異なる既製のコンポーネントがありますが、最終的にはあなたはまだ満足していません-コンポーネントのいくつかはあなたが望むように間違ったスタイルを持っています、ライブラリはすでに10回古くなっています、致命的ですライブラリ内のエラー、サポートはスラックを与えます...どうすればいいですか? 間違いなく、1人の無料のデザイナーとコードを自分の手に渡してください!..

ですから、最初に、深呼吸をして、息を吐き、落ち着いてください-コンポーネントを書くことに何の問題もありません。 これは完全に認識されなければなりません。 また、特定のコンポーネントまたはUIを作成する期間は、その複雑さ、設計者の作業の複雑さ、およびJavaプログラマーとしての知識のみに依存することを認識する必要があります。 また、明確な境界もあります-これを行う場合としない場合-それはタイミング、利用可能なリソース、およびその他のよく知られていない要因に依存しますが、これは完全に異なるトピックです。 だから、私は今あなたが続ける準備ができていると思います。

UIを書く


まず、この時点で少し時間がかかります。いくつかのトリッキーなコンポーネントが常に必要なわけではなく、定型化された標準的なコンポーネントだからです。 このような場合、特にすべての標準Swingコンポーネントには例外なく、コンポーネントの独自のスタイルをすばやく簡単に作成できるBasic-UIクラスがあるため、Jコンポーネントの小さなUIクラスを記述することに頼る必要があります。 JSliderのUIを作成する小さな例を示します。

最初に、BasicSliderUIをオーバーライドするクラスを作成し、その外観を既存の画像で記述します。
public class MySliderUI extends BasicSliderUI
{
public static final ImageIcon BG_LEFT_ICON =
new ImageIcon ( MySliderUI. class .getResource ( "icons/bg_left.png" ) );
public static final ImageIcon BG_MID_ICON =
new ImageIcon ( MySliderUI. class .getResource ( "icons/bg_mid.png" ) );
public static final ImageIcon BG_RIGHT_ICON =
new ImageIcon ( MySliderUI. class .getResource ( "icons/bg_right.png" ) );
public static final ImageIcon BG_FILL_ICON =
new ImageIcon ( MySliderUI. class .getResource ( "icons/bg_fill.png" ) );
public static final ImageIcon GRIPPER_ICON =
new ImageIcon ( MySliderUI. class .getResource ( "icons/gripper.png" ) );
public static final ImageIcon GRIPPER_PRESSED_ICON =
new ImageIcon ( MySliderUI. class .getResource ( "icons/gripper_pressed.png" ) );

public MySliderUI ( final JSlider b )
{
super ( b );

//
b.addChangeListener ( new ChangeListener()
{
public void stateChanged ( ChangeEvent e )
{
b.repaint ();
}
} );
b.addMouseListener ( new MouseAdapter()
{
public void mousePressed ( MouseEvent e )
{
b.repaint ();
}

public void mouseReleased ( MouseEvent e )
{
b.repaint ();
}
} );
}

//
protected Dimension getThumbSize ()
{
return new Dimension ( GRIPPER_ICON.getIconWidth (), GRIPPER_ICON.getIconHeight () );
}

//
public void paintThumb ( Graphics g )
{
int positionX = thumbRect.x + thumbRect.width / 2;
int positionY = thumbRect.y + thumbRect.height / 2;
g.drawImage ( isDragging () ? GRIPPER_PRESSED_ICON.getImage () : GRIPPER_ICON.getImage (),
positionX - GRIPPER_ICON.getIconWidth () / 2,
positionY - GRIPPER_ICON.getIconHeight () / 2, null );
}

// «»
public void paintTrack ( Graphics g )
{
if ( slider.getOrientation () == JSlider.HORIZONTAL )
{
//
g.drawImage ( BG_LEFT_ICON.getImage (), trackRect.x,
trackRect.y + trackRect.height / 2 - BG_LEFT_ICON.getIconHeight () / 2, null );
g.drawImage ( BG_MID_ICON.getImage (), trackRect.x + BG_LEFT_ICON.getIconWidth (),
trackRect.y + trackRect.height / 2 - BG_MID_ICON.getIconHeight () / 2,
trackRect.width - BG_LEFT_ICON.getIconWidth () - BG_RIGHT_ICON.getIconWidth (),
BG_MID_ICON.getIconHeight (), null );
g.drawImage ( BG_RIGHT_ICON.getImage (),
trackRect.x + trackRect.width - BG_RIGHT_ICON.getIconWidth (),
trackRect.y + trackRect.height / 2 - BG_RIGHT_ICON.getIconHeight () / 2, null );

//
g.drawImage ( BG_FILL_ICON.getImage (), trackRect.x + 1,
trackRect.y + trackRect.height / 2 - BG_FILL_ICON.getIconHeight () / 2,
thumbRect.x + thumbRect.width / 2 - trackRect.x - BG_LEFT_ICON.getIconWidth (),
BG_FILL_ICON.getIconHeight (), null );
}
else
{
// jar'
}
}
}
ご覧のとおり、レンダリングに必要な変数はすべてBasic-UIクラスにあり、直接使用できます。
thumbRect-インフルエンザの長方形
trackRect-スライダーの「パス」の長方形
実際、必要な再描画のほとんどはBasic-UIクラス自体が原因であり、心配する必要はありません(たとえば、ドレーパーをクリックしたりドラッグしたりする場合)。

このUIでは、スライダーの「パス」の大きさを決定するcalculateTrackRect()メソッドを少し改ざんし、再定義しませんでした。 レンダリングされた画像を使用可能なサイズの中央に合わせました。 しかし、これは好みの問題です。 もう1つの質問は、好ましいサイズがインフルエンザのサイズとパスに基づいて計算されることです。この場合、インフルエンザはパスよりもはるかに多くなります。

そのため、このような簡単な方法で、まったく新しい外観のスライダーができました。 これをアプリケーションで使用するには、JSliderのUIとして設定します。
JSlider mySlider = new JSlider ();
mySlider.setUI ( new GuiSliderUI ( mySlider ) );
そして、ここにそのような様式化されたスライダーがあります:

サンプルとグラフィックを含む完全なコードはここで取得できます (スライダーの垂直バージョンも実装します。元のJavaクラスはjar内にあります)。

もちろん、個々のBasic-UI(BasicButtonUI、BasicTextFieldUI、BasicTableUI、BasicTabbedPaneUIなど)には、コンポーネントパーツをレンダリングする独自の特性がありますが、特にJDKソースコードで使用可能なすべてのメソッドが非常に詳細であるため、そこに複雑なものはなく、それを把握することは難しくありませんコメントと説明。

UIについての会話を論理的に締めくくるために、Basic-UIに加えて、既知のすべてのJコンポーネントについて、WindowsSliderUI、MetalSliderUIなど、さまざまなLaFの実装があることを追加します。 通常、ライブラリの名前またはその目的(ネイティブOSコンポーネントの場合、たとえばOsnameComponentUI)に従って呼び出されます。 時々、共通のLaFをインストールせずにそれらを使用することができますが、常にではありません-可能かどうかは、純粋にライブラリの実装に依存します(たとえば、LaFをインストールする場合、スタイルは個別のUIが動作しない場合にロードされます)。

したがって、この例では、既製のグラフィックスを使用してきちんとした視覚コンポーネントを作成しましたが、標準のJavaツールまたはGraphics2Dツールのみを使用して優れた視覚コンポーネントを描画できます-これについては後で説明します...

Graphics2Dを使用する


上記の章で、グラフィックスの操作について何気なく言及しましたが、スライダーの個々の部分が描画されるのはまさにその意味ででした。 しかし、これはGrahics2Dが提供する可能性のまさに最上部にすぎません。

「paint」のすべてのメソッド(および「paintComponent」、「print」など)にはGrahics2Dではなくグラフィックスが含まれていますが、Jで作業していれば、Grahics2Dにキャストすることを恐れることはできません。コンポーネントまたはその後継。 これはなぜですか? これらは、いわば、Swingが育った古い部分の小さな残骸であり、現時点で深く掘り下げる価値はないと思います。 この問題の詳細については、Swing内部デバイスに関するSkipyの記事をご覧ください

また、この章では、グラフィックスを使用するためのオプションがいくつかあることもすぐに言います。実際、その範囲ははるかに広いです。 グラフィックスとそれに関連するすべての詳細については、おそらくJavaに関する次のテーマのトピックで説明します(もちろん、この結果と読者の間での関心の高まりがあります)。 しかし、トピックに戻って、十分なネタバレ...

では、何がありますか? Graphics2Dはどのような機能を提供しますか?
主なものをリストします。


私はすぐに言います-グラフィックを扱うとき、おそらく、時間の経過とともに、最高の視覚効果を達成するために、例外なく、提供されたすべてのツールを使用する必要があります。 ここで何がどのように見つかるかの詳細な説明は、公式のGraphics2Dチュートリアルです。 最新の状態にするのに十分なはずです。

UIを作成する小さな例を既に示しましたが、インターフェイスをカスタマイズするための他のオプションがあります。 個々のJコンポーネントは、paint()メソッドを使用して独自の軽量レンダリングを生成します。これは、簡単に再定義および変更できます。 直接(常にではありませんが、ほとんどの場合)、使用しない方が良いです(これは別のトピックのトピック全体であるため、詳細には触れません)。 次の例では、paintComponent()メソッドを使用します。 それがどのように近く適用されるのか見てみましょう...

例から始めましょう-コンテンツがない場合の視覚的なフィードバックを備えたテキストフィールド:
JTextField field = new JTextField()
{
private boolean lostFocusOnce = false ;
private boolean incorrect = false ;

{
//
addFocusListener ( new FocusAdapter()
{
public void focusLost ( FocusEvent e )
{
lostFocusOnce = true ;
incorrect = getText ().trim ().equals ( "" );
repaint ();
}
} );
addCaretListener ( new CaretListener()
{
public void caretUpdate ( CaretEvent e )
{
if ( lostFocusOnce )
{
incorrect = getText ().trim ().equals ( "" );
}
}
} );
}

protected void paintComponent ( Graphics g )
{
super.paintComponent ( g );

//
if ( incorrect )
{
Graphics2D g2d = ( Graphics2D ) g;

//
g2d.setRenderingHint ( RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON );

//
Insets insets;
if ( getBorder () == null )
{
insets = new Insets ( 2, 2, 2, 2 );
}
else
{
insets = getBorder ().getBorderInsets ( this );
}

//
GeneralPath gp = new GeneralPath ( GeneralPath.WIND_EVEN_ODD );
gp.moveTo ( insets.left, getHeight () - insets.bottom );
for ( int i = 0; i < getWidth () - insets.right - insets.left; i += 3 )
{
gp.lineTo ( insets.left + i,
getHeight () - insets.bottom - ( ( i / 3 ) % 2 == 1 ? 2 : 0 ) );
}

//
g2d.setPaint ( Color.RED );
g2d.draw ( gp );
}
}
};
印刷してフィールドでフォーカスを失うと、コンテンツの存在が再確認されます。 別のコンポーネントに切り替えると、JTextFieldへの追加がどのようにレンダリングされるかがわかります。

完全なサンプルコードはこちらにあります

したがって、個別のかさばるUIや本格的なコンポーネントを記述することなく、レンダリングメソッドをすばやくオーバーライドおよび追加することにより、使用可能なコンポーネントを拡張できます。 さらに、この例を簡単に別のクラスに配置し、インターフェイスの既製の要素として使用できます。
この方法のもう1つの利点は、現在インストールされているアプリケーション/コンポーネントとは無関係にLaF / UIを取得できることです。これは常に機能します。 当然、一部の特定のUIでは、若干異なるレンダリングが必要になる場合があります-それをサポートするかどうかはあなた次第です。

インターフェースをより明るく魅力的にしたい場合がよくありますが、このために新しいコンポーネントを発明する必要は必ずしもありません-標準ツールを使用できます。 状態と背景のアニメーション化された変更による最も単純なチェックボックスのカスタマイズの例を示します。 今回も(はい、スティックで私を打ち負かすことができます)私はデザイナーの熟練した手から準備された画像を使用します。

16 x 16の8つの画像が基準として使用されます-4つのチェックボックスバックグラウンド状態と4つのジャックドー状態(実際には5つですが、プログラムで5つ目を追加します)


もちろん、標準のチェックボックスには、状態をアニメートするためのスプライトを設定する機能がありません。さらに、さまざまなバリエーションの背景にジャックドー画像をオーバーレイする必要があります。 これを行うには、別のメソッドを追加します。
public static List <ImageIcon> BG_STATES = new ArrayList <ImageIcon> ();
public static List <ImageIcon> CHECK_STATES = new ArrayList <ImageIcon> ();

static
{
//
for ( int i = 1; i <= 4; i++ )
{
BG_STATES.add ( new ImageIcon (
MyCheckBox. class .getResource ( "icons/states/" + i + ".png" ) ) );
}

// ""
CHECK_STATES.add ( new ImageIcon (
new BufferedImage ( 16, 16, BufferedImage.TYPE_INT_ARGB ) ) );

//
for ( int i = 1; i <= 4; i++ )
{
CHECK_STATES.add ( new ImageIcon (
MyCheckBox. class .getResource ( "icons/states/c" + i + ".png" ) ) );
}
}

private Map< String , ImageIcon> iconsCache = new HashMap< String , ImageIcon> ();

private synchronized void updateIcon ()
{
//
final String key = bgIcon + "," + checkIcon;
if ( iconsCache.containsKey ( key ) )
{
//
setIcon ( iconsCache. get ( key ) );
}
else
{
//
BufferedImage b = new BufferedImage ( BG_STATES. get ( 0 ).getIconWidth (),
BG_STATES. get ( 0 ).getIconHeight (), BufferedImage.TYPE_INT_ARGB );
Graphics2D g2d = b.createGraphics ();
g2d.drawImage ( BG_STATES. get ( bgIcon ).getImage (), 0, 0,
BG_STATES. get ( bgIcon ).getImageObserver () );
g2d.drawImage ( CHECK_STATES. get ( checkIcon ).getImage (), 0, 0,
CHECK_STATES. get ( checkIcon ).getImageObserver () );
g2d.dispose ();

ImageIcon icon = new ImageIcon ( b );
iconsCache.put ( key, icon );
setIcon ( icon );
}
}
いくつかの状態遷移ハンドラーを追加することは残り、それらの間のアニメーション化された遷移を取得します。

結果のコンポーネントは、たとえばラジオボタンに変更したり、アニメーションをスムーズにするために遷移状態を追加したりするなど、非常に簡単になります。
実際、完全な動作コードと画像はここに個別にレイアウトされています

したがって、要素をカスタマイズする方法は多数あります(おそらく、それらの一部については知らない/知らないこともあります)。 どちらを選択するかは、状況によって異なります-受け取る必要があるもの、既存のコンポーネントに加えることができる変更など。

この章の終わりに、アニメーション付きの完全に変更されたUIボタン、個々の角を丸くする機能、スタイルをカスタマイズする機能、およびその他の改善点の別の例を示します。 最終的な外観のスクリーンショットを次に示します(もちろん、アニメーションはここには表示されません)。

嘘はつきませんが、このUIボタンは多くの時間を要しましたが、Graphics2DとSwingの純粋な手段を使用するデザイナーの助けとヒントなしに作成されました。 興味がある場合は、ここでこのUIの完全なコースとデモをダウンロードして学習できます。 かなり広範囲のGraphics2D機能を使用し、多くの場合に役立つ可能性のあるいくつかのトリックを適用します。

したがって、グラフィックスについては十分な話があると思います。今後のトピックでさらに詳しく説明します。そして、SwingとGraphics2Dとのかなり長い期間の「コミュニケーション」で得た興味深い資料を紹介します。

DnDとGlassPane


私は最初に思う-あなたはすべて知っているだけでなく、それに関連する問題。 2番目については、GlassPaneのことをたぶん聞いたことがあるか、標準フレームの異なるレイヤーの配置に関するこの古代の画像 (ちなみにまだ関連している)を見たことがあるかもしれません。 そこに何があり、なぜ私はこれを覚えていましたか? さらに、DnDとGlassPaneはどのように関連していますか? それがまさにそれらをつなぐ方法であり、それから何ができるかであり、この章で伝えたい。

さて、順番に始めましょう-DnDについて何を知っていますか?
一部のSwingコンポーネントには、dr用の既製の実装(たとえば、JTreeやJList)があります-他のコンポーネントでは、独自のコンポーネントを簡単に追加できます。 言葉を風に投げ込まないために、ラベルからのDnD文字列の小さな例を示します。
JLabel label = new JLabel ( " DnD" );
label.setTransferHandler ( new TransferHandler()
{
public int getSourceActions ( JComponent c )
{
return TransferHandler.COPY;
}

public boolean canImport ( TransferSupport support )
{
return false ;
}

protected Transferable createTransferable ( JComponent c )
{
return new StringSelection ( ( ( JLabel ) c ).getText () );
}
} );
label.addMouseListener ( new MouseAdapter()
{
public void mousePressed ( MouseEvent e )
{
if ( SwingUtilities.isLeftMouseButton ( e ) )
{
JComponent c = ( JComponent ) e.getSource ();
TransferHandler handler = c.getTransferHandler ();
handler.exportAsDrag ( c, e, TransferHandler.COPY );
}
}
} );
これで、このラベルのテキストをインターフェイスから直接、ドロップを介してテキストを挿入できる他の場所にドラッグできます。
実際、TransferHandlerは、ドラッグ中にコンポーネントが提供するデータと、ドラッグ時にコンポーネントが受信データを使用する方法を処理します。

しかし、ドラッグアンドドロップ時のユーザーアクションのシーケンスを追跡する必要がある場合はどうでしょうか。
これを行うには、リスナーをハングさせる別の機会があります。
DragSourceAdapter dsa = new DragSourceAdapter()
{
public void dragEnter ( DragSourceDragEvent dsde )
{
// -
}

public void dragExit ( DragSourceEvent dse )
{
// -
}

public void dropActionChanged ( DragSourceDragEvent dsde )
{
//
}

public void dragOver ( DragSourceDragEvent dsde )
{
//
}

public void dragMouseMoved ( DragSourceDragEvent dsde )
{
//
}

public void dragDropEnd ( DragSourceDropEvent dsde )
{
//
}
};
DragSource.getDefaultDragSource ().addDragSourceListener ( dsa );
DragSource.getDefaultDragSource ().addDragSourceMotionListener ( dsa );
最後の瞬間は残ります-GlassPaneの役割を示します。 実際、GlassPaneを使用すると、他のコンテナと同様にコンポーネント自体をコンポーネントに配置/描画できますが、その特徴は、表示されているときにすべてのSwingコンポーネントの上に配置されることです。 つまり その上に何かを描くと、その下のインターフェース全体をカバーします。 これにより、メインコンテナに関係なくコンポーネントを配置し、視覚効果を作成し、他の興味深いことを実行できます。

理解を深めるために、このような「効果」の小さな例を示します。Swingコンポーネントがいくつかあるフレームです。 ウィンドウの任意の部分をクリックすると、「広がっている」円の効果が表示され、すべての要素の上に表示されます。 最も興味深いのは、同様の効果がリソースを浪費せず、大量のコードを必要としないことです。 信じられない? -デモを見て、jarに埋め込まれたソースを見てください。

ちなみに、このトピックにはかなり興味深いライブラリがあり、追加のスクロール機能といくつかの他の利点を提供しています-JXLayer( オフサイト )( 説明#1 説明#2 説明#3 )。 残念ながら、Javaサイトでホストされているプロジェクトは現在最適な状態ではないため、個々のリソースを参照する必要があります。

そこで、この章ですでに説明したことをすべて統合し、最終的に何かを実行します。 たとえば、ウィンドウ内にコンポーネントがあるwithパネルを表示します。

パネルをラベルの後ろにドラッグすると、ドラッグの最後にパネルが配置される正確な場所を示すパネルの半透明のコピーが表示されます。 ESCキーを使用して移動をキャンセルすることもできます。
ここで実際の例とソースコードを取得できます

もちろん、この特定の機能を実装するには、DnDを使用するだけの価値はありません。もっと短い方法があります。 ただし、それはすべて状況に依存します。 このオプションを使用すると、ウィンドウ内の他のコンポーネントとは無関係にドラッグアンドドロップを描画できます。 それに基づいて、たとえば、アプリケーションウィンドウ間にドラッグパネルを実装することもできます。

AWTUtilities


JDK6には7kiからのいくつかの将来のイノベーションが長い間含まれているため、それらを無視することはできません。彼らの助けがあれば、はるかに少ない労力で多くのことができるからです。
したがって、AWTUtilitiesのいくつかのメソッドに興味があります。
  1. AWTUtilities.setWindowShape(ウィンドウ、形状)-任意のウィンドウを特定の形状に設定できます(円またはトリッキーな多角形)。 フォームを正しく設定するには、ウィンドウをネイティブスタイル(setUndecorated(true))で装飾しないでください。
  2. AWTUtilities.setWindowOpacity(ウィンドウ、フロート)-ウィンドウの透明度を0(完全に透明)から1(不透明)に設定できます。 ウィンドウはネイティブスタイルで装飾できます。
  3. AWTUtilities.setWindowOpaque(ウィンドウ、ブール値)-背景表示とウィンドウ装飾を完全に非表示にすることができますが、その上に配置されたコンポーネントはすべて表示されます。 このパラメーターを正しく設定するには、ウィンドウと段落1をネイティブスタイルで装飾しないでください。


これにより何が得られますか? 実際、かなり広い範囲の可能性。 アプリケーションのウィンドウは、必要なトリッキーなフォームに設定したり、アプリケーションの中央に穴を開けたり、ウィンドウのカスタムシャドウを作成したり、見栄えの良いポップアップを作成したりできます。

詳細に移る場合、ウィンドウに与えられたフォームは厳密にエッジの周りでトリミングされ、あまり見栄えが良くないため、実際にはsetWindowShapeを使用しません。 SetWindowOpaqueが役立ちます-ウィンドウのデザインと背景を非表示にすることで、カスタムレンダリングされた背景を持つコンテナを使用して、任意のウィンドウを作成できます。 使用の小さな例を示します(この記事の前の章のいくつかのトリックも使用します)。

ここでは、ソースコードを使用して作業用のjarファイルを取得できます。 正直なところ、この例では10分しか費やしていません(このうち、約5分でダイアログ内の要素をどのように配置するのか疑問に思いました:)。 当然、これはこれらの新しい機会を採用するためのオプションの1つにすぎません-実際、さらに多くの選択肢があります。

AWTUtilitiesを使用する際の唯一の不快なことは、Linuxシステムでの不安定な作業です。 つまり どこでもなく、ウィンドウの透明度を常に正しく満たすわけではありません。 これが現在のJDKまたはOSに問題があるかどうかはわかりません。

インタラクティブコンポーネントの作成


アプリケーションのインターフェイス用のコンポーネント、UI、およびいくつかの「ツイスト」の作成方法についてはすでに表面的に説明しましたが、コンポーネントに機能部分を追加したり、機能とスタイルを使用して独自の完全に新しいコンポーネントを作成する必要がある場合はどうすればよいですか? 標準コンポーネントを定型化し、新しいコンポーネントの別々の部分にすることは、コンポーネントの1つにわずかな変更を加えても回路全体にかかる場合には、かなり長く退屈な作業です。 このような場合、コンポーネントを「ゼロから」作成する価値があります。

そのため、JComponentをベースとして、ペイントメソッドを使用してそのコンテンツを描画するのが最適です。 実際、JComponent自体は空白のキャンバスであり、レンダリングと既成の標準メソッドsetEnabled / setFont / setForeground / setBackgroundなどの有線の改善がいくつか行われています。 使用方法(およびそれらを使用するかどうか)はあなた次第です。 レンダリングメソッドに追加するものはすべてコンポーネントの一部になり、コンテナに追加すると表示されます。

ところで、余談ですが、コンテナについて話しているので、子孫とJComponent自体はコンテナです。 インストールされているレイアウトコンポーネントに応じて配置される他のコンポーネントを含めることができます。 このコンポーネントにある子コンポーネントのレンダリングはどうなっていますか?また、このコンポーネントのレンダリングとどのように関連していますか? 以前は、Jcomponentのペイントメソッドがどのように構造化され接続されているかについては詳しく説明しませんでしたが、ここで詳細に説明します...

実際、paint()メソッドには、paintComponent、paintBorder、paintChildrenの3つの個別のメソッドの呼び出しが含まれています。 もちろん、彼はさらに、別の領域の印刷や再描画など、いくつかの「特殊な」レンダリングのケースを処理します。 これらの3つのメソッドは、上の画像に示されている順序で常に呼び出されます。 したがって、最初にコンポーネント自体が描画され、次に境界線が上に描画され、次に子コンポーネントが呼び出されます。子コンポーネントはさらにpaint()メソッドなども呼び出します。 当然、不必要なレンダリングを防ぐさまざまな最適化もありますが、これについては後で説明します。

コンポーネントはレンダリングされますが、静的であり、単なるイメージです。 マウスとさまざまなホットキーでそれらを制御する機能を処理する必要があります。
これを行うには、まず適切なリスナー(MouseListener / MouseMotionListener / KeyListener)をコンポーネント自体に追加し、個々のアクションを処理する必要があります。

私の指のすべてを説明しないために、渡されたImageIconを視覚的にサイズ変更できるコンポーネントの例を示します。

ここで、ソースコードを中に含む実例を見ることができます。

このコンポーネントを作成するとき、いくつかの重要なポイントを強調します。
  1. コンポーネントの機能と外観を決定します。この場合、これは、画像が配置された領域、画像の周囲の境界線、および隅にある4つのリサイザーです。 各サイズ変更ツールを使用すると、画像のサイズを変更できます。 また、領域を中心に画像を移動し、中央で「つかむ」こともできます。
  2. コンポーネントが機能するために必要なすべてのパラメーターを決定します。この場合、画像自体とその「参照」ポイント(左上隅と右下隅)です。 また、サイズ変更およびdr画像の実装時に必要となる多くの変数があります。
  3. コンポーネントの空白をラップしています (複数回使用する場合は別のクラスが望ましい) -この場合、ImageResizeComponentクラスを作成し、レンダリングに必要なすべてのパラメーターを定義し、paintComponent()メソッドを再定義し、コンテンツを描画します。 また、getPreferredSize()メソッドを再定義して、コンポーネント自体がその「望ましい」サイズを決定できるようにします。
  4. コンポーネントの機能部分を実装します -この場合、MouseAdapterはサイズ変更と移動を実装するのに十分です。 — — , — . , — mouseMoved() .
, ? «» — , . ( ). , .

, — - .


, Swing – .
  1. / Event Dispatch «» . Event Dispatch :
    SwingUtilities.invokeLater ( new Runnable()
    {
    public void run ()
    {
    //
    }
    } );
    , , listener' (ActionListener/MouseListener .) .
  2. paint- , .
  3. — paint , , setEnabled(enabled). paint . ( ). .
  4. Event Dispatch . SwingUtilities.invokeLater().
  5. , , . /. , Event Dispatch .
  6. repaint() ( repaint(Rectangle) – ), repaint Event Dispatch . revalidate() ( Event Dispatch ). updateUI() ( ), , UI UI LaF'.
  7. LaF UI UI, LaF. LaF - .
«» .
, , /.

まとめ


, . . , , -.

, , — , -. /, . ( ) / — .

«». :

«» .

, ( ), :
.
— .

Source Code Highlighter'y .

Update1: jar
Update2: jar /
Update3: jar

Source: https://habr.com/ru/post/J117430/


All Articles