[JList] 자바 스윙 JList, ListModel, ListCellRenderer 1

dasfdsfsdd

전편에서는 ListModel에 대해서 다뤘는데, 모델 구현이 어느정도 마무리되면 모델의 데이터를 화면에 보여줄 뷰를 구성할 수 있다. 여기에 참여하는 컴포넌트 중에 사용자가 직접 구현해야 하는 것은 javax.swing.ListCellRenderer 이다.

ListCellRenderer는 "도장"과 같은 역할을 한다. 도장을 하나 파 놓고 계약서든 어디든 꾹꾹 눌러 찍기만 하면 똑같은 모양을 얼마든지 찍어낼 수 있는 것처럼, ListCellRenderer 도 기본적으로 도장과 같이 작동한다.

사용자가 모델에 관리하려는 데이터 인스턴스를 넣으면 ListModel이 등록된 리스너들에게 "새로운 element가 추가되었다" 라고 메세지를 보내고, 리스너들은 이 메세지에 포함된 index 값으로 다시 모델에 등록된 "새로 추가된 데이터 인스턴스"를 얻어낸다.(수정이든 삭제든 모두 이런 식으로 이루어진다.)

그리고 이렇게 얻어낸 인스턴스를 ListCellRenderer 구현체에 전달하면, 데이터 인스턴스를 그려줄 적당한 컴포넌트를 하나 반환하는데(이 부분을 사용자가 구현해야한다), 실제 그리는 역할을 전담하는 ListUI 구현 내용을 보면 ListCellRenderer 가 반환하는 컴포넌트를 CellRendererPane 에 전달해서 화면에 똑같이 그려넣는다.

[그림1]BasicListUI.paintCell 메소드

paintCell 메소드의 코드 마지막 줄에서 rendererPane.paintComponent(g, renderereComponent, ....) 가 호출되면 저기서 진짜 "그리기" 작업이 이루어진다.(말 그대로 도장 찍듯이 그리기만 한다. 이 말의 의미는 밑에서 추가 설명한다.)

ListModel 구현체에 데이터 인스턴스를 넣는 것에서 실제 화면에 그려지기까지 기나긴 과정 중에서 사용자가 개입해야하는 부분이 바로 ListCellRenderer 를 구현해서 "rendererComponent"를 정확하게 전달해주는 것이다.

아래와 같이 파일 다운로드 화면에서, "redererComponent"가 "DownloadPanel 컴포넌트에 해당한다.

[그림2]각각의 다운로드 화면은 도장 자국.

ListCellRenderer를 구현한 코드는 대략 아래와 같은데..
DownloadPanel 인스턴스를 하나 만들어놓고(도장 하나 파 놓는 것처럼) getListCellRendererComponent 가 호출될때마가 설정 정보만 데이터 인스턴스(Job, DownloadInfo) 에 맞게 살짝 바꿔서 반환한다. 여기서 반환하는 값이 [그림1]에서 rendererComponent 로 참조된다.(그리고 rendererPane이 이걸 가지고 그리는 작업으로 넘어감..)

여기서 계속해서 "도장자국"을 얘기하는 것은, [그림2]에 보듯이 세 개의 다운로드 컴포넌트가 보이는데, 저것들은 실제 DownloadPanel 인스턴스가 3개 들어있는게 아니라, 하나의 DownloadPanel 인스턴스를 가지고  상태값만 적절히 바꿔가며 도장 찍듯이 redererPane 에 그려넣은 "자국"에 불과하다.

ListCellRenderer 구현을 아래와 같이 바꾸더라도 밑단에서 그리는 작업을 하는 ListUI 의 기본적인 작업 방식이 "도장 찍기" 방식이라 하부 구현을 직접 바꾸지 않는 한 별 소용이 없다.
오히려 위처럼 메소드 안에서 매번 객체를 만들면, ProgressBar가 진행할때마다 엄청나게 많은 DownloadPanel 인스턴스가 생성되고 곧바로 소멸되는 화려한 광경을 목격하게 된다.(어디 갈때마다 도장 하나 파고 한 번 쓰고 버리고 또 도장 새로 파고 한 번 쓰고 버리는 식..)

만일 저 화면에서 DownloadPanel의 오른쪽에 버튼을 하나 넣고 리스너를 붙여서 <일시 정지 기능>을 구현한다면.. JList 화면을 눌러봐도 아무 반응이 없다.(진짜로 그림의 떡일 뿐)

JList는 기본 구현이 "단순히 보여주기"위한 기능에 맞춰져 있어서 마우스 클릭과 같이 사용자 행위에 적절히 반응하도록 작성하려면 많은 장애물이 도처에 널려있다.

이렇게 "도장찍기"식의 ListUI 구현이 마음에 안든다면 직접 BasicListUI 를 상속해서 새로운 ui 그리는 기능을  만드는 것도 가능하지만, 차라리 BoxLayout 을 이용하면 JList와 얼추 비슷한 기능을 구현할 수가 있다.(ListModel 구현을 그대로 가져다가 쓸 수도 있고..)

정리하면 JList를 사용할 때 다음과 같은 단계를 밟는다.

1. DownloadInfo와 같은 데이터 클래스 작성

2. 데이터 클래스의 인스턴스를 관리할 ListModel 구현체 작성 후 테스트. - 관리가 단순하다면 기본 구현인 DefualtListModel을 그대로 가져다 사용

3. ListCellRenderer 구현체 작성

그리고 아래와 같이 JList를 초기화한다.



Posted by yeori
,