這幾天研究了一下JList,發現自定義它的樣式真的是很難的過程。不過在此我想把我研究的東西整理並分享一下,希望對大家有所幫助。
一、列表基本操作
首先我們來創建一個JList對象,並給它加一個滾動條:
JList jl=new JList(); //實例化JList對象
JScrollPane jsp=new JScrollPane(); //實例化滾動條對象
jsp.setViewportView(jl); //把JList加到滾動條裏面
再把滾動條對象jsp加入到JPanel對象即可。
不過如何給JList加元素呢?通常我們使用默認數據模型DefaultListModel對象,在默認數據模型對象裏面添加元素(addElement()方法),再把模型對象放入JList的構造方法裏面,那麼,我把上面代碼改變如下:
DefaultListModel dfl=new DefaultListModel(); //實例化默認數據模型對象
dfl.addElement("a"); //給模型加入字符串元素a
dfl.addElement("b"); //給模型加入字符串元素b
JList jl=new JList(dfl); //實例化JList對象,構造方法寫入默認數據模型對象
JScrollPane jsp=new JScrollPane(); //實例化滾動條對象
jsp.setViewportView(jl); //把JList加到滾動條裏面
這樣就實現了最簡單的文字列表了:
觀察DefaultListModel對象的addElement()方法,發現參數是Object,也就是說他可以裝任何類型數據。我試着把圖片對象(ImageIcon)和文件對象(File)裝進去試試,改變代碼如下:
DefaultListModel dfl=new DefaultListModel(); //實例化默認數據模型對象
dfl.addElement("a"); //給模型加入字符串元素a
dfl.addElement("b"); //給模型加入字符串元素b
dfl.addElement(new ImageIcon("res\\testicon.png")); //給模型加入圖片類型元素
dfl.addElement(new File("res\\testicon.png")); //給模型加入文件類型元素
JList jl=new JList(dfl); //實例化JList對象,構造方法寫入默認數據模型對象
JScrollPane jsp=new JScrollPane(); //實例化滾動條對象
jsp.setViewportView(jl); //把JList加到滾動條裏面
效果:
可見,不同類型數據元素有着不同的顯示和繪製方法,其餘大家可以自行嘗試。
二、重寫渲染器以實現自定義列表樣式
不過怎麼自定義這個列表使其又有圖片又有文字呢?
首先我們要知道,JList有一個渲染器(DefaultListCellRenderer),它決定着JList如何顯示內容。要想自定義這個列表樣式,我們就必須 重寫這個渲染器。
在這個渲染器裏面有一個getListCellRendererComponent(JList<? extends Object> list,Object value,int index,boolean isSelected,boolean cellHasFocus)方法,它決定着列表如何顯示等等。
在此之前我們必須瞭解getListCellRendererComponent方法自帶的五個參數(JList<? extends Object> list,Object value,int index,boolean isSelected,boolean cellHasFocus)分別是什麼意義,否則我們不知道從何下手來自定義列表。
查看官方文檔得知意義如下:
JList<? extends Object> list:表示我們正在創建的JList對象,其中<? extends Object>表示我們的列表對象元素類型是任意的。這個參數基本上我們不會使用。
Object value:這個代表了我們列表中的元素,可以是任何類型,他是list.getModel().getElementAt(index)的返回值。
int index:表示列表中被我們鼠標選擇的元素的索引,例如列表中第一個元素被選中了,則這個值就是0。
boolean isSelected:表示我們的列表是否有元素被選中。
boolean cellHasFocus:表示我們的列表是否有焦點(???)。
知道了這5個參數的意義,我們就可以很好的用他們來重寫我們的渲染器了。當然,剛開始這裏有一點我一直弄不明白,就是上面的參數Object value的意義,因爲他是Object,所以我們可能難以判斷其真實的、具體的類型,那麼怎麼來使用呢?於是我想看看它的“真面目”,或者是它包含着什麼信息。
不過我們知道了,他是list.getModel().getElementAt(index)的返回值,所以我決定先在JList所在的類裏面試試這個方法是什麼。
我又將上述JList相關代碼下面加上了這個方法,改變如下:
DefaultListModel dfl=new DefaultListModel(); //實例化默認數據模型對象
dfl.addElement("a"); //給模型加入字符串元素a
dfl.addElement("b"); //給模型加入字符串元素b
dfl.addElement(new ImageIcon("res\\testicon.png")); //給模型加入圖片類型元素
dfl.addElement(new File("res\\testicon.png")); //給模型加入文件類型元素
JList jl=new JList(dfl); //實例化JList對象,構造方法寫入默認數據模型對象
JScrollPane jsp=new JScrollPane(); //實例化滾動條對象
jsp.setViewportView(jl); //把JList加到滾動條裏面
/**
使用Object的toString()方法,用控制檯輸出獲取到的表格的第1、2、3、4個內容。
*/
System.out.println(jl.getModel().getElementAt(0).toString());
System.out.println(jl.getModel().getElementAt(1).toString());
System.out.println(jl.getModel().getElementAt(2).toString());
System.out.println(jl.getModel().getElementAt(3).toString());
然後控制檯輸出如下:
我們發現,通過toString()方法,表格內容包含的文字信息被輸出了。也就是說,我們通過toString()方法,就可以吧渲染器裏面value值給以String形式獲取了!
好了,解除了疑惑,我們現在就要開始自己重寫渲染器,來自定義列表樣式了!
新建一個類,我這裏起名爲CRTest,然後重寫方法:
import java.awt.*;
import javax.swing.*;
public class CRTest extends DefaultListCellRenderer { //繼承渲染器類
public Component getListCellRendererComponent(JList<? extends Object> list,Object value,int index,boolean isSelected,boolean cellHasFocus) { //重寫渲染器的方法
return this;
}
}
需要注意的是,默認渲染器類繼承了JLabel,因此在這個裏面設置樣式我們就用JLabel的方法即可。
現在給列表加上圖片,改變渲染器代碼如下:
import java.awt.*;
import javax.swing.*;
public class CRTest extends DefaultListCellRenderer { //繼承渲染器類
public Component getListCellRendererComponent(JList<? extends Object> list,Object value,int index,boolean isSelected,boolean cellHasFocus) { //重寫渲染器的方法
setText(value.toString()); //設置文字(獲取每個元素文字信息將其顯示)
setIcon(new ImageIcon("res\\testicon.png")); //設置圖標
return this;
}
}
然後再JList所在的類設置渲染器爲我們自己寫的渲染器,加上下面這句話:
jl.setCellRenderer(new CRTest()); //設置渲染器爲我們自己的
效果如下:
通過設置圖片大小、背景顏色等等,我們可以實現列表每個元素的圖標的自適應、設置鼠標放上去時和被選中時顯示的顏色等等。
改變渲染器代碼如下:
import java.awt.*;
import javax.swing.*;
public class CRTest extends DefaultListCellRenderer { //繼承渲染器類
public Component getListCellRendererComponent(JList<? extends Object> list,Object value,int index,boolean isSelected,boolean cellHasFocus) { //重寫渲染器的方法
setText(value.toString()); //設置文字(獲取每個元素文字信息將其顯示)
ImageIcon ico=new ImageIcon("res\\testicon.png"); //實例化一個ImageIcon對象
Image img=ico.getImage(); //實例化Image對象獲取ico對象的內容
img=img.getScaledInstance(25,25,Image.SCALE_DEFAULT); //把圖片全部縮放爲25x25
ico.setImage(img); //ImageIcon對象重新獲取縮放後圖標
setIcon(ico); //設置圖標
if(isSelected) { //當某個元素被選中時
setForeground(Color.WHITE); //設置前景色(文字顏色)爲白色
setBackground(Color.BLUE); //設置背景色爲藍色
System.out.println(index+"被選中");
} else { //某個元素未被選中時(取消選中)
setForeground(Color.BLACK); //設置前景色(文字顏色)爲黑色
setBackground(Color.WHITE); //設置背景色爲白色
}
return this;
}
}
效果:
還可以添加如下方法設置每個單元格水平、垂直位置關係,大家可以加入自行嘗試:
setVerticalTextPosition(SwingConstants.CENTER); //垂直
setHorizontalTextPosition(SwingConstants.CENTER); //水平
//上面CENTER意思是設置到中間,改變這個CENTER可以換位其它位置
如果我想每一個元素圖標不同怎麼辦呢?那也很簡單,在渲染器裏面判斷value值,然後分別設置即可,我改變代碼如下:
import java.awt.*;
import javax.swing.*;
public class CRTest extends DefaultListCellRenderer { //繼承渲染器類
public Component getListCellRendererComponent(JList<? extends Object> list,Object value,int index,boolean isSelected,boolean cellHasFocus) { //重寫渲染器的方法
setText(value.toString()); //設置文字(獲取每個元素文字信息將其顯示)
if(value.toString().equals("a")) { //判斷value(列表值)來分情況設置不同圖標
ImageIcon ico=new ImageIcon("res\\testicon.png"); //實例化一個ImageIcon對象
Image img=ico.getImage(); //實例化Image對象獲取ico對象的內容
img=img.getScaledInstance(25,25,Image.SCALE_DEFAULT); //把圖片全部縮放爲25x25
ico.setImage(img); //ImageIcon對象重新獲取縮放後圖標
setIcon(ico); //設置圖標
} else {
ImageIcon ico=new ImageIcon("res\\testicon-2.png"); //實例化一個ImageIcon對象
Image img=ico.getImage(); //實例化Image對象獲取ico對象的內容
img=img.getScaledInstance(25,25,Image.SCALE_DEFAULT); //把圖片全部縮放爲25x25
ico.setImage(img); //ImageIcon對象重新獲取縮放後圖標
setIcon(ico);
}
if(isSelected) { //當某個元素被選中時
setForeground(Color.WHITE); //設置前景色(文字顏色)爲白色
setBackground(Color.BLUE); //設置背景色爲藍色
System.out.println(index+"被選中");
} else { //某個元素未被選中時(取消選中)
setForeground(Color.BLACK); //設置前景色(文字顏色)爲黑色
setBackground(Color.WHITE); //設置背景色爲白色
}
return this;
}
}
效果:
這樣,我們就完成了列表自定義了!
我們還可以設置列表是橫排還是豎着顯示。回到JList所在的類,通過JList的setLayoutOrientation()方法和setVisibleRowCount()方法實現。如下,我加入下面兩行代碼:
jl.setLayoutOrientation(JList.HORIZONTAL_WRAP); //設置表格橫着排列元素
jl.setVisibleRowCount(1); //設置總行數爲1行
效果:
setVisibleRowCount設置爲2時:
其餘大家可以自己嘗試。
那麼以上就是自定義列表的方法了,我們總共有2個類,其中注意渲染器類重寫時每個參數的意義!這非常重要!
完整代碼:
主類:
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import java.io.*;
public class JListObj {
@SuppressWarnings({ "unchecked", "rawtypes", "static-access", "deprecation" })
public static void main(String[] args) {
JFrame jf=new JFrame("列表對象獲取");
jf.setSize(500,300);
jf.setDefaultCloseOperation(jf.EXIT_ON_CLOSE);
DefaultListModel dfl=new DefaultListModel();
dfl.addElement("a");
dfl.addElement("b");
dfl.addElement(new ImageIcon("res\\testicon.png"));
dfl.addElement(new File("res\\testicon.png"));
JList jl=new JList(dfl);
jl.setCellRenderer(new CRTest()); //設置渲染器爲我們自己的
JScrollPane jsp=new JScrollPane();
jsp.setBounds(14, 13, 431, 210);
jsp.setViewportView(jl);
JPanel jp=new JPanel();
jf.getContentPane().add(jp);
jp.setLayout(null);
jp.add(jsp);
jf.show();
/**
使用Object的toString()方法,用控制檯輸出獲取到的表格的第1、2、3、4個內容。
*/
System.out.println(jl.getModel().getElementAt(0).toString());
System.out.println(jl.getModel().getElementAt(1).toString());
System.out.println(jl.getModel().getElementAt(2).toString());
System.out.println(jl.getModel().getElementAt(3).toString());
}
}
自定義渲染器類(重寫默認渲染器):
import java.awt.*;
import javax.swing.*;
@SuppressWarnings("serial")
public class CRTest extends DefaultListCellRenderer {
public Component getListCellRendererComponent(JList<? extends Object> list,Object value,int index,boolean isSelected,boolean cellHasFocus) {
setText(value.toString()); //設置文字
if(value.toString().equals("a")) { //判斷value(列表值)來分情況設置不同圖標
ImageIcon ico=new ImageIcon("res\\testicon.png"); //實例化一個ImageIcon對象
Image img=ico.getImage(); //實例化Image對象獲取ico對象的內容
img=img.getScaledInstance(25,25,Image.SCALE_DEFAULT); //把圖片全部縮放爲25x25
ico.setImage(img); //ImageIcon對象重新獲取縮放後圖標
setIcon(ico); //設置圖標
} else {
ImageIcon ico=new ImageIcon("res\\testicon-2.png"); //實例化一個ImageIcon對象
Image img=ico.getImage(); //實例化Image對象獲取ico對象的內容
img=img.getScaledInstance(25,25,Image.SCALE_DEFAULT); //把圖片全部縮放爲25x25
ico.setImage(img); //ImageIcon對象重新獲取縮放後圖標
setIcon(ico);
}
if(isSelected) { //當某個元素被選中時
setForeground(Color.WHITE); //設置前景色(文字顏色)爲白色
setBackground(Color.BLUE); //設置背景色爲藍色
System.out.println(index+"被選中");
} else { //某個元素未被選中時(取消選中)
setForeground(Color.BLACK); //設置前景色(文字顏色)爲黑色
setBackground(Color.WHITE); //設置背景色爲白色
}
return this;
}
}