共享模仿QQ好友列表,点击表头展开对应Tab的源码。
Tab的展开使用动画,有兴趣的还可以去看看Easing的各种动画效果。
package com.tur.demo;import info.clearthought.layout.TableLayout;
import info.clearthought.layout.TableLayoutConstraints;import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;/**
 * 模仿QQ好友列表的布局, 展开一个tab的同时,其他tab被隐藏.
 *
 * 主要使用API:
 *  1. addTab: 添加一个新的tab
 *  2. removeTab: 删除一个tab,这个方法有待改进
 *  3. setExpandedTabIndex: 指定要展开的tab,默认所有的tab都是收缩起来的。界面初始化的时候经常有必要调用这个方法设置一个tab是展开的.
 *
 * tab的标题栏如果想要好看一些,可以使用图片美化,也可以手动绘制好一点的样式。
 */
public class ScrollingTabPane extends JComponent {
    private TableLayout layout;
    private List<Tab> tabs;
    private MouseListener mouseListener;    private int expandedTabIndex = 0; // 被展开的tab的index
    public static final int COLLAPSE_TAB_HEIGHT = 30; // 收陇的tab标签的高度    public ScrollingTabPane() {
        tabs = new LinkedList<Tab>();
        mouseListener = new TabMouseListener();
        layout = new TableLayout(new double[]{TableLayout.FILL}, new double[]{});
        setLayout(layout);
    }    /**
     * 添加一个新的tab
     * @param com - 要添加到tab里的组件
     */
    public void addTab(JComponent com, String title) {
        Tab tab = new Tab(com, title);
        tabs.add(tab);        int rowCount = layout.getNumRow();
        layout.insertRow(rowCount, COLLAPSE_TAB_HEIGHT);
        add(tab, "0, " + rowCount);
    }    /**
     * 删除第index个tab
     * @param index - 删除的tab的index
     */
    public void removeTab(int index) {
        if (index < 0 || index >= tabs.size()) { return; }        Container tab = tabs.get(index);
        int row = layout.getConstraints(tab).row1;        // 必须同时从layout和component中移除
        remove(tab);
        tabs.remove(tab);
        layout.deleteRow(row);        if (row == expandedTabIndex) {
            // 如果删除的是展开的tab,则展开它下面的tab
            setExpandedTabIndex(expandedTabIndex);
        } else if (row < expandedTabIndex) {
            // 如果删除的tab在展开的tab上面,则展开的tab的index减一
            --expandedTabIndex;
        }        invalidate();
        validate();
    }    /**
     * 取得当前展开的tab的index
     * @return - 当前展开的tab的index
     */
    public int getExpandedTabIndex() {
        return expandedTabIndex;
    }    /**
     * 设置展开的tab的index,然后展开此tab并把其他的tab的高度设置为TAB_HEIGHT.
     * @param expandedTabIndex - 需要被展开的tab的index
     */
    public void setExpandedTabIndex(int expandedTabIndex) {
        this.expandedTabIndex = expandedTabIndex;        for (int i = 0; i < layout.getNumRow(); ++i) {
            layout.setRow(i, COLLAPSE_TAB_HEIGHT);
        }        layout.setRow(expandedTabIndex, TableLayout.FILL);        invalidate();
        validate();
    }    /**
     * 使用动画展开tab
     * @param tab - 需要展开的tab
     */
    private synchronized void expandingTab(Tab tab) {
        TableLayoutConstraints constraints = layout.getConstraints(tab);
        int row = constraints.row1;        if (row == expandedTabIndex) { return; }        int delay = 1000 / 250;
        int counter = 0;
        double height = 0;
        double tabHeight = layout.getRow(row);
        double maxTabHeight = getHeight() - COLLAPSE_TAB_HEIGHT * (layout.getNumRow() - 1);
        double deltaHeight = maxTabHeight - tabHeight;
        double stepRadian = Math.PI * 0.01;        while (height < maxTabHeight) {
            // sin的函数趋势是先快后慢, sin值的范围是[-1, 1].
            // 使用sin是为了展开tab结束的时候比开展的过程中慢一些,以致展开动画结束的效果平滑一些.
            height = tabHeight + deltaHeight * Math.sin(stepRadian * counter++);
            layout.setRow(row, height);            invalidate();
            validate();            try { Thread.sleep(delay); } catch(InterruptedException e) {}
        }        layout.setRow(row, TableLayout.FILL);
        layout.setRow(expandedTabIndex, COLLAPSE_TAB_HEIGHT);
        expandedTabIndex = row;        invalidate();
        validate();
    }    /**
     * 鼠标点击tab时的事件
     */
    private class TabMouseListener extends MouseAdapter {
        @Override
        public void mouseClicked(MouseEvent e) {
            final TabTitleBar titleBar = (TabTitleBar) e.getSource();
            final Tab tab = (Tab) titleBar.getParent();
            final TableLayoutConstraints constraints = layout.getConstraints(tab);            if (expandedTabIndex != constraints.row1) {
                new Thread() {
                    public void run() {
                        expandingTab(tab);
                    }
                }.start();
            }
        }
    }    /**
     * 第个tab包含标题栏和内容区域
     */
    private class Tab extends Container {
        JLabel titleBar;        public Tab(JComponent com, String title) {
            titleBar = new TabTitleBar(title);            double[] row = {TableLayout.FILL};
            double[] col = {COLLAPSE_TAB_HEIGHT, TableLayout.FILL};
            setLayout(new TableLayout(row, col));            add(titleBar, "0, 0");
            add(com, "0, 1");
        }
    }    /**
     * 每个tab的标题栏
     */
    private class TabTitleBar extends JLabel {
        public TabTitleBar(String title) {
            super(title);            setHorizontalAlignment(SwingConstants.CENTER);
            setOpaque(true);
            setBorder(BorderFactory.createEtchedBorder());
            addMouseListener(mouseListener);
        }
    }    /**
     * 创建frame并显示
     */
    private static void createAndShowGui() {
        JFrame frame = new JFrame("Scrolling TabPane Demo");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(270, 600);
        frame.setLocationRelativeTo(null);        ScrollingTabPane tabPane = new ScrollingTabPane();
        List<JComponent> coms = new ArrayList<JComponent>();        JPanel panel = new JPanel();
        panel.add(new JButton("Button"));
        panel.add(new JSlider(0, 100, 40));
        panel.add(new JCheckBox("Check Box"));
        panel.add(new JRadioButton("Radio Button"));        coms.add(panel);
        coms.add(new JPanel());
        coms.add(new JPanel());
        coms.add(new JPanel());
        coms.add(new JTextArea("Text Area"));
        coms.add(new JPanel());        Random rand = new Random(System.nanoTime());
        for (int i = 0; i < coms.size(); ++i) {
            JComponent com = coms.get(i);            tabPane.addTab(com, "Tab " + i); // Add a new tab            // Set the tab's background using random color
            if (com instanceof JPanel) {
                com.setBackground(new Color(rand.nextInt(256), rand.nextInt(256), rand.nextInt(256)));
            }
        }        // Expanding the second tab
        tabPane.setExpandedTabIndex(0);        tabPane.removeTab(2); // 删除第2个tab        frame.getContentPane().add(tabPane);
        frame.setVisible(true);
    }    public static void main(String[] args) {
        createAndShowGui();
    }
}
qq

