2013-04-28 2 views
0

collapsable (또는 foldable) 문장을 만들고 싶습니다. 중요하게는 단락에 삽입해야하고 나머지 텍스트와 함께 흐르게해야합니다. Java/Swing에서 JEditorpaneDefaultStyledDocument의 확장자로 작업하고 있습니다. 예를 들어 :자바 스윙의 단락 내의 접을 수있는 문장 jeditorpane

[-]이 문장
하나입니다. [-] 이것은
문장 2입니다. [-] 이것은
문장 3입니다. 두 문장이 축소되면

, 그것은된다 :

[이 -]이 문장
이다. [+] [-] 이것은
문장 3입니다. webpage

문서의 영역을 축소하는 방법의 일례를 갖고 있지만,이 영역은 단락에 포함되지 않는다. 내 문제는 이것을 기반으로 어떤 뷰를 결정하려고합니다.

  • LabelView와의 확장으로 SentenceView 만들기 : 문제는 LabelView와는 다른 스타일 속성 부분을 지원하지 않는다는 것입니다. 어쩌면 이것을 추가 할 수 있지만 대부분의 뷰는 CompositeView를 기반으로합니다.
  • SentenceView를 의 확장자로 작성하십시오. BoxView : 어쩌면, 어쩌면이 텍스트를 텍스트와 함께 올바르게 전달되는 방식으로 ParagraphView에 삽입하는 방법을 알 수 없습니다.
  • 확장 ParagraphView : 어디에서 문장을 시작하고 끝내고 모든 축소를 처리하는지 이해하는 일종의 슈퍼 인식 ParagraphView를 만들 수 있습니다. 아마도이 작업을 수행 할 수는 있지만, 이는 거의 틀림없이 잘못된 방식입니다.

누군가가 나에게 포인터 (또는 직감)를 줄 수 있다면 SentenceView를 기반으로 (또는 다른 팁이 오랫동안 도움이 될 수 있음) 나는 그것을 매우 높이 평가할 수 있습니다.

답변

0

나는 내 자신의 질문에 답하려고 노력할 것이다. 표준 Java Swing 클래스에 대한 몇 가지 코드를 검토 한 후에 필자는 필요한 기능 대부분을 FlowViewParagraphView에 구현하기로 결정했습니다. 특히, 메서드는 FlowStrategy이며 중단 점을 찾고 텍스트 행을 전달하는 역할을합니다. 내 SentenceView에서 나는 더 많거나 적게 똑같이해야만합니다. 유일한 차이점은 행을 직접 레이아웃하고 싶지 않다는 것입니다. 대신 내 SentenceViewbreakView을 구현해야 문장을 포함하는 단락이 문장을 잘라내어 배치 할 수 있습니다. 불행히도, 나는 함수 layoutRow을 재사용 할 수있는 방법을 찾을 수 없으며, 나는 그 코드의 정당한 비트를 복사/붙여 넣기를 끝내었다.

내 (접을 수있는) SentenceView을 구현하는 동안 많은 Java 7 버그가 발생했습니다. 예를 들어 Java 7 line wrapping bug을 참조하십시오.또한 minimumSize가 스페이스 (좋은 구분 가중치) 또는 문자 (좋은 구분 가중치)의 중단 점을 기반으로하는지 여부는 Java에서 모호한 것으로 보입니다. GlyphViewLabelView은 우수한 구분 가중치를 기준으로 minimumSize를 제공하지만 ParagraphViewBadBreakWeight보다 더 좋은 모든 중단 점과 혼합합니다 (calculateMinorAxisRequirements 코드 참조). 어느 것이 다소 혼란 스럽습니다.

어쨌든 SentenceView의 구현에서 가장 중요한 부분을 강조하겠습니다. 그것이 약간 고심하고 내가 고군분투했던 부분이 아니기 때문에 저는 실제 접을 수있는 부분을 생략 할 것입니다. breakView 기능에 중점을 두겠습니다.

