나는 내 자신의 질문에 답하려고 노력할 것이다. 표준 Java Swing 클래스에 대한 몇 가지 코드를 검토 한 후에 필자는 필요한 기능 대부분을 FlowView
및 ParagraphView
에 구현하기로 결정했습니다. 특히, 메서드는 FlowStrategy
이며 중단 점을 찾고 텍스트 행을 전달하는 역할을합니다. 내 SentenceView
에서 나는 더 많거나 적게 똑같이해야만합니다. 유일한 차이점은 행을 직접 레이아웃하고 싶지 않다는 것입니다. 대신 내 SentenceView
은 breakView
을 구현해야 문장을 포함하는 단락이 문장을 잘라내어 배치 할 수 있습니다. 불행히도, 나는 함수 layoutRow
을 재사용 할 수있는 방법을 찾을 수 없으며, 나는 그 코드의 정당한 비트를 복사/붙여 넣기를 끝내었다.
내 (접을 수있는) SentenceView
을 구현하는 동안 많은 Java 7 버그가 발생했습니다. 예를 들어 Java 7 line wrapping bug을 참조하십시오.또한 minimumSize가 스페이스 (좋은 구분 가중치) 또는 문자 (좋은 구분 가중치)의 중단 점을 기반으로하는지 여부는 Java에서 모호한 것으로 보입니다. GlyphView
및 LabelView
은 우수한 구분 가중치를 기준으로 minimumSize를 제공하지만 ParagraphView
은 BadBreakWeight
보다 더 좋은 모든 중단 점과 혼합합니다 (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
에 대한 containsStart
및 isEnd
에 대한 게터 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
이 참일 경우). minimumSize
및 preferredSize
은이 여분의 공간 비트를 고려해야합니다 (필자는 인세 트를 사용하고 있습니다). SentenceView
및 Rows
은 접힌 상태를 공유해야합니다. 보기가 접히면 minimumSize
과 preferredSize
은 [+] 표식의 크기와 같습니다. 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();
}
};
희망이 있습니다.