积微成著 不积跬步,无以至千里

首页   >   web开发   >   SwingQQ消息来了好友头像闪动

SwingQQ消息来了好友头像闪动

在使用QQ的时候,当有人给我们发消息,好友列表里面,他(她)的头像就会开始闪烁,如果收起了分组,那么好友分组又会开始闪烁,这个功能非常的强大,也非常的实用和人性化,能够第一时间通知用户,极大的提升了用户体验。

之前在iteye上发表的SwingQQ项目中已经有这个功能了,但是杂糅在了一起,看起来不太方便,今天来单独就这个功能写一篇文章,方便大家更清晰的学习如何实现这个功能,来几张效果图看看:

消息来了好友头像闪动

上图中,是展开状态下,我点击消息来啦的按钮之后,好友1-1的头像就会开始来回闪动,向右偏移了1个单位,看起来不太明显,大家以那个企鹅的眼睛来做对比,就会发现还是有点儿偏差的。

消息来了好友头像闪动

上面这是一张收缩状态下的效果图,间隔半秒钟闪烁一次,就是空和名字轮流着显示。

消息来了好友头像闪动

而当我点击别闪了按钮的时候,则会立即停止闪动,并还原到最初状态,相当于我们在QQ上打开了这个好友的对话框。

好了,效果图就放到这里了,大家待会儿自己运行代码看效果,毕竟还是动态的才会有感觉。

实现起来并不是很难,就是不断的切换显示,再加上线程的sleep来一个停顿的效果,就可以实现闪烁的效果了

package com.wolffy.frame;

import com.wolffy.node.BuddyTreeNode;
import com.wolffy.node.GroupTreeNode;
import com.wolffy.ui.MyTreeUI;
import com.wolffy.util.ImgUtils;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTree;
import javax.swing.WindowConstants;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;
import java.awt.BorderLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;


/**
 * Created by SongFei on 2017/12/18.
 */
public class FlashFrame extends JFrame {

    private JPanel jPanel;

    private JTree jTree;

    private JButton jButton1;
    private JButton jButton2;

    private DefaultMutableTreeNode root;
    private DefaultTreeModel model;

    // 定义两个变量,记录第一个分组和第一个好友节点,因为是案例,所以就写死这两个
    private GroupTreeNode groupTreeNode;
    private BuddyTreeNode buddyTreeNode;

    public FlashFrame() {
        initGUI();
    }

    private void initGUI() {
        setSize(700, 375);
        //setUndecorated(true);
        setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);

        jPanel = new JPanel();
        getContentPane().add(jPanel, BorderLayout.CENTER);

        root = new DefaultMutableTreeNode();
        model = new DefaultTreeModel(root);
        for (int i = 1; i <= 3; i++) {
            GroupTreeNode cate = new GroupTreeNode(ImgUtils.getIcon("arrow_left.png"), "我的分组" + i);
            if (i == 1) {
                groupTreeNode = cate;
                oldName = cate.getNameLabel().getText();
            }
            for (int j = 1; j <= 3; j++) {
                BuddyTreeNode node = new BuddyTreeNode(ImgUtils.getIcon("avatar.png"), "好友" + i + "-" + j, "人生若只如初见");
                if (i == 1 && j == 1) {
                    buddyTreeNode = node;
                }
                cate.add(node);
            }
            root.add(cate);
        }
        jTree = new JTree(model);
        jTree.setUI(new MyTreeUI());
        jPanel.add(jTree);

        jButton1 = new JButton("消息来啦");
        jPanel.add(jButton1);
        jButton1.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                if (!isFlashing) {
                    new FlashThread().start();
                    isContinue = true;
                    isFlashing = true;
                }
            }
        });

        jButton2 = new JButton("别闪了");
        jPanel.add(jButton2);
        jButton2.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                isContinue = false;
                isFlashing = false;
                if (jTree.isExpanded(new TreePath(groupTreeNode.getPath()))) {
                    buddyTreeNode.getIconLabel().setBounds(8, 4, 42, 42);
                } else {
                    groupTreeNode.getNameLabel().setText(oldName);
                }
            }
        });
    }

    /**
     * 是否继续运行线程,因为要实现消息通知的效果,所以就必须循环监听,要给它一个退出的机会
     */
    private boolean isContinue = true;

    /**
     * 在点击取消闪烁的时候,不一定刚好把名字闪回去,所以记录一下,点击的时候强行还原回去
     */
    private String oldName;

    /**
     * 是否已经在闪烁,防止重复点击启动了多个线程,这样会越闪越快,24K钛合金的眼睛都不够用
     */
    private boolean isFlashing;

    /**
     * 闪烁的线程,必须另开线程,不然会导致主进程卡死
     */
    class FlashThread extends Thread {
        @Override
        public void run() {
            try {
                // 通过一个标记来控制是否继续这个线程
                while (isContinue) {
                    // 没展开分组时,闪烁的就是分组名称,在设置为空和名字之间来回切换
                    // 展开了分组时,闪烁的就是好友头像,动态切换头像区域的坐标
                    if (jTree.isExpanded(new TreePath(groupTreeNode.getPath()))) {
                        buddyTreeNode.getIconLabel().setBounds(9, 5, 42, 42);
                        Thread.sleep(500);
                        model.reload(buddyTreeNode);// 这一步很关键,起到刷新的作用,否则必须点一下才有效果

                        buddyTreeNode.getIconLabel().setBounds(8, 4, 42, 42);
                        Thread.sleep(500);
                        model.reload(buddyTreeNode);
                    } else {
                        groupTreeNode.getNameLabel().setText("");
                        Thread.sleep(500);
                        model.reload(groupTreeNode);

                        groupTreeNode.getNameLabel().setText(oldName);
                        Thread.sleep(500);
                        model.reload(groupTreeNode);
                    }
                }
            } catch (InterruptedException e1) {
                e1.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        FlashFrame flashFrame = new FlashFrame();
        flashFrame.setVisible(true);
        flashFrame.setLocationRelativeTo(null);
    }

}

为了方便,代码就放到一起了,大家可以copy过去直接运行就可以看到效果,间隔时间和头像闪烁的偏移量,大家都可以根据自己的需要来设置。

需要注意的地方就是一定要另起一个线程,否则会导致主进程卡死,好了,这里就不再多说了,其他需要注意的地方,我已经在代码中都写了注释了!

QQ群:积微成著官方群(686430774),验证消息:积微成著

站长Q:1347384268(加好友请注明来意)

分享到:

欢迎分享本文,转载请注明出处!

作者:不忘初心

发布时间:2017-12-18

永久地址:http://www.jiweichengzhu.com/article/95db495c5c494fe7b59d9cc3510cfd49