public class SentenceView extends BoxView { 
    boolean containsStart; 
    boolean containsEnd; 
    public SentenceView(Element elem) { 
     super(elem, View.X_AXIS); 
     containsStart = true; 
     containsEnd = true; 
    } 
    ... 

는 또한 containsEnd에 대한 containsStartisEnd에 대한 게터 isStart 있습니다. 내가 말했듯이 breakView 방법은 주로 복사 :

public View breakView(int axis, int p0, float pos, float len) { 
    if (axis == View.Y_AXIS) return this; 
    if (p0 == getStartOffset() && len >= getPreferredSpan(axis)) return this; 
    float spanLeft = len; 

    int p = p0; 
    float x = pos; 
    int end = getEndOffset(); 
    Row row = new Row(getElement()); 
    TabExpander te = (this instanceof TabExpander) ? (TabExpander)this : null; 
    int breakWeight = BadBreakWeight; 
    float breakX = 0f; 
    float breakSpan = 0f; 
    int breakIndex = -1; 
    int n = 0; 

    if (p0 == getStartOffset() && isStart()) row.setContainsStart(true); 

    Vector<View> viewBuffer = new Vector<View>(10, 10); 
    while (p < end && spanLeft >= 0) { 
     View v = createView(p); 
     if (v == null) break; 

     int bw = v.getBreakWeight(axis, x, spanLeft); 
     if (bw >= ForcedBreakWeight) { 
      View w = v.breakView(axis, p, x, spanLeft); 
      if (w != null) { 
       viewBuffer.add(w); 
      } else if (n == 0) { 
       viewBuffer.add(v); 
      } 
      break; 
     } else if (bw >= breakWeight && bw > BadBreakWeight) { 
      breakWeight = bw; 
      breakX = x; 
      breakSpan = spanLeft; 
      breakIndex = n; 
     } 

     float chunkSpan; 
     if (v instanceof TabableView) { 
       chunkSpan = ((TabableView)v).getTabbedSpan(x, te); 
      } else { 
       chunkSpan = v.getPreferredSpan(axis); 
      } 
     if (isEnd() && v.getEndOffset() == end) { 
      if ((chunkSpan <= spanLeft) && (chunkSpan > spanLeft)) { 
       spanLeft = chunkSpan - 1; 
      } 
     } 
     if (chunkSpan > spanLeft && breakIndex >= 0) { 

       if (breakIndex < n) { 
        v = viewBuffer.get(breakIndex); 
       } 
       for (int i = n - 1; i >= breakIndex; i--) { 
        viewBuffer.remove(i); 
       } 

       v = v.breakView(axis, v.getStartOffset(), breakX, breakSpan); 
     } 

     spanLeft -= chunkSpan; 
     x += chunkSpan; 
     viewBuffer.add(v); 
     p = v.getEndOffset(); 
     n++; 
    } 
    // cloning: 
    Vector<View> viewBuffer2 = new Vector<>(); 
    for (int j = 0; j < viewBuffer.size(); j++) { 

     View v = viewBuffer.get(j); 
     if (v instanceof MyLabelView) { 
      v = (View) ((MyLabelView)v).createClone(); 
     } else { 
      throw new RuntimeException("SentenceView can only contain MyLabelView"); 
     } 
     viewBuffer2.add(v); 
    } 
    View[] views = new View[viewBuffer2.size()]; 
     viewBuffer2.toArray(views); 
     row.replace(0, row.getViewCount(), views); 
     return row; 
    }  

여기에서주의해야 할 몇 가지가 있습니다. 먼저 breakView은 자체 또는 SentenceView.Row을 반환합니다. 둘째로, LabelView 대신에 MyLabelView을 사용하고 있습니다. 주로 앞서 언급 한 줄 바꿈 버그 때문입니다. 나는 여전히 Java와 Java Swing에 익숙하지 않고 Cloneable 인터페이스와 조금 어려움을 겪었으며 clone은 보호되고 최종 버전입니다. 그래서 createClone을 구현하여 clone으로 전화했습니다. 별로 우아하지는 않지만 내 주된 걱정도 아닙니다. 서브 뷰는 에 추가 될 때 부모가되기 때문에 필요한 복제입니다. 그러나 나중 단계에서 원래 SentenceView이 다시 표시되면 (더 이상 분리가 필요 없기 때문에) 문제가 발생합니다. 더 좋은 방법은 this에 모든 하위 뷰를 다시 작성하는 것이지만 가장 잘 할 수있는 부분을 파악할 수는 없을 것입니다. 나는 이것에 대한 코멘트를 환영 할 것이다. 또한 실제로 접을 수있는 기능을 구현하기 위해 SentenceView은 접힌 상태를 생성 된 Rows과 공유해야합니다. 나는 객체에 부울을 래핑 한 다음 객체를 공유함으로써 그렇게했습니다.

코드없이 다음 방법을 제공 할 것입니다. 너무 어렵지 않으므로 ParagraphView에서 복사/수정 될 수 있습니다.

protected SizeRequirements calculateMajorAxisRequirements(int axis, 
               SizeRequirements r) 
protected SizeRequirements calculateMinorAxisRequirements(int axis, SizeRequirements r) 
public View createFragment(int p0, int p1) 
protected View createView(int startOffset) 

protected void layoutMinorAxis(int targetSpan, int axis, int[] offsets, int[] spans) { 
    baselineLayout(targetSpan, axis, offsets, spans); 
} 

그리고, 마지막으로, 내부 클래스가 : 작동

class Row extends SentenceView { 
    Row(Element elem) { 
     super(elem); 
     containsStart = false; 
     containsEnd = false; 
    } 
    public float getAlignment(int axis) { 
     if (axis == View.X_AXIS) return 0; 
     return super.getAlignment(axis); 
    } 