解决方案 »

  1.   

    需要提示的是,布局管理器使用的是TableLayout,这个包需要自己去下载:http://search.maven.org/remotecontent?filepath=tablelayout/TableLayout/20050920/TableLayout-20050920.jar
      

  2.   

    谢谢楼楼主分享,苹果系统就是好啊
    不过这在windows下,很难看美化一下
     不过需要添加苹果风格的美化jar包
    下载地址:
    http://download.csdn.net/detail/kiritor/5109445代码中main方法中添加:
    public static void main(String[] args) {
         System.setProperty("Quaqua.tabLayoutPolicy", "wrap");
    if (!System.getProperty("os.name").toLowerCase().startsWith("mac")) { // 如果不是Mac
    // os
    // x,则更改窗体样式。
    try {
    Methods.invokeStatic(JFrame.class,
    "setDefaultLookAndFeelDecorated", Boolean.TYPE,
    Boolean.TRUE); // 主窗体样式
    Methods.invokeStatic(JDialog.class,
    "setDefaultLookAndFeelDecorated", Boolean.TYPE,
    Boolean.TRUE); // 子窗体样式
    } catch (NoSuchMethodException e) {
    e.printStackTrace();
    }
    }
    try {
    UIManager
    .setLookAndFeel("ch.randelshofer.quaqua.QuaquaLookAndFeel");
    } catch (Exception e) {
    }
            createAndShowGui();
        }
      

  3.   

    这个美化挺不错的。
    以前用XP的主题美化效果也只能达到这样了。
    要想更好的效果,得用WindowBlinds了。