2016-06-20 6 views
0

数日前、私は単純なボタンを作成し、メソッドを使ってスタイルを変更することでJavaFXでカスタムボタンを作成しましたボタンがクリックされたかどうかに応じて)をパラメータとして使用します。getStyleClass()。add()といくつかのコード行を理解する

カスタマイズボタンを私が使用するたびにインポートできるclassに変換する方法がわかりませんでしたので、私は研究していました。そして、thisプロジェクトには、カスタマイズされたいくつかのJavaFXコントローラが含まれていますマテリアルデザイン。

import com.sun.javafx.scene.control.skin.ButtonSkin; 

import javafx.animation.Animation; 
import javafx.animation.FadeTransition; 
import javafx.animation.Interpolator; 
import javafx.animation.KeyFrame; 
import javafx.animation.KeyValue; 
import javafx.animation.ParallelTransition; 
import javafx.animation.SequentialTransition; 
import javafx.animation.Timeline; 
import javafx.animation.Transition; 
import javafx.beans.binding.DoubleBinding; 
import javafx.beans.value.ObservableValue; 
import javafx.collections.ListChangeListener; 
import javafx.scene.control.Button; 
import javafx.scene.control.Skin; 
import javafx.scene.control.SkinBase; 
import javafx.scene.effect.BlurType; 
import javafx.scene.effect.DropShadow; 
import javafx.scene.input.MouseEvent; 
import javafx.scene.paint.Color; 
import javafx.scene.shape.Circle; 
import javafx.scene.shape.Rectangle; 
import javafx.scene.shape.Shape; 
import javafx.util.Duration; 

@SuppressWarnings("restriction") 
public class CustomButton extends Button { 

    private static final Duration RIPPLE_DURATION = Duration.millis(250); // Duration of the ripple effect 
    private static final Duration SHADOW_DURATION = Duration.millis(350); // Duration of the shadow effect 
    private static final Color RIPPLE_COLOR = Color.web("#FFF", 0.3); // Ripple color 

    public CustomButton() { // Except from the setPrefHeifht() method, everything between this braces seems useless. 
          // Probably I'm wrong, but why would you want to do this? 
     textProperty().addListener((ObservableValue<? extends String> observable, String oldValue, String newValue) -> { 
      if (!oldValue.endsWith(newValue.toUpperCase())) { 
       textProperty().set(newValue.toUpperCase()); 
      } 
     }); 
     setPrefHeight(36); // Height of the button 
    } 

    @Override 
    public Skin<?> createDefaultSkin() { // Overrides the default skin of the button. 
     ButtonSkin buttonSkin = (ButtonSkin) getSkin(); 
     if (buttonSkin == null) { 
      buttonSkin = new ButtonSkin(this); 
      Circle circleRipple = new Circle(0.1, RIPPLE_COLOR); // Creates the circle that must appear when the 
                   // ripple effect animation is displayed. 
      buttonSkin.getChildren().add(0, circleRipple); // Adds the nodes to the screen. 
      setSkin(buttonSkin); 

      createRippleEffect(circleRipple); // Creates the ripple effect animation. 
      getStyleClass().add("ripple-button"); // I don't know what this line does, but if it is 
                // removed, the button is narrowed. 
     } 
     return buttonSkin; 
    } 

    public ButtonSkin getButtonSkin() { // Returns the skin casted to a ButtonSkin. 
     return (ButtonSkin) getSkin(); 
    } 

    public void setFlated(boolean flated) { // The button is "flated" when it's pressed, so I guess that this is the same that saying "clicked". 
     if (flated) { 
      getStyleClass().add("flat"); // I don't know what this does. 
     } else { 
      getStyleClass().remove("flat"); // I don't know what does this do, either. 
     } 
    } 

    public boolean getFlated() { 
     return getStyleClass().indexOf("flat") != -1; // If the style class doesn't contain "flat", it returns false. 
    } 

    public void toggled(boolean toggled) { // For as far as I know, a toggle is the switch from one effect to another. 
     if (toggled) { 
      getStyleClass().add("toggle"); // I know as much about this line as I know about the other "getStyleClass()" lines. 
     } else { 
      getStyleClass().remove("toggle"); // I know as much about this line as I know about the other "getStyleClass()" lines. 
     } 
    } 

    public boolean getToggled() { 
     return getStyleClass().indexOf("toggle") != -1; // If the style class doesn't contain "toggle". it returns false. 
    } 

