したがって、プログラムには JPanels の配列、ページ (固定サイズ) があります。それぞれに JTextAreas の配列が含まれています。
ユーザーはキーボードを使用して JTextAreas を追加でき、テキストが追加または削除されたときに自動的にサイズを変更できます (ネイティブの動作、右)。
私が実装する必要がある (そして私がある程度実装した) のは、このイベントを処理し、ページに収まらなくなった JTextAreas を次のページに移動するコードです。十分なスペースがある場合は、次のページからその空きスペースにコンテンツを移動します。全体として、基本的なページ管理に関するものです。
最初の問題の 1 つは、これらの変更が非常に大きく、いくつかの要素を移動する必要があることです。
私がしたことは、各ページの最後にフィラー オブジェクトを追加し、フィラーのサイズが変更されるたびにトリガーされるリスナーをそれに添付し、上記のイベントのいずれかが発生したときにサイズを変更することです。
リスナー コードは、自然にオブジェクトを移動します。
今、私がすべてをこのままにしておくと、1 つの大きな混乱が発生します。上記の条件のいずれかが満たされるたびに、リスナーは 1 つの大きな連鎖反応で至る所でトリガーされます。
さて、これが私がこれまでに思いついたものです(このコードは、要素が縮小または削除された場合のみのものです。追加のコードは十分に似ています。気にする必要はありません)。これは、リスナーの componentResized() メソッド内で呼び出されます。
public void movingElements()
{
//gets the new filler height to compare
int newFillerHeight = getFiller().getHeight();
//chapter contains an array of pages I need to deal with here
Chapter chapter = getChapter();
//element removed/shrunk
//compares the oldFillerHeight which contains the height of the filler
//prior to this particular resizing
else if (newFillerHeight >= oldFillerHeight)
{
//fetches the next and previous page of this page (getPage()
//returns page we are dealing with)
Page previousPage = chapter.getPreviousPage(getPage());
Page nextPage = chapter.getNextPage(getPage());
//here is where it gets tricky
//I didn't want to check if first (few) element(s) can be
//moved to previous page (which can happen if the first, large
//element followed by several small ones is removed) (CHECK A) AND
//if any elements on the next page can be moved to this one
//(CHECK B). What I did instead was to always do the CHECK A.
//if this is the first page of the chapter, I cannot perform the
//CHECK A
if (previousPage == null)
{
//I have to invoke this method on the next page, if it
//exists. If it doesn't, then this is the only page of
//the chapter and I have nothing to do here.
if (nextPage != null)
{
//this is explained bellow this method
nextPage.dummy.setVisible(true);
}
}
//if previous page exists, we preform CHECK A
else
{
Element mover = getElement(1);
//I have to check if the first element on this page fits
//onto the free space of the previous one
//-2 is required to prevent infinite loops
if (mover.getHeight() < previousPage.getFiller().getHeight()-2)
{
//I move the element
removeElement(mover);
previousPage.addElement(mover, previousPage.getElementCount()+1);
//This is a flag that tells that an object was
//moved, you'll se why I need it soon enough
chapter.setMoved(true);
}
//If I can't move the object, I have move onto the next
//page (if it exists) and repeat the process. I also
//check for isMoved flag because maybe nothing was moved
//on the previous page and there is no need to keep the
//this chain of execution going
else if ((nextPage != null) && (chapter.isMoved()))
{
//sets isMoved flag to false so the above code
//would work
chapter.setMoved(false);
nextPage.dummy.setVisible(true);
}
}
}
//saves the new filer height for further use
oldFillerHeight = newFillerHeight;
}
注: Element は、JPanel を拡張するクラスであり、その中に JTextArea があり、高さを指定します。
ダミーの内容は次のとおりです。
dummy = new JPanel();
dummy.setVisible(false);
dummy.addComponentListener(new ComponentAdapter()
{
@Override
public void componentShown(ComponentEvent arg0)
{
dummy.setVisible(false);
movingElements();
}
});
これにより、次に movingElements() が呼び出されたときにすべてが再描画されます。そのレフから直接呼び出すと、フィラーが高さを更新する前に終了し、混乱します。
これが正しい方法かどうかはわかりませんが、最も単純に見えましたが、複雑に見えるかもしれません。
しかし、実行チェーンが完了する前に、このメソッドがリスナーから呼び出されないようにする必要があります。また、これらのうちの 2 つを並行して実行するためにユーザーが何らかの操作を行う必要もないため、チェーンが完了するまでユーザーを完全にブロックしたいと考えています。これはすべて非常に高速に行われますが、それでも...
それで、これは正しい方法ですか、それとも他の方法に頼るべきですか?一度に 1 つのチェーンだけが実行されるようにするにはどうすればよいですか?
編集:
コードのフォーマットについては申し訳ありません。編集ボックスでのタブ移動は非常に見栄えがしますが、表示がめちゃくちゃです...
EDIT2:
私はそれを解決しました、これは私がやったことです:
フィラー コード:
filler.addComponentListener(new ComponentAdapter()
{
@Override
public void componentResized(ComponentEvent arg0)
{
if (!getChapter().isChaining())
{
getChapter().setChaining(true);
movingElements();
}
oldFillerHeight = getFiller().getHeight();
}
});
ダミーコード:
dummy.addComponentListener(new ComponentAdapter()
{
@Override
public void componentShown(ComponentEvent arg0)
{
dummy.setVisible(false);
movingElements();
}
});
movingElements() メソッド:
public void movingElements()
{
int newFillerHeight = getFiller().getHeight();
Document document = getDocument();
Chapter chapter = getChapter();
//element added/enlarged
if (newFillerHeight == 0)
{
Page nextPage = chapter.getNextPage(getPage());
if (nextPage == null)
{
nextPage = new Page();
chapter.addPage(nextPage, chapter.getPageIndex(getPage())+1);
}
Element mover = getPage().getElement(getPage().getElementCount());
removeElement(mover);
nextPage.addElement(mover, 1);
getPage().dummy.setVisible(true);
}
//element removed/shrunk
else if (newFillerHeight >= oldFillerHeight)
{
Page previousPage = chapter.getPreviousPage(getPage());
Page nextPage = chapter.getNextPage(getPage());
if (previousPage == null)
{
if (nextPage != null)
{
nextPage.dummy.setVisible(true);
}
else
{
//chain end
chapter.setChaining(false);
}
}
else
{
Element mover = getElement(1);
if (mover.getHeight() < previousPage.getFiller().getHeight()-2) //-2 is required to prevent infinite loops
{
removeElement(mover);
previousPage.addElement(mover, previousPage.getElementCount()+1);
chapter.setMoved(true);
getPage().dummy.setVisible(true);
}
else if ((nextPage != null) && (chapter.isMoved()))
{
nextPage.dummy.setVisible(true);
}
else
{
//chain end
chapter.setChaining(false);
}
}
}
else
{
//chain end
chapter.setChaining(false);
}
}
そして、これをすべてのページの所有者である章に追加しました。
private AtomicBoolean chaining= new AtomicBoolean(false);
public boolean isChaining()
{
return chaining.get();
}
public void setChaining(boolean chaining)
{
this.chaining.set(chaining);
}
おそらく、この 2 つの方法にキーボード入力ブロッカーとアンブロッカーを追加します。