Swing制作FloorTabbedPane

[b]引子[/b]
FloorTabbedPane这个名称来源于jide components,这个也类似于outlook左边栏的风格,具体的Demo画面如下:
[img]http://dl.iteye.com/upload/attachment/328504/5a52ec33-4ee1-3e6c-8f8b-8b190170ef5d.png[/img]
当然学Swing也有段时间了,这东西看着很简单,就几个按钮和一个panel,应该花不了多少时间就能写好。但实际上自己动手确有些难度,不知如何下手,于是乎就借助网络这个工具了(这是个很坏的习惯)。果真让我找到了一段非常简洁的代码:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class Test3 extends JFrame {

public Test3() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container content = getContentPane();
FloorTab ft = new FloorTab();
content.add(ft, BorderLayout.CENTER);
for (int i = 0; i < 5; i++) {
JPanel jp = new JPanel();
jp.setBorder(BorderFactory.createTitledBorder("tab-" + i));
ft.addTab("Tab-" + i, jp);
}
setSize(200, 500);
setVisible(true);
}

public static void main(String[] args) {
new Test3();
}
}

class FloorTab extends JPanel implements ActionListener {

GridBagConstraints gbc = new GridBagConstraints(0, 1, 1, 1, 1.0, 1.0,
GridBagConstraints.CENTER,
GridBagConstraints.BOTH,
new Insets(0, 0, 0, 0), 0, 0);
GridBagLayout gbl = new GridBagLayout();
CardLayout cl = new CardLayout();
JPanel panels = new JPanel(cl);
java.util.ArrayList buttons = new java.util.ArrayList();

public FloorTab() {
setLayout(gbl);
add(panels, gbc);
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.weighty = 0.0;
}

public void addTab(String name, JPanel panel) {
gbc.gridy = getComponentCount();
if (gbc.gridy == 1) {
gbc.gridy = 0;
}
JButton jb = new JButton(name);
add(jb, gbc);
buttons.add(jb);
jb.addActionListener(this);
panels.add(name, panel);
}

public void actionPerformed(ActionEvent ae) {
int y = 0;
GridBagConstraints tmp;
JButton srcButton = (JButton) ae.getSource();
for (int i = 0; i < buttons.size(); i++) {
JButton jb = (JButton) buttons.get(i);
tmp = gbl.getConstraints(jb);
tmp.gridy = y++;
gbl.setConstraints(jb, tmp);
if (srcButton == jb) {
tmp = gbl.getConstraints(panels);
tmp.gridy = y++;
gbl.setConstraints(panels, tmp);
}
}
cl.show(panels, srcButton.getText());
}
}

[b]总结及发现问题[/b]
整个panel用两个布局就解决了问题,外层用GridBagLayout从上往下排布几个按钮和一个panels,panels中用CardLayout由外往里排列几个按钮所对应的panel,点击按钮的时候,用GridBagLayout重新排列一下外层结构,panels再用CardLayout show出点击按钮所对应的panel。
好像比较好的解决了这个问题,但是新问题来了:没有动画效果,看起来从一个画面突然跳到了另一个画面,使用起来非常生硬。如何添加动画效果呢,这里有个简单的办法,使用Timing Framework和Animated Transitions这个两个框架就可以实现(这个两个jar包我放到附件中了,具体的使用方法和API大家可能要去网上搜搜了,另外这个框架在《java动画、图形和极富客户端效果开发》这个本书上都有介绍,英文名为《Filthy Rich Clients》),简单增加动画的代码:

/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package org.freedom.vdream.ui;

import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Container;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import org.jdesktop.animation.timing.Animator;
import org.jdesktop.animation.transitions.ScreenTransition;
import org.jdesktop.animation.transitions.TransitionTarget;

public class FloorTabPanel extends JPanel implements ActionListener, TransitionTarget {

GridBagConstraints gbc = new GridBagConstraints(0, 1, 1, 1, 1.0, 1.0,
GridBagConstraints.CENTER,
GridBagConstraints.BOTH,
new Insets(0, 0, 0, 0), 0, 0);
GridBagLayout gbl = new GridBagLayout();
CardLayout cl = new CardLayout();
JPanel panels = new JPanel(cl);
java.util.ArrayList buttons = new java.util.ArrayList();
ScreenTransition transition;
JButton eventButton;

public FloorTabPanel() {
setLayout(gbl);
add(panels, gbc);
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.weighty = 0.0;

panels.setOpaque(false);

Animator animator = new Animator(280);
animator.setAcceleration(.2f);
animator.setDeceleration(.4f);
transition = new ScreenTransition(this, this, animator);
}

public void addTab(String name, JPanel panel) {
gbc.gridy = getComponentCount();
if (gbc.gridy == 1) {
gbc.gridy = 0;
}
JButton jb = new JButton(name);
add(jb, gbc);
buttons.add(jb);
jb.addActionListener(this);
panels.add(name, panel);
}

public void actionPerformed(ActionEvent ae) {
eventButton = (JButton) ae.getSource();
transition.start();
}

public void setupNextScreen() {
int y = 0;
GridBagConstraints tmp;
JButton srcButton = eventButton;
for (int i = 0; i < buttons.size(); i++) {
JButton jb = (JButton) buttons.get(i);
tmp = gbl.getConstraints(jb);
tmp.gridy = y++;
gbl.setConstraints(jb, tmp);
if (srcButton == jb) {
tmp = gbl.getConstraints(panels);
tmp.gridy = y++;
gbl.setConstraints(panels, tmp);
}
}
cl.show(panels, srcButton.getText());
}
}

