javaswing编程一个简单的图片查看器.docx
《javaswing编程一个简单的图片查看器.docx》由会员分享,可在线阅读,更多相关《javaswing编程一个简单的图片查看器.docx(19页珍藏版)》请在冰豆网上搜索。
![javaswing编程一个简单的图片查看器.docx](https://file1.bdocx.com/fileroot1/2023-2/23/dbacbfaf-1da0-4e2f-ac1d-824ff9cd37a5/dbacbfaf-1da0-4e2f-ac1d-824ff9cd37a51.gif)
javaswing编程一个简单的图片查看器
图1
图2
图3
图4
图5
图6
怎么样,javaswing可视化编程。
适合对javaswing有所了解的java爱好者。
如果有兴趣就继续往下看,别见笑!
包含的内容:
1.整个窗口为一个JFrame。
2.最上方的JMenuBar。
3.中间最大的那块区域——mainPanel。
4.右侧边栏——rightPanel。
5.底部的一栏——basePanel。
主要功能:
JMenuBar里设置了两个菜单项——File和Help。
File里有打开文件、关闭文件和退出菜单项。
单击Open...或者按快捷键alt+O,弹出JFileChooser文件选择对话框,选择图片文件(这里支持jpg、jpeg、gif、tif、tiff和png五种格式)后,图片将在mainPanel里显示。
同一文件夹下的其他图片文件显示在rightPanel,如果图片很多可以出现滑动条。
basePanel里有两个按钮和一个显示当前图片序号和图片总数的标签;按钮可以往上往下翻图片,主面板、右侧边栏和标签都会动态更新。
如果到了最后一张,“下一个”按钮被禁用;第一张时,“上一个”被禁用。
同理在右侧边栏里选图片其它地方也都可以动态更新。
点File里的Close时,会清空mainPanel,rightPanel和标签中显示的内容,禁用两个按钮,效果(如图1)就像是还没有打开文件一样。
点退出时关闭Frame,结束程序。
下面正式开始介绍程序。
注:
这里所讲的和提供的源码稍有差异,有兴趣的可以结合文中给出的代码去编写自己的类。
程序分为四部分,分别为三个面板的建立。
最后组合在一起,放在一个JFrame里,加入菜单栏,各种监听器。
PartI写主面板类——MainPanel
可以从JPanel继承。
MainPanel相对与JPanel,多了一个图像显示的功能,所以里面一定要有获取图片的方法,还必须重载paintComponent方法。
关于paintComponent这我想多说一点。
可视化组件要完成显示的工作一般都要调用paint方法,而paint方法又把绘图任务交给了三个方法:
paintComponent,paintBorder,和paintChildren。
我们只需把需要个性定制的实现代码放在paintComponent方法里,在添加你的代码之前记得一定要调用super.paintComponent。
在写自己的实现方法前一定要记住给自己留一条退路。
什么退路?
比如说我们前面提到的关闭文件方法,要实现一定的清理工作,等价于不在原组件里画图。
这里我们可以这样实现:
@override
publicvoidpaintComponent(Graphicsg)
{
super.paintComponent(g);
/*Customizeyourpaintplanshere.*/
if(image!
=null){/*假设image就是需要显示的图片*/
/*yourcodetopainttheimage*/
}
}
一般情况下,我们不能直接调用paint方法,当需要更新显示内容时直接调用repaint。
repaint先完成一定的清理工作然后会调用paint,paint又调用paintComponent,就可以显示出你画的东西了。
要让MainPanel类获取图片,可以给它传一个ImageIcon,或图片文件或其它任何可以得到图片的东西。
我们从JFileChooser中得到的是图片文件,直接把图片文件或者经转化为ImageIcon后作为参数传递给MainPanel。
具体如下:
/*MainPanel.java*/
/*importeveryclassneededhere*/
publicclassMainPanelextendsJPanel
{
protectedFileimgFile;
protectedImageIconimg;
publicPaintImage()
{
/*addyourcodehere*/
}
publicvoidsetImageFile(FilenewImgFile)
{
imgFile=newImgFile;
ImageIconnewImg=newImageIcon(imgFile.getPath());
setImage(newImg);
}
publicvoidsetImage(ImageIconnewImg)
{
img=newImg;
repaint();
}
publicvoidpaintComponent(Graphicsg)
{
super.paintComponent(g);
if(img!
=null){
Util.paintImg(this,g,img);/*paintImg是类Util里的一个方法,用来画img*/
}
}
}
上述我们已经完成了MainPanel类的创建。
下面写一个测试类,看看效果:
/*MainPanelTest.java*/
/*importneededclasseshere*/
publicclassMainPanelTest
{
privatestaticfinalImageIconpigImg=newImageIcon(“imgs/Pig.gif”);
publicMainPanelTest(MainPanelmp)
{
JPanelpanel=newJPanel();
JButtonbtn=newJButton(“Loadimage”);
panel.setLayout(newBorderLayout());
mp.setPreferredSize(newDimension(350,350));
panel.add(mp);
btn.addActionListener(newActionListener(){
publicvoidactionPerformed(ActioneEvente){
mp.setImage(pigImg);
}
});
panel.add(btn);
Util.run(this,null);
}
publicstaticvoidmain(String[]args)
{
newMainPanelTest(newMainPanel());
}
}
运行时点击按钮即可载入图片。
这里再一次地用到了Util类,有关Util请参考Util.java。
PartII创建右侧面板类——SlidePane
SlidePane的显示内容是一个列表,我们可以从JList继承。
JList通过ListSelectionModel可以设置三种选择模式:
MULTIPLE_INTERVAL_SELECTION,SINGLE_INTERVAL_SELECTION,SINGLE_SELECTION。
由于每次mainPanel里只能显示一张图片,故我们把它设置成单选模式(SINGLE_SELECTION)。
getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
JList中每个元素的显示方式通过ListCellRenderer来管理。
我们想要改变它的显示方式,变成显示图标,我们就必须通过设置ListCellRenderer来实现。
ListCellRenderer是一个接口,里面只有一个方法:
Component getListCellRendererComponent(JList
extendsE> list,
E value,
int index,
boolean isSelected,
boolean cellHasFocus)
list是JList中所要显示的元素,value是当前选择的元素,index是当前选择元素的序号。
我们可以选择JLabel来实现这个方法,让list里放ImageIcon,然后把label的图标设置成value就可以了。
如:
publicclassImageRendererextendsJLabelimplementsListCellRenderer
{
publicImageRenderer()
{
setOpaque(true);
setHorizontalAlignment(SwingConstants.CENTER);
setVerticalAlignment(SwingConstants.CENTER);
}
publicComponent getListCellRendererComponent(JList
extendsE> list,
E value,
int index,
boolean isSelected,
boolean cellHasFocus)
{
if(isSelected){
setBackground(list.getSelectionBackground());
setForeground(list.getSelectionForeground());
}
else{
setBackground(list.getBackground());
setForeground(list.getForeground());
}
setIcon(value);
returnthis;
}
}
最后要出现滑动条的效果还需要把SlidePane加到JSlidePane里,不需要设置SlidePane的大小,否则SlidePane的可视区域就局限于设置的大小里,超出部分不能通过滚动滑动条看到。
PartIII创建BasePanel类
这里我们稍稍做一点改动,让按钮和面板标签中的显示内容想关联,让按钮来实现数组index的移动。
下面给出代码:
/*BasePanel.java*/
/*importneededclasseshere*/
publicclassBasePanelextendsJPanel
{
JButtonlast;
JButtonnext;
privateJLabelprogress;
privateintindex;
privateintlength;
publicBasePanel()
{
setLayout(newFlowLayout());
setBorder(newMatteBorder(1,0,0,0,Color.black));
last=newJButton(“上一个”);
last.setActionCommand(ActionCommand.LAST_IMG);
next=newJButton(“下一个”);
next.setActionCommand(ActionCommand.NEXT_IMG);
progress=newJLabel(“/”);
setPreferredSize(newDimension(600,50));
add(last);
add(progress);
add(next);
}
publicvoidsetParam(intlength,intindex)throwsIllegalParameterException
{
if(index<0||length<0||lengththrownewIllegalParameterException();
}
this.length=length;
setIndex(index);
}
publicvoidnext()
{
setIndex(index+1);
}
publicvoidlast()
{
setIndex(index-1);
}
publicintgetIndex()
{
returnindex;
}
publicvoidsetIndex(intindex)
{
this.index=index;
if(index<=0){
last.setEnabled(false);
}
else{
last.setEnabled(true);
}
if(index>=length-1){
next.setEnabled(false);
}
else{
next.setEnabled(true);
}
if(index==0&&length==0){
progress.setText(“/”);
}
else{
progress.setText((index+1)+”/”+length);
}
}
}
两个button的监听器在这里暂时不加,因为BasePanel、SlidePane和MainPanel三者之间要互相通信,在这里增加监听器只能实现对其自身的控制,不能改变别的面板的状态。
故监听器的功能等到了写主程序的时候再一并实现。
PartIV创建主程序
将前面几个容器都组合进来,实现一个menuBar,再写各类监听器,最后显示出来。
第一步:
添加组件到面板
首先定义前面三个类的组件。
MainPanelmainPanel=newMainPanel();
SlidePanerightPanel=newSlidePane();
BasePanelbasePanel=newBasePanel();
然后把rightPanel加到JScrollPane里,
JScrollPanescrolledRightPanel=newJScrollPane(rightPanel);
再把mainPanel和scrolledRightPanel放到一个JSplitPane里,
JSplitPanesplitPanel=newJSplitPane(JSplitPane.HORIZONTAL_SPLIT,mainPanel,scrolledRightPanel);
最后把splitPanel和basePanel装到一个容器中,容器使用BorderLayout排布。
即:
这里主程序类继承自JPanel,其本身就是一个容器。
setLayout(newBorderLayout());
add(splitPanel,BorderLayout.CENTER);
add(basePanel,BorderLayout.SOUTH);
第二步:
创建menuBar
仅以一个为例:
JMenuBarmenuBar=newJMenuBar();
JMenufileMenu=newJMenu(“File”);
menuBar.add(fileMenu);
JMenuItemfileOpenItem=newJMenuItem(“OpenFile”);
fileMenu.add(fileOpenItem);
创建其它菜单或菜单项与此过程类似,不重复写了。
第三步:
增加监听器
这一步最复杂,留在了最后作为压轴部分。
首先让我们来盘点一下有哪些组件需要增加监听器,都需要增加什么类型的监听器。
按照从上到下,从左到右的顺序:
1.File菜单,Help菜单的各个菜单项。
需要ActionListener。
2.mainPanel,当尺寸改变时,需要重新绘制,应增加ComponentListener。
3.rightPanel里,选择不同的文件时其它的面板需要更新。
需要ListSelectionListener。
4.basePanel里两个按钮,需要ActionListener。
所有这些监听器的实现强烈建议使用内部类在主程序里实现,这样一来主程序中的变量可以被这些类访问,你便拥有了更大的灵活性。
但是你可以发现有多个组件需要同一种监听器,当在一个组件上发生动作时,监听器就可以收到信号,但是它并不知道是谁发出的信号,这带来了一个问题。
有两种解决方式。
第一,getSource方法获取源,然后判断。
这种方法针对类型单一的组件(比如都是JButton)比较好用,但是像我们这里的情况解决起来就不是很合适。
第二,在增加监听器的同时调用当前组件的setActionCommand方法设置动作命令。
当监听器收到触发信号后,调用getActionCommand,然后判断和哪个组件的相同。
下面增加监听器:
openFileItem.addActionListener(this);
openFileItem.setActionCommand(ActionCommand.OPEN_FILE);
openFileItem.setAccelerator(HotKey.OPEN_FILE);
……
mainPanel.addComponentListener(newComponentAdapter(){
publicvoidcomponentResized(ComponentEvente)
{
mainPanel.repaint();
}
});
/*ListSelectionListener在后面说*/
basePanel.lastImg.addActionListener(this);
basePanel.lastImg.setActionCommand(ActionCommand.LAST_IMG);
……
setActionCommand方法里用到的String类型的参数都使用定义在ActionCommand里的常量。
菜单项设置了快捷键,按钮不支持。
主程序里实现ActionListener接口的方法如下:
publicvoidactionPerformed(ActionEvente)
{
Stringcommand=getActionCommand();
if(command==null)return;
switch(command)
{
caseActionCommand.OPEN_FILE:
doOpenFile();
caseActionCommand.CLOSE_FILE:
doCloseFile();
……
caseActionCommand.LAST_IMG:
showLastImg();
}
}
以下细说doOpenFile(),doCloseFile()和showLastImg(),actionPerformed里其它都略了。
doOpenFile()
点击OpenFile菜单,执行doOpenFile方法,首先应该弹出一个选择文件的对话框。
定义一个JFileChooser,使用里面的showOpenDialog,调用该方法后得到一个返回值。
通过返回值可以判断是否选择了文件,如果选择了文件就进一步处理。
首先可以用JFileChooser的getSelectedFile得到所选的文件curImageFile。
由curImageFile又可以得到当前目录下的图片文件列表imgFileList。
把imgFileList传递给rightPanel和basePanel里的两个按钮,因为打开文件后,不仅要在mainPanel里显示图片,而且右侧边栏和下方的控制/状态栏也会被激活。
这两个组件需要的正是一个文件数组。
调用MainPanel的setImageFile就可以显示当前图片。
调用SlidePane的setListData就可以把要显示的文件加入到列表里了,但是我们前面约定的SlidePane里的元素类型是ImageIcon,这里不能直接提供File类型的元素列表,故转换后再调用setListData方法。
针对我们前面写的BasePanel类,只需要向其传递一个图片数组的长度和当前选定图片在图片数组中的位置就可以了。
所以doOpenFile就可以这样来写:
/*inthefielddeclaration*/
protectedFilecurImageFile;
protectedFile[]imgFiles;
protectedImageIconcurImage;
protectedImageIcon[]imgs;
protectedintindex;
……
voiddoOpenFile()
{
JFileChooserjc=newJFileChooser();
intretValue=jc.showOpenDialog();
if(retValue!
=JFileChooser.APPROVE_OPTION){
/*specifyyourhandleoption*/
return;
}
/*herewedealwithwhenchooseapproveoption*/
curImageFile=jc.getSelectedFile();
imgFiles=curImgFile.getParentFile().listFiles(newFilenameFilter(){
publicbooleanaccept(Filedir,Stringname){
/*tofilterouttheimagefiles.handleityourself.*/
}
});
imgs=newImageIcon[imgFiles.length];
for(inti=0;iimgs[i]=newImageIcon(imgFiles[i]);
if(imgFiles[i].equals(curImageFile)){
index=i;
}
}
curImage=imgs[index];
mainPanel.setImage(curImage);
rightPanel.setListData(imgs);
rightPanel.setSelectedIndex(index);
try{
basePanel.setParam(imgs.length,index);
}catch(IllegalParameterExceptonex){
newRuntimeException(“illegalparamter”);
}
}
doCloseFile()
这里要做的是模拟文件关闭以后三个面板上应该显示的东西,通过擦出是不明智的选择。
在开始设计的时候就需要考虑到这一步了,现在来看看解决方法:
MainPanel接受空的参数,故对它就这样写:
mainPanel.setImage(null);
RightPa