Javaに関する様々な情報をご紹介します。

Javaに関する様々な情報をご紹介します。
評価

0

JViewport#setViewPositionメソッドでスクロールができない。

HTMLドキュメントを表示する簡易ブラウザを作成しています。
下記がその簡易ブラウザとHTMLドキュメントのソースコードです。

同じドキュメント内のリンク先からリンク元(ビューポート位置)へは戻る事ができるのですが、違うドキュメントファイルからリンク元へ戻る場合、なせかリンク元のビューポートを無視して戻るべきドキュメントの先頭に戻ってしまいます。
違うドキュメントファイルからでもリンク元のビューポート位置に戻る方法を探しています。
どなたか、わかる方、お知恵をお貸しください。

OS: WindowsXP sp3
JDK Version: 1.5.0_06

htmlPane.java
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.util.*;
import javax.swing.*;
import javax.swing.event.*;

class HtmlPane extends JFrame {

    private static final int WIDTH = 600;
    private static final int HEIGHT = 200;

    public HtmlPane() {
        super("HtmlPane");
        setSize(WIDTH, HEIGHT);

        final Stack stack = new Stack();
        final JEditorPane html = new JEditorPane();
        JScrollPane scrollPane = new JScrollPane(html);
        final JViewport view = scrollPane.getViewport();
        final JTextField url = new JTextField(15);
        url.setText("file:test1.html");
        html.setEditable(false);

        // ハイパーリンクの処理
        html.addHyperlinkListener(new HyperlinkListener() {
            public void hyperlinkUpdate(HyperlinkEvent he) {
                if (he.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
                    try {
                        Page page1 = new Page(url.getText(),
                                view.getViewPosition());
                        stack.push(page1);
                        checkStack(stack);

                        url.setText(he.getURL().toString());
                        html.setPage(he.getURL());

                        Page page2 = new Page(he.getURL().toString(),
                                view.getViewPosition());
                        stack.push(page2);
                        checkStack(stack);

                    } catch (IOException ie) {
                        ie.printStackTrace();
                    }
                }
            }
        });

         // 進むボタンの処理
         ActionListener listener = new ActionListener() {
             public void actionPerformed(ActionEvent ae) {  
                 try {
                     if (html.getPage() != null) {
                         Page page1 = new Page(html.getPage().toString(),
                                 view.getViewPosition());
                         stack.push(page1);
                         checkStack(stack);
                     }

                     html.setPage(url.getText());

                     Page page2 = new Page(url.getText(),
                             view.getViewPosition());
                     stack.push(page2);
                     checkStack(stack);
                 } catch (IOException ie) {  
                     ie.printStackTrace();
                 }
             }
         };
   
         JButton loadButton = new JButton("進む");
         loadButton.addActionListener(listener);
         url.addActionListener(listener);

         // 戻るボタンの処理
         JButton backButton = new JButton("戻る");
         backButton.addActionListener(new ActionListener() {
             public void actionPerformed(ActionEvent ae) {
                 if (stack.size() > 1) {
                     try {

                         checkStack(stack);
                         stack.pop();
                         checkStack(stack);

                         // 前のページを表示する
                         String urlString = ((Page) stack.peek()).getURL();
                         url.setText(urlString);
                         html.setPage(urlString);
                         view.setViewPosition(((Page) stack.peek()).getPoint());
                     } catch (IOException ie) {  
                         html.setText("例外: " + ie);
                     }
                 }
             }
         });

         // パネルにコンポーネントを追加する
         JPanel panel = new JPanel();
         panel.add(new JLabel("URL"));
         panel.add(url);
         panel.add(loadButton);
         panel.add(backButton);

         // コンテナにパネルを追加する
         Container contentPane = getContentPane();
         contentPane.add(panel, BorderLayout.NORTH);
         contentPane.add(scrollPane, BorderLayout.CENTER);
    }

    // スタックの中を表示する(デバッグ用)
    private static void checkStack(Stack stack) {
        for (int i = 0; i < stack.size(); i++) {
            System.out.print("URL = " + ((Page) stack.get(i)).getURL());
            System.out.println(" : Point = "
                    + ((Page) stack.get(i)).getPoint());
        }
        System.out.println("Stack Size = " + stack.size());
    }