class Test3 extends JFrame {

public Test3() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container content = getContentPane();
FloorTabPanel ft = new FloorTabPanel();
content.add(ft, BorderLayout.CENTER);
for (int i = 0; i < 5; i++) {
JPanel jp = new JPanel();
jp.setBorder(BorderFactory.createTitledBorder("tab-" + i));
jp.add(new JButton("" + i));
ft.addTab("Tab-" + i, jp);
}
setSize(200, 500);
setVisible(true);
}

public static void main(String[] args) {
new Test3();
}
}

[b]再发现问题[/b]
动画是出来了,效果确不入人意:有的按钮是从panels上面移动的,有的是从panels下面移动,而且panels每次的变化都是在移动而不是大小的变化,这里需要再思考了。

[b]最后的解决[/b]
如果再上面的代码上进行修改是没有多大意义(并不是说不能解决),我们需要独立解决问题的能力,其实有些东西学到了就得变为自己的东西了。重新思考问题的解决之道:
何不将FloorTabbedPane的每一页tab设计成一个独立的类,而且这个应该是FloorTabbedPane的内部类。每一个tab页都能够展开和收缩,当其中一页展开时通知其他页进行收缩,这个考虑可以使用观察者模式了。另外每一个tab页的伸展和收缩都要符合动画的要求,每一次的动画都要好像一个panel在膨胀,进而把原来伸展的panel压缩了,并把按钮挤到了一边。具体的看下面代码:

/*
* @(#)FloorTabbedPane.java 1.00 Oct 10, 2010
*
* Copyright (c) 2010, Graham Liu. All rights reserved.
* This software is the proprietary information of Graham Liu.
*/
package org.freedom.vdream.ui;

import java.awt.Container;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Observable;
import java.util.Observer;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import org.freedom.vdream.ui.FloorTabbedPane.FloorTab;
import org.jdesktop.animation.timing.Animator;
import org.jdesktop.animation.transitions.ScreenTransition;
import org.jdesktop.animation.transitions.TransitionTarget;

