Java Интерактивная прокрутка ScrollPane - PullRequest
1 голос
/ 13 июля 2020

Я создаю приложение для чата, в котором я добавляю чаты в JPanel, прикрепленный к JScrollPane. Чаты могут быть добавлены и удалены из моего JPanel, и в этом случае я хочу, чтобы мой JScrollPane выполнял 3 функции

  1. Если вертикальный scrollBar не виден (т.е. для прокрутки из-за очень небольшого количества чатов на экране), и когда новый чат входит в панель, в результате чего scrollBar становится видимым в первый раз, тогда scrollBar должен прокручиваться до максимального значения, чтобы сделать новый добавленный чат видимым

  2. Если пользователь прокрутил до максимального значения / самого низа scrollPane, насколько это физически возможно, и когда новый чат входит на панель, максимальное значение scrollBar становится go вверх, затем выполните то же самое, что и Действие 1

  3. Если пользователь прокрутил чат вверх, чтобы увидеть предыдущие сообщения (т.е. до некоторой случайной точки, которая МЕНЬШЕ, чем МАКСИМАЛЬНОЕ значение scrollBar), а затем появляется новый чат, тогда в чате должна появиться кнопка прокрутки, и если пользователь нажимает на нее, scrollBar прокручивается до НОВОГО МАКСИМАЛЬНОГО значения, чтобы сделать вновь добавленное чаты видны

В основном мой алгоритм выглядит следующим образом:

//The user is currently at the bottom of the chat or scrollBar is not visible therefore making current & maximum value = 0
if (scrollBar.currentValue() == scrollBar.maxValue()) {
    scrollButton.setVisible(false);
    guiUpdate();  //Then Add,Remove The Chat
    scrollToVeryButton();
}
else {
    //The user is somewhere top of the chat looking at previous messages
    guiUpdate();   //Then Add,Remove The Chat
    scrollButton.setVisible(true); //Upon clicking scrollToVeryButton(); Is Called
}

Этот алгоритм, к сожалению, не работает, и scrollButton всегда оказывается видимым независимо от того. Вот полный код:

Класс отображения чата для отображения чатов


import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
import javax.swing.JScrollBar;
import javax.swing.JScrollPane;

class ChatDisplay extends JLayeredPane {

    private JScrollPane scrollPane;
    private JPanel chatDisplay;
    private Scroll notify;

    ChatDisplay() {
        setLayout(null);
        addChatDisplay();
        addNotifyButton();
    }

    private void addChatDisplay() {
        chatDisplay = new JPanel();
        chatDisplay.setLayout(new BoxLayout(chatDisplay, BoxLayout.Y_AXIS));
        scrollPane = new JScrollPane(chatDisplay);
        scrollPane.setBounds(0, 0, 300, 300);
        add(scrollPane, new Integer(0));
    }

    private void addNotifyButton() {
        notify = new Scroll();
        notify.setBounds(150, 150, 80, 50);
        add(notify, new Integer(1));
        notify.setVisible(false);
    }

    //Called To Add An New Chat
    void addButton(String text) {
        guiUpdate(() -> {
            chatDisplay.add(new Chat(text));
            chatDisplay.revalidate();
            chatDisplay.repaint();
        });
    }

    //The general update when gui is changed in anyway
    private void guiUpdate(Runnable update) {
        JScrollBar scrollBar = scrollPane.getVerticalScrollBar();
        if (scrollBar.getValue() == scrollBar.getMaximum()) {
            notify.setVisible(false);
            update.run();
            scrollBar.setValue(scrollBar.getMaximum());
        } else {
            update.run();
            notify.setVisible(true);
        }
    }

    //Called To Remove An Chat
    private void removeButton(JButton button) {
        guiUpdate(() -> {
            chatDisplay.remove(button);
            chatDisplay.revalidate();
            chatDisplay.repaint();
        });
    }

    //The Chat Button
    private final class Chat extends JButton implements ActionListener {

        private Chat(String text) {
            setText(text);
            Dimension dim = new Dimension(300, 100);
            setPreferredSize(dim);
            setMinimumSize(dim);
            setMaximumSize(dim);
            addActionListener(Chat.this);
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            removeButton(Chat.this); //Upon Clicking Remove Itself From Chat
        }
    }

    // The ScrollBar
    private final class Scroll extends JButton implements ActionListener {

        private Scroll() {
            setText("Scroll");
            setBackground(Color.green);
            addActionListener(Scroll.this);
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            JScrollBar scrollBar = scrollPane.getVerticalScrollBar();
            scrollBar.setValue(scrollBar.getMaximum());  //Scroll To The Bottom Upon Clicking
            setVisible(false);  //Its Job Is Done Wait For Next Event
        }
    }
}

ChatPanel устанавливается как панель содержимого

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JPanel;

class ChatPanel extends JPanel implements ActionListener {
    private ChatDisplay display;
    private int chat;

    ChatPanel() {
        super(null);
        addDisplay();
        addButton();
    }

    private void addDisplay() {
        display = new ChatDisplay();
        display.setBounds(0, 0, 300, 300);
        add(display);
    }

    private void addButton() {
        JButton add = new JButton("ADD CHAT");
        add.addActionListener(this);
        add.setBounds(0, 310, 300, 30);
        add(add);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        display.addButton("Chat " + (++chat));
    }
}

Основной класс

import javax.swing.JFrame;

public class Main {
    public static void main(String args[]) throws Exception {
        JFrame frame = new JFrame("Design Frame");
        frame.setContentPane(new ChatPanel());
        frame.setSize(320, 380);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setResizable(false);
        frame.setVisible(true);
    }
}

1 Ответ

0 голосов
/ 14 июля 2020

Я нашел ответ

Решение

Ключ в том, чтобы заставить полосу прокрутки всегда прокручиваться вниз всякий раз, когда есть какие-либо «РЕГУЛИРОВКИ» [add / удалить чат], и он должен выполнять это поведение только в том случае, если пользователь предварительно прокрутил до максимального значения

 scrollBar.addAdjustmentListener((event)->
 {
   if(autoScroll)//an boolean flag which is set to true whenever an chat is added/removed 
   {
    scrollBar.setValue(scrollBar.getMaximum());
    
    autoScroll=false; //set to false so the user can scroll freely upwards otherwise it will always reset it to the amaximum value
   }
 });

Исправления

current=scrollBar.getValue() 

будет никогда не быть равным максимуму, если он не добавлен к видимой сумме, отображаемой на экране, т.е.

currentValue=scrollBar.getValue()+scrollBar.getVisibleAmount()

Решение и исправление были реализованы методом guiUpdate()

FullCode

 private boolean autoScroll;
 private void guiUpdate(Runnable update)
 {
  JScrollBar scrollBar=scrollPane.getVerticalScrollBar();
  
  scrollBar.addAdjustmentListener((event)->
  {
   if(autoScroll)
   {
    scrollBar.setValue(scrollBar.getMaximum());
    
    autoScroll=false;
   }
  });
  
 
  if(scrollBar.getValue()+scrollBar.getVisibleAmount()==scrollBar.getMaximum())
  {
   notify.setVisible(false);
     
   autoScroll=true;  //The user is at the bottom of the screen hence autoScroll to the new maximum
   
   update.run();
  }
  else
  {
   autoScroll=false;  //the user is looking at previous chat messages hence don't scroll to the bottom
   
   update.run();
   
   notify.setVisible(true);
  }
 }

Похожие сообщения

  1. JScrollPane scrollToBottom
...