    private void createRippleEffect(Circle circleRipple) { // Defines the ripple effect animation. 
     Rectangle rippleClip = new Rectangle(); // Creates a new Rectangle 
     rippleClip.widthProperty().bind(widthProperty()); // For as far as I understand, it binds the width property of the 
                  // rippleClip to itself. Why would you do that? 

     rippleClip.heightProperty().bind(heightProperty()); // For as far as I understand, it binds the width property of the 
                  // rippleClip to itself. Why would you do that? 

     circleRipple.setClip(rippleClip); // For as far as I know, clipping is the process that consists 
              // in hiding everything that is outside of a specified area. 
              // What this does is specifying that area so that the parts of the circle 
              // that are outside of the rectangle, can be hided. 
     circleRipple.setOpacity(0.0); // Sets the circle's opacity to 0. 

     /*Fade Transition*/ 
     FadeTransition fadeTransition = new FadeTransition(RIPPLE_DURATION, circleRipple); // Creates the fadeTransition. 
     fadeTransition.setInterpolator(Interpolator.EASE_OUT); 
     fadeTransition.setFromValue(1.0); 
     fadeTransition.setToValue(0.0); 

     /*ScaleTransition*/ 
     final Timeline scaleRippleTimeline = new Timeline(); // Creates the scaleRippleTimeLine Timeline. 
     DoubleBinding circleRippleRadius = new DoubleBinding() { // Binds the radius of the circle to a double value. 
      { 
       bind(heightProperty(), widthProperty()); 
      } 

      @Override 
      protected double computeValue() { 
       return Math.max(heightProperty().get(), widthProperty().get() * 0.45); // Returns the greater of both. 
      } 
     }; 

     // The below line adds a listener to circleRippleRadius. 
     circleRippleRadius.addListener((ObservableValue<? extends Number> observable, Number oldValue, Number newValue) -> { 
      KeyValue scaleValue = new KeyValue(circleRipple.radiusProperty(), newValue, Interpolator.EASE_OUT); 
      KeyFrame scaleFrame = new KeyFrame(RIPPLE_DURATION, scaleValue); 
      scaleRippleTimeline.getKeyFrames().add(scaleFrame); 
     }); 
     /*ShadowTransition*/ 
     Animation animation = new Transition() { // Creates and defines the animation Transition. 
      { 
       setCycleDuration(SHADOW_DURATION); // Sets the duration of "animation". 
       setInterpolator(Interpolator.EASE_OUT); // It sets the EASE_OUT interpolator, 
                 // so that the shadow isn't displayed forever and its an animation. 
      } 

      @Override 
      protected void interpolate(double frac) { 
       setEffect(new DropShadow(BlurType.GAUSSIAN, Color.rgb(0, 0, 0, 0.30), 5 + (10 * frac), 0.10 + ((3 * frac)/10), 0, 2 + (4 * frac))); 
       // Creates a a DropShadow effect and then sets it to "animation". 
      } 
     }; 
     animation.setCycleCount(2); 
     animation.setAutoReverse(true); 

     final SequentialTransition rippleTransition = new SequentialTransition(); // Creates a SequentialTransition. The circle's scaling is the 
                        // first transition to occur, and then the color of the button 
                        // changes to the original one with fadeTransition 
     rippleTransition.getChildren().addAll(
       scaleRippleTimeline, 
       fadeTransition 
     ); 

     final ParallelTransition parallelTransition = new ParallelTransition(); 

     getStyleClass().addListener((ListChangeListener.Change<? extends String> c) -> { // For as far as I understand, each time that the 
                         // Style class changes, the lines of code between the 
                         // braces are executed, but I still don't understand how 
                         // does the Style class work. 
      if (c.getList().indexOf("flat") == -1 && c.getList().indexOf("toggle") == -1) { 
       setMinWidth(88); 
       setEffect(new DropShadow(BlurType.GAUSSIAN, Color.rgb(0, 0, 0, 0.30), 5, 0.10, 0, 2)); 
       parallelTransition.getChildren().addAll(rippleTransition, animation); 
      } else { 

       parallelTransition.getChildren().addAll(rippleTransition); 
       setMinWidth(USE_COMPUTED_SIZE); 
       setEffect(null); 
      } 
     }); 

     this.addEventHandler(MouseEvent.MOUSE_PRESSED, event -> { // When the button is clicked, each object's value is assigned to the first 
                    // that it must have at the beginning of the animation. Then, the animation 
                    // starts. 
      parallelTransition.stop(); 
      circleRipple.setOpacity(0.0); 
      circleRipple.setRadius(0.1); 
      circleRipple.setCenterX(event.getX()); 
      circleRipple.setCenterY(event.getY()); 
      parallelTransition.playFromStart(); 

     }); 
    } 