    public static void main(String[] args) {
        JFrame frame = new HtmlPane();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }
}

class Page {

    private String url;
    private Point point;

    public String getURL() {
        return url;
    }

    public Point getPoint() {
        return point;
    }

    public Page(String url, Point point) {
        this.url = url;
        this.point = point;
    }
}

test1.html
<html>
 <head>
  <title>test1</title>
 </head>
 <body>
  test1.htmlです。<br>
  <a name="anchor1">test1のanchor1です。</a><br>
<br>
<br>
<br>
<br>
<br>
  <a href="test2.html#anchor1">link1(test2のanchor1へ)</a><br>
<br>
<br>
<br>
  <a href="test1.html#anchor2">link2(test1のanchor2へ)</a>
<br>
<br>
<br>
  <a name="anchor2">test1のanchor2です。</a>
<br>
<br>
<br>
 </body>
</html>

test2.html
<html>
 <head>
  <title>test2</title>
 </head>
 <body>
  test2.htmlです。<br>
  <a name="anchor1">test2のanchor1です。</a><br>
<br>
<br>
<br>
  <a href="test1.html#anchor2">link1(test1のanchor2へ)</a><br>
<br>
<br>
<br>
  <a href="test2.html#anchor2">link2(test2のanchor2へ)</a>
<br>
<br>
<br>
  <a name="anchor2">test2のanchor2です。</a>
<br>
<br>
<br>
 </body>
</html>

4

回答

5009

閲覧

4件の回答

評価

0

補足です。

ファイル名は htmlPane.java ではなく、HtmlPane.javaの間違いでした。
あるアプリケーションのマニュアルを表示するためにそのアプリケーションから開くことができる簡易ブラウザを作っています。
素直に Desktop クラス(バージョン1.6から)を使えばリンク関係の問題は解決するのですが、OS依存を避けたいためとバージョンの関係上、この方法以外の方法を探しています。

評価

0

ちょっとこんな長いのを読むのは大変なので、可能性のある原因を、

JEditorPane の中身を設定し直してから実際に読み込まれるまでには
時間がかかりますので、その前に表示位置を移動することは出来ません。
setPage は中身の指定であり、中身の読み込みが終わる前に戻ってきます。

評価

0

仙人様

迅速な回答、本当にありがとうございます。
setPage メソッドの後に Thread.sleep メソッドで50ミリ秒ほどウェイトを指定したら、解決しました。
(参考までに30ミリ、20ミリ、10ミリ秒と挑戦しましたが、うちのマシンでは 30ミリが限界でした。)
JDK のインストールディレクトリにある src.zip ファイルのソースコードを確認したところ、案の定 JEditorPane クラスは Thread を使っていました。
入出力が伴う描画メソッドは要注意なのですね。
本当に参考になりました。
今後の参考までに教えていただきたいのですが、setPageメソッドが怪しいとすぐに問題を切り分けられたのは、どの点からでしょうか?
やはり、入出力を伴った描画メソッドだからでしょうか?
私はスタックの要素を確かめるぐらいしか、浮かびませんでしたが。

評価

0

たとえば html の表示処理では url の設定からすぐに戻ってこないと「中止」
機能が実装できなくなりますから想定すべきことと思います。

sleep でごまかしたようですが、根本的な対処にはなりませんので、出来るだ
け見た目に影響が無い限り長くした方がいいでしょうし、スレッドを眠らせる
とほかのイベント処理も止まってしまいますので、同じ時間調整なら 
javax,swing.Timer などを駆使して、いったん actionPerformed から抜けた
後で、別イベントでスクロールさせた方がいいかもしれません。

根本的な解決はページ処理の終了を待つことだと思いますが、JEditorPane の 
html 処理は相当複雑で、img src= なんかがあるとどうなることやら、なの
で、私も「これぞ」と言う解決策は把握してません。あしからず。

質問から6ヶ月以上経過しているので、回答を書き込むことはできません。