    public AttributeSet getAttributes() { 
     View p = getParent(); 
     return (p != null) ? p.getAttributes() : null; 
    } 
    protected int getViewIndexAtPosition(int pos) { 
     if(pos < getStartOffset() || pos >= getEndOffset()) 
      return -1; 
     for(int counter = getViewCount() - 1; counter >= 0; counter--) { 
      View v = getView(counter); 
      if(pos >= v.getStartOffset() && 
       pos < v.getEndOffset()) { 
       return counter; 
      } 
     } 
     return -1; 
    } 
    protected void loadChildren(ViewFactory f) { 
    } 
} 

은. 그것은 약간의 불행한 일이지만, 기존의 코드를 꽤 많이 복제했지만, X 축에 breakView을 구현하는 방법이나 paragraphView에 다른 방법을 찾는 방법이 분명하지 않습니다. 또한 접을 수있는 기능을 구현하려면 조금 더 많은 작업을 수행해야합니다. paint 메서드는 앞에 [-]를 그리기 위해 변경해야합니다 (containsStart이 참일 경우). minimumSizepreferredSize은이 여분의 공간 비트를 고려해야합니다 (필자는 인세 트를 사용하고 있습니다). SentenceViewRows은 접힌 상태를 공유해야합니다. 보기가 접히면 minimumSizepreferredSize은 [+] 표식의 크기와 같습니다. viewToModel, modelToView, getNextEastWestVisualPositionFrom으로 둘러보실 수 있습니다. 이것은 모두 Folding (collapsible) area in the JEditorPane/JTextPane과 유사합니다. 내 코드에서 조금 더 맘에 드는 유일한 점은 마우스 수신기에서 SentenceView 내부에서 이벤트가 발생했는지 여부를 확인하는 부분인데 viewToModel을 사용하지 않는 것이 좋습니다.그럼, 저도 알려 드리겠습니다 :

MouseListener sentenceCollapse = new MouseAdapter() { 
    public void mouseClicked(MouseEvent e) { 
     JEditorPane src = (JEditorPane)e.getSource(); 
     View v = src.getUI().getRootView(src); 
     Insets ins = src.getInsets(); 
     int x = ins.left; 
     int y = ins.top; 
     Point p = e.getPoint(); 
     p.translate(-x, -y); 
     Rectangle alloc = new Rectangle(0,0,Short.MAX_VALUE,Short.MAX_VALUE); 

     while (v != null && !(v instanceof SentenceView)) { 
      View vChild = null; 
      int n = v.getViewCount(); 
      for (int i = 0; i < n; i++) { 
       Rectangle r = v.getChildAllocation(i, alloc).getBounds(); 

       if (r.contains(p)) { 
        vChild = v.getView(i); 
        alloc = (Rectangle) r.clone(); 
       } 
      } 
      v = vChild; 
     } 
     if (v != null && v instanceof SentenceView) { 
     <<< unfold or fold the Sentence View >>> 
     src.repaint(); 
    } 
}; 

희망이 있습니다.