    public void setRippleColor(Color color) { 
     ((Shape) ((SkinBase) getSkin()).getChildren().get(0)).setFill(color); // I don't understand this line of code. 
    } 

} 

私はJavaFXのにかなり新たなんだとしてだけではなく以来、私は、金鉱山として全体のGitHubのプロジェクトを参照してください。私が今興味を持っていた中で、コントローラは、そのソースコードは以下の通りですMaterialButton、あります他のコントローラからインポートできるクラスとしてカスタムコントローラを作成する方法を示す例にアクセスできるだけでなく、他のいくつかのコントローラをカスタマイズする方法も示しています。

問題は、私が理解できないいくつかのコード行があることです(あなたがソースコード上で作ったコメントを読んでも分かります)。

例として、getStyleClass().add("something")が使用されている回数があります。 getStylesheets().add()の仕組みは分かっていますが、これは異なっています。私は "スタイル"クラスはCSSファイルとは異なると言うでしょう。

この場合、どのように機能しますか?私が理解する限り、getStyleClass().add()メソッドのパラメータとして使用されるStringは、後でif()ステートメントを持つ「スタイル」クラスの内部にあるかどうかを判断するために使用されます。このクラスは正確に何ですか?私はそれについてインターネット上のドキュメントを見ていません。

ソースコードの末尾にあるsetRippleColor()メソッドの理解に問題があります。誰かがそれがどのように機能するか、それを理解するために何を調べるべきかを知っていれば、私はそれを感謝します。

ありがとうございます。

UPDATE:誰かが、ripple-buttonは、GitHubプロジェクトにあるCSSファイルの一部であると指摘しました。 MaterialButtonクラスをコピーして新しいプロジェクトに貼り付けたので、言及するだけでripple-buttonにアクセスすることはできません。それにもかかわらず、このコード行を削除すると、ボタンが絞り込まれることがわかります。私は何かによって "リップルボタン"を変えることができ、結果は同じになるだろうが、ラインはそこになければならない。なぜこれが起こるのですか?

UPDATE 2:基本的には円の皮膚を取得し、それがShapeにキャストだ後、それは四角形の色を変更することができますので、その子を取得します。私はすでにsetRippleColor(Color color)メソッドが何をするのか理解していました。 RectangleShapeに拡張されているため、形状にキャストされています。それは実際にはとても簡単です。

+0

CSSファイルを確認してください。 'ripple-button'クラスのいくつかのスタイル定義が見つかると思います。 'setRippleColor'については、何を理解していないのですか?スキンの最初の子を 'Shape'にキャストし、' setFill(...) 'を呼び出しています。 –

+0

@James_Dそれは私が最初に考えたことですが、CSSファイルはありません。実際には、バットマンでリップルカラーを変更することもできますが、プログラムは完全に動作しますが、 'getStyleClass()。add()'メソッドを削除すると、プログラムボタンが絞り込まれます。 – SpaceCore186

+0

確かにCSSファイルがあります:https://github.com/Kairos-Project/kairos-material-components/blob/master/src/main/resources/org/kairos/components/controls.css –

答えて

1

あなたの混乱を明らかにするいくつかの問題があります。

まずは物事は「コントローラ」ではなく「コントロール」と呼ばれますが、これは分かりやすくするために簡単に混乱する可能性があるためです。

getStyleSheets()メソッドはStringObservableListを返します。このリストには、アプリケーションのスタイルを定義するファイル.cssへのさまざまなパスが含まれています。スタイルはのSceneまたはControlのいずれかに追加されます。詳細についてはリンクのJavaDocまたはこれらの記事を確認してください。

スタイルシートは、コントロールのスタイルを定義します。また、NodegetStyleClass()に設定できる追加のスタイルクラスも用意されています。これはStringObservableListを返します。今回はスタイルクラス名を定義します。レンダリング時に、Node用に定義されたスタイルシートのセットで名前が検索され、適用されます。そのようなスタイルクラスが見つからない場合は無視されます。 Nodeは、任意のコントロールの基本クラスです。

あなたのコメントで述べたように、デフォルトのスキンを上書きするが、それは(CustomButtonSkinが上書きされますButtonを拡張するだけでなく、あなたが部分的に正しい)デフォルトのスキンを定義しませんcreateDefaultSkin()方法。一般的に、コントロールは「コントロール」クラスと「スキン」クラスで構成されていますが、少なくともJavaFXがバージョン8まで変更されたケースがこれに該当します。詳細については、the control architectureの記事を参照してください。

+0

多くのことをもっときれいにしてくれてありがとう。あなたが言及した記事を読んでいます。 – SpaceCore186