/**
* 楼层样式的Tab Pane
*
* @version 1.00 Oct 10, 2010
* @author Graham Liu
*/
public class FloorTabbedPane extends JPanel implements TransitionTarget {

//FloorTab的一个有序集
private ArrayList<FloorTab> floorTabList = new ArrayList<FloorTab>();
//布局
private GridBagLayout floorLayout = new GridBagLayout();
//GridBagConstraints
private GridBagConstraints floorgbc = new GridBagConstraints();
//Observable
private Observable notifier;
//动画控制
private ScreenTransition transition;

/**
* creates a FloorTabbedPane with not param
*/
public FloorTabbedPane() {
this(null);
}

/**
* 带有一个FloorTab的一个有序集的构造方法
* @param floorTabList
*/
private FloorTabbedPane(ArrayList<FloorTab> floorTabList) {
super();

//动画设置
Animator animator = new Animator(200);
animator.setAcceleration(.4f);
animator.setDeceleration(.2f);
this.transition = new ScreenTransition(this, this, animator);

this.notifier = new Observable() {

@Override
public void notifyObservers(Object b) {
// Otherwise it won't propagate changes:
setChanged();
//通知所有的观察者进行更新
super.notifyObservers(b);
//启动动画
transition.start();
}
};
if (floorTabList != null) {
this.floorTabList = floorTabList;
}
this.setLayout(floorLayout);
floorgbc = new GridBagConstraints();
floorgbc.gridx = 0;
floorgbc.gridy = 0;
floorgbc.gridwidth = 1;
floorgbc.gridheight = 1;
floorgbc.weightx = 1.0;
floorgbc.weighty = 0.0;
floorgbc.fill = GridBagConstraints.BOTH;
floorgbc.anchor = GridBagConstraints.CENTER;
floorgbc.insets = new Insets(0, 0, 0, 0);
layoutComponents();
}

/**
* 组件布局
*/
private void layoutComponents() {
int size = floorTabList.size();
for (int i = 0; i < size; i++) {
FloorTab ft = floorTabList.get(i);
if (i == 0) {
floorgbc.weighty = 1.0;
} else {
ft.contract();
floorgbc.weighty = 0.0;
}
floorgbc.gridy = i;
this.add(ft, floorgbc);
}
}

/**
* 添加一页
* @param button tab页中的button
* @param pane tab页中的pane
*/
public void addTab(JButton button, JPanel pane) {
FloorTab floorTab = new FloorTab(button, pane);
this.addTab(floorTab);
}

/**
* 添加一页
* @param floorTab tab页
*/
public void addTab(FloorTab floorTab) {
this.floorTabList.add(floorTab);
int index = floorTabList.indexOf(floorTab);
if (index == 0) {
floorgbc.weighty = 1.0;
} else {
floorTab.contract();
floorgbc.weighty = 0.0;
}
floorgbc.gridy = index;
this.add(floorTab, floorgbc);
}

/**
* 动画的下一个画面
*/
public void setupNextScreen() {
this.repaint();
}

/**
* FloorTabbedPane中的tab页
*/
public class FloorTab extends JPanel implements ActionListener, Observer {

//tab页中的button
private JButton tabButton;
//tab页中pane
private JPanel tabPane;
//布局
private GridBagLayout tabLayout = new GridBagLayout();
//GridBagConstraints
private GridBagConstraints tabgbc = new GridBagConstraints();
//是否展开
private boolean isExpended = true;

/**
* Creates a FloorTab with no set button or pane.
*/
public FloorTab() {
this(null, null);
}

/**
* Creates a FloorTab with a button.
*/
public FloorTab(JButton floorButton) {
this(floorButton, null);
}

/**
* Creates a FloorTab with a pane.
*/
public FloorTab(JPanel floorPane) {
this(null, floorPane);
}

/**
* Creates a FloorTab with a button and a pane.
*/
public FloorTab(JButton floorButton, JPanel floorPane) {
super();
this.tabButton = floorButton;
this.tabPane = floorPane;
FloorTabbedPane.this.notifier.addObserver(FloorTab.this);
initComponents();
}

/**
* 初始化FloorTab组件并布局
*/
private void initComponents() {
if (this.tabButton == null) {
this.tabButton = new JButton();
}
if (this.tabPane == null) {
this.tabPane = new JPanel();
}
this.tabPane.setPreferredSize(new Dimension(0, 0));
this.tabButton.addActionListener(this);
this.setLayout(tabLayout);
tabgbc = new GridBagConstraints();
tabgbc.gridx = 0;
tabgbc.gridy = 0;
tabgbc.gridwidth = 1;
tabgbc.gridheight = 1;
tabgbc.weightx = 1.0;
tabgbc.weighty = 1.0;
tabgbc.fill = GridBagConstraints.BOTH;
tabgbc.anchor = GridBagConstraints.CENTER;
tabgbc.insets = new Insets(0, 0, 0, 0);

tabgbc.gridy = 0;
tabgbc.weighty = 0.0;
this.add(this.tabButton, tabgbc);

tabgbc.gridy = 1;
tabgbc.weighty = 1.0;
this.add(this.tabPane, tabgbc);
}

/**
* 展开
*/
private void expend() {
if (!this.isExpended) {
//重新排列tabPanetabPane
this.add(this.tabPane, tabgbc);

//更新外层TabbedPane中的布局
GridBagConstraints gtmp = FloorTabbedPane.this.floorLayout.getConstraints(this);
gtmp.weighty = 1.0;
FloorTabbedPane.this.floorLayout.setConstraints(this, gtmp);

//已经展开
this.isExpended = true;
}

}

/**
* 收缩
*/
public void contract() {
if (this.isExpended) {
//更新外层TabbedPane中的布局
GridBagConstraints gtmp = FloorTabbedPane.this.floorLayout.getConstraints(this);
gtmp.weighty = 0.0;
FloorTabbedPane.this.floorLayout.setConstraints(this, gtmp);

//已经收缩
this.isExpended = false;
}
}

/**
* button点击事件处理
* @param e
*/
public void actionPerformed(ActionEvent e) {
//进行展开处理
expend();
//通知其他观察者
FloorTabbedPane.this.notifier.notifyObservers(FloorTab.this);
}

/**
* 接收到通知的FloorTab进行收缩
* @param o Observable->FloorTabbedPane.this
* @param arg 通知者
*/
public void update(Observable o, Object arg) {
if (this != arg) {
this.contract();
}
}
}

/**
* test for this
* @param args
*/
public static void main(String[] args) {

SwingUtilities.invokeLater(new Runnable() {

public void run() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container container = frame.getContentPane();

FloorTabbedPane ftp = new FloorTabbedPane();
for (int i = 0; i < 5; i++) {
JPanel jp = new JPanel();
jp.setBorder(BorderFactory.createTitledBorder("tab-" + i));
jp.add(new JButton("b" + i));
JButton bt = new JButton("" + i);
ftp.addTab(bt, jp);
}

container.add(ftp);

frame.setSize(200, 500);
frame.setVisible(true);
}
});
}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值