
ブレーメン音楽隊をきっかけに
おそらく多くの人が覚えているように、前世紀(
Windows 2000の時代)に、すべての種類の
スプラッシュスクリーンとミニアプリケーションを非長方形のウィンドウ(および異常なコントロール)で作成することは流行でした。
これらのポンテは、いわゆる
WinAPIを使用して
C \
C ++で記述されています。 地域。 これはそれほど単純ではありませんでした。なぜなら、横柱やウィンドウ、言語につまずく必要があるだけでなく、レンダリングのためのポリゴンの計算ミスも恐ろしかったからです。 したがって、1つまたは2つの丸いウィンドウを「描画」して、このトピックを長い間脇に置いていました。
そして今週の月曜日、
「不規則な形状の窓」という記事が点滅し、このトピックに再び注目を集めました。 この目的のために
.NETに
WinAPIラッパー関数が実装されていることを期待して、外部関数の説明を見ることに失望しました。 そしてここで、私は、主に
Javaのプログラマーとして、当時
Sunが任意の形状のウィンドウをレンダリングするための関数を導入することを約束していたことを思い出しました。
一杯のコーヒー
さて、
Sun /
Oracleはどのような機会を与えてくれましたか?
- JNI / JNA :
- プラットフォーム依存
- システムごとに追加のネイティブコードを記述する
- 背景を描画して透明度をエミュレートする:
- ウィンドウはまだクリック可能です
- レンダリングが遅い
- ウィンドウを移動する際の顕著な遅延
- 背景の変化に即座に反応しない(特に、背景のアニメーションの問題)
- クラスcom.sun.awt.AWTUtilities :
- Sun VMのバージョン1.6_10からのみ利用可能
- サポートが保証されていない内部クラス
- クラスjava.awt.Window 、 java.awt.GraphicsEnvironment 、 java.awt.GraphicsConfigurationのパブリックメソッド:
- バージョン1.7.0からご利用いただけます。
- 迷惑です: OpenJDKはどうですか?
現時点では、最も受け入れられるのは最後から2番目の方法です。 したがって、私たちが持っているタスクはこれです:
複雑な非長方形の半透明ウィンドウを(画像に基づいて)描画し、背景画像とともに、ウィンドウの移動、ウィンドウのサイズ変更、コントロールの配置(標準および任意の形状の両方)を行うことができますリージョン、エリア、エッジ
スモーク、スモークのマニュアル、チュートリアルを行った結果、
java.awt.Shapeインターフェースを実装するクラスが、他の幾何学的形状と同様にウィンドウの形状を担当することが明らかになりました。 つまり、Windowsの領域と同様に、画像から閉じた
Shapeを生成できれば、問題は解決します。
ただし、確立されたクロスプラットフォームの伝統に従って、OS自体がそのようなウィンドウを描画できることを確認する必要があります。
AWTUtilitiesを使用して、たとえば次のコードを記述します。
if((com.sun.awt.AWTUtilities.isTranslucencySupported (com.sun.awt.AWTUtilities.Translucency.PERPIXEL_TRANSLUCENT)) && (com.sun.awt.AWTUtilities.isTranslucencyCapable (GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration()))) {
PERPIXEL_TRANSLUCENTは、グラフィックシステムが完全に透明なウィンドウを表示できるだけでなく、おおまかに言って、ウィンドウに「穴がある」ことを意味します。また、現在のデバイスは一般的に透明(ALチェックはしませんが、これらの関数を使用しようとすると、対応する例外が飛び出します。
それでも、システムに古いJavaがインストールされており、
AWTUtilitiesがまったくないことが
判明する場合があります。
この記事の補助クラス
AWTUtilitiesWrapperを使用すると、
AWTUtilitiesをリフレクションで使用できます。 誰もがJavaを更新した可能性が十分にあり、コードが理解しにくくなるため、私はそれを使用しませんでした。
AWTUtilitiesと直接
連携します:
- ウィンドウを表示する前に、タイトルバーを削除します(標準のOS装飾を無効にします)。
this.setUndecorated(true);
- ウィンドウを半透明にする機能を要求します。
com.sun.awt.AWTUtilities.setWindowOpaque(this, false);
- ウィンドウの透明度を設定します。
com.sun.awt.AWTUtilities.setWindowOpacity(this, .75);
2番目の引数の値は0から1の間でなければなりません(
ケース75%)。
ウィンドウを完全に透明にすることもできますが、応答します
押す - 目的のShapeを構築したら 、ウィンドウに必要な形状を設定します。
com.sun.awt.AWTUtilities.setWindowShape(this, s);
今、魔法のように、私たちの姿を超えているすべてのものではありません
描画されないだけでなく、クリックにも反応しません(覚えておいてください
本「 スイングハック 」からのアプローチ ) - 標準のタイトルバーは失われましたが、ウィンドウを移動したいので、
ウィンドウ内の任意の場所をクリックしてウィンドウを移動する機能を追加します(このため
同じ本のMoveMouseListenerクラスを使用しました) - また、非標準の「閉じる」ボタンをウィンドウに追加します
これらはフォームです!
次のタスク:背景が透明で、前景から十分に分離された画像から、閉ループを作成します。 私の倒錯した拷問のために、私はこのかなり若い女性をグーグルで検索しました:

最初は、
凸包法を実装することを考えましたが、その後拒否しました-私たちの若い女性は非常に凸であるだけでなく、いくつかの場所でも凸です。 夕方、頭を悩ますだけでは十分ではありませんでした。ただし、「額」ソリューションを使用する場合を除き
ます 。
最初の長方形から透明なピクセルを削除して、ピクセルを調べます。 結果は次のコードです。
static Shape contour(final BufferedImage i) { final int w = i.getWidth(); final int h = i.getHeight(); final Area s = new Area(new Rectangle(w, h)); final Rectangle r = new Rectangle(0, 0, 1, 1); for (ry = 0; ry < h; r.y++) { System.out.println(ry + "/" + h); for (rx = 0; rx < w; r.x++) { if ((i.getRGB(rx, ry) & 0xFF000000) != 0xFF000000) { s.subtract(new Area( r )); } } } return s; }
ここでは、半透明ピクセルも半透明と見なされます(つまり、
アルファ <255)-もちろん、必要に応じて、特定のしきい値を指定したり、任意の色、たとえば白:0xFFFFFFを割り当てたりすることもできます。 しかし、それは重要ではありません。他の何かが重要であることが判明しました-速度。 はい。フォームを毎回生成する必要はありません。どこかに保存しておくと、すぐにダウンロードして表示できます。
Rテープロードエラー
Javaには、
ava.io.Serializableインターフェイスを実装し、再帰的にシリアル化可能なプリミティブとオブジェクトを保存およびロードするための組み込みツールがあります。 しかし問題は、Shapeもその実装クラスもシリアル化できないことです。 長い間苦労してきたので、輪郭を迂回して保存できる
java.awt.geom.PathIteratorインターフェースと、以前に保存した輪郭を書き込むことができる
java.awt.geom.GeneralPathクラスを取得しました。 そして、ここで何が起こったのです:
static void save(final Shape s, final DataOutput os) throws IOException { final PathIterator pi = s.getPathIterator(null); os.writeInt(pi.getWindingRule()); System.out.println(pi.getWindingRule()); while (!pi.isDone()) { final double[] coords = new double[6]; final int type = pi.currentSegment(coords); os.writeInt(type); System.out.println(type); for (final double coord : coords) { os.writeDouble(coord); System.out.println(coord); } System.out.println(""); pi.next(); } }
java.io.DataOutputインターフェースを使用すると、データをどこにでも保存できます。これを実装するまれなクラス
java.io.RandomAccessFileを使用しましたが、
java.io.ObjectOutputStreamで記述できます。 クラスファイルの横にファイルを保存することができます(さらに良いことです)。これにより、後でアプレットを使用してアーカイブからファイルを取り出すこともできます。
static Shape load(final DataInput is) throws IOException { final GeneralPath gp = new GeneralPath(is.readInt()); final double[] data = new double[6]; CYC: while (true) { final int type = is.readInt(); for (int i = 0; i < data.length; i++) { data[i] = is.readDouble(); } switch (type) { case PathIterator.SEG_MOVETO: gp.moveTo(data[0], data[1]); break; case PathIterator.SEG_LINETO: gp.lineTo(data[0], data[1]); break; case PathIterator.SEG_QUADTO: gp.quadTo(data[0], data[1], data[2], data[3]); break; case PathIterator.SEG_CUBICTO: gp.curveTo(data[0], data[1], data[2], data[3], data[4], data[5]); break; case PathIterator.SEG_CLOSE: break CYC; } } return gp.createTransformedShape(null); }
もちろん、正しい回復には正しいファイルが必要です-簡単にするために、私はチェックを行わず、例外は処理されません(ただし、一般的には必要です!)。 ファイル形式は次のとおりです。
- 輪郭バイパス方向:時計回りまたは反時計回り(整数)
- 任意の数のブロック:
- 曲線タイプ(整数)
- 曲線のアンカーポイント(6個の分数の配列)
さらに、
ペイント(Graphics g)メソッド、またはむしろ
paintComponent(Graphics g)メソッドをオーバーロードする
ことにより、完全に標準的な方法で背景画像を描画します。
すべてまたは何もない
まあ、自分でウィンドウを描画する場合は、完全を期すために、独自のコントロールも描画する必要があります。 今のところ、[閉じる]ボタンに限定します。
- ネイティブSWTコントロール:
- スイング制御拡張:
- 各要素のクラスを書く
- 外観の難しい変更(それによる)
- 独自のルックアンドフィールの作成 :
- javax.swing.plaf.LayerUIを使用した Swingコントロールの装飾
デモンストレーションを行うために、2番目の方法を使用しました。
- ウィンドウと同じ方法で画像と輪郭を生成します
- JButtonクラスの拡張
- 透明にする:
this.setOpaque(false);
- バックグラウンドレンダリングを無効にします。
this.setContentAreaFilled(false);
- また、フレームやフォーカスラインを描画しません。
this.setFocusPainted(false); this.setBorderPainted(false);
- 明示的な寸法を設定して、レイアウトマネージャーを使用しない
( LayoutManager )画像を台無しにしませんでした:
this.setSize(this.result.size); this.setMinimumSize(this.result.size); this.setMaximumSize(this.result.size);
- 絵を描くためのpaintComponent(Graphics g)メソッドの置き換え
- 同じメソッドをパスに委任することにより、contains(int x、int y)メソッドを置き換えます
Finita la Comedia
それだけです! 最後の仕上げを行います。標準的な方法で、ウィンドウにボタンを追加し、ハンドラーをその上に掛けます。 私たちは立ち上げて賞賛します:

私の多くのプロジェクトと同様に、私はそれらを
xp-dev.comに保存します:
プロジェクトページ 、
svnリポジトリEclipse Heliosには 、Java6用のShapedとJava7用のShaped7の2つのプロジェクトがあります。 バージョンの違いを減らすために、特定の要求はクラスメソッドで発行され、補助関数は別のユーティリティクラスで発行されました。
続きを読む: