最近几天闲着没事,练习了一下。编写了一个模仿QQ的界面,主要是练习Swing。呵呵,基本上使用到了我博客前面讲的各种技术,在这里跟大家分享了。我们先来看看主界面:

     

左边的界面是用Swing编写的,右边的QQ的原界面,大致的界面已经很像了但qq的按钮的确很难做试了几种方法但效果都不好。还有就是QQ的发光文字用Swing挺难实现的,界面并没有细化。像搜索栏可以再加上一层渐变,联系人列表中的热度和群很简单这里就不实现了。

先来看看菜单按钮的实现:

第一种状态:

第二种状态:

这里使用了两个图片:

来看看按钮的代码:

package com.flyingwind.tutorial; import java.awt.AlphaComposite; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Image; import java.awt.Paint; import java.awt.RadialGradientPaint; import java.awt.Shape; import java.awt.geom.Ellipse2D; import java.awt.geom.Point2D; import javax.swing.JButton; public class RButton extends JButton { Image image; boolean flag=false; boolean isheight=false; public boolean isIsheight() { return isheight; } public void setIsheight(boolean isheight) { this.isheight = isheight; } public boolean isFlag() { return flag; } public void setFlag(boolean flag) { this.flag = flag; } public Image getImage() { return image; } public void setImage(Image image) { this.image = image; } Image rimage; int xs,ys,widths,heights; public RButton(Image im,Image old,int sizes) { super(""); image=im; rimage=old; // 这些声明把按钮扩展为一个圆而不是一个椭圆。 setPreferredSize(new Dimension(sizes,sizes)); //这个调用使JButton不画背景,而允许我们画一个圆的背景。 setContentAreaFilled(false); //this.setBackground(Color.GRAY); } // 画圆的背景和标签 protected void paintComponent(Graphics g) { Graphics2D g2d = (Graphics2D) g; if (getModel().isArmed()) { // 你可以选一个高亮的颜色作为圆形按钮类的属性 //g.setColor(Color.lightGray); if(rimage!=null){ g.drawImage(rimage, xs, ys, widths, heights, null); } } else { /*Paint f=new GradientPaint(0.0f, 0.0f, new Color(255, 255, 255, 100), 0,getSize().height,new Color(255, 255, 255, 0),false);*/ Paint p; if(isheight){ p = new RadialGradientPaint(new Point2D.Double(getWidth() / 2.0f, getHeight() / 2.0f), getWidth() / 2.0f, new float[] { 0.0f, 1.0f }, new Color[] { new Color(51, 153, 255), new Color(255, 255, 255, 200) }); }else{ p = new RadialGradientPaint(new Point2D.Double(getWidth() / 2.0f, getHeight() / 2.0f), getWidth() / 2.0f, new float[] { 0.0f, 1.0f }, new Color[] { new Color(51, 153, 255), new Color(255, 255, 255, 100) }); } g2d.setPaint(p); g2d.fillOval(0, 0, getSize().width - 1, getSize().height - 1); if(image!=null){ if (flag) { AlphaComposite ac2 = AlphaComposite.getInstance( AlphaComposite.SRC_OVER, 0.5f); g2d.setComposite(ac2); } g2d.drawImage(image, xs, ys, widths, heights, null); } //g.setColor(getBackground()); } //这个调用会画一个标签和焦点矩形。 super.paintComponent(g); } public void setPoints(int x,int y,int width,int height){ this.xs=x; this.ys=y; this.widths=width; this.heights=height; } // 用简单的弧画按钮的边界。 protected void paintBorder(Graphics g) { g.setColor(new Color(0,0,0,30)); g.drawOval(0, 0, getSize().width - 1, getSize().height - 1); } // 侦测点击事件 Shape shape; public boolean contains(int x, int y) { // 如果按钮改变大小,产生一个新的形状对象。 if (shape == null || !shape.getBounds().equals(getBounds())) { shape = new Ellipse2D.Float(0, 0, getWidth(), getHeight()); } return shape.contains(x, y); } }

对于按钮我们也进行了封装:

package com.flyingwind.tutorial; import java.awt.Color; import java.awt.Dimension; import java.awt.GradientPaint; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Image; import java.awt.Paint; import javax.swing.Icon; import javax.swing.JLabel; public class GnButton extends JLabel { private boolean flag=false; private Image image; public Image getImage() { return image; } public void setImage(Image image) { this.image = image; } public GnButton(Icon icon){ super(icon); } public GnButton(Image image){ super(""); } public GnButton(int size){ super(""); setPreferredSize(new Dimension(size,size)); } public boolean isFlag() { return flag; } public void setFlag(boolean flag) { this.flag = flag; } protected void paintComponent(Graphics g) { Graphics2D g2d = (Graphics2D) g; if (isFlag()) { Paint f = new GradientPaint(0.0f, 0.0f, new Color(255, 255, 255, 100), 0, 50, new Color(255, 255, 255, 200), true); g2d.setPaint(f); // g2d.fillRect(0, 0, this.getWidth(), this.getHeight()); g2d.fillRoundRect(1, 1, this.getWidth() - 2, this.getHeight() - 2, 3, 3); f=null; } g2d.drawImage(image, 5, 5, 18, 20, null); } }

在鼠标悬停在按钮上时按钮就会发光。

接下来看看这个按钮,在按钮的边缘使用了黑色边缘调整了透明度,再用一个白色的边框表现出高光。中间使用了线性渐变。

package com.flyingwind.tutorial; import java.awt.AlphaComposite; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Image; import java.awt.LinearGradientPaint; import java.awt.Paint; import java.awt.Shape; import java.awt.geom.RoundRectangle2D; import javax.swing.JButton; public class BaseButton extends JButton { Image image; boolean flag=false; boolean isheight=false; int xs,ys,widths,heights; public boolean isIsheight() { return isheight; } public void setIsheight(boolean isheight) { this.isheight = isheight; } public boolean isFlag() { return flag; } public void setFlag(boolean flag) { this.flag = flag; } public Image getImage() { return image; } public void setImage(Image image) { this.image = image; } public BaseButton(Image im,String text,int width,int height){ super(""); if(text!=null){ setText(text); } image=im; setPreferredSize(new Dimension(width,height)); //这个调用使JButton不画背景, setContentAreaFilled(false); } // 画圆的背景和标签 protected void paintComponent(Graphics g) { Graphics2D g2d = (Graphics2D) g; if (getModel().isArmed()) { // 你可以选一个高亮的颜色作为圆形按钮类的属性 //g.setColor(Color.lightGray); if(image!=null){ g.drawImage(image, xs, ys, widths, heights, null); } if (!getText().equals("")) { g2d.drawString(getText(), xs+widths+4, ys+heights-heights/4); } } else { /*Paint f=new GradientPaint(0.0f, 0.0f, new Color(255, 255, 255, 100), 0,getSize().height,new Color(255, 255, 255, 0),false);*/ if (isheight) { //画出多点线性渐变 Paint oldPaint = g2d.getPaint(); LinearGradientPaint p; p = new LinearGradientPaint(0.0f, 0.0f, 0.0f, 20.0f, new float[] { 0.0f, 0.5f, 0.501f, 1.0f }, new Color[] { new Color(255,255,255,200), new Color(255,255,255,0), new Color(255,255,255,0), new Color(255,255,255,200) }); g2d.setPaint(p); //g2d.fillRect(0, 0, getWidth(), 21); g2d.fillRoundRect(1, 1, getWidth()-4, getHeight()-4, 1, 1); g.setColor(new Color(255, 255, 255, 200)); /* * g.drawOval(0, 0, getSize().width - 1, getSize().height - 1); */ //画出高光部分 g.drawRoundRect(1, 1, getSize().width-4, getSize().height-4, 1, 1); g2d.setPaint(oldPaint); } if(image!=null){ if (flag) { AlphaComposite ac2 = AlphaComposite.getInstance( AlphaComposite.SRC_OVER, 0.5f); g2d.setComposite(ac2); } g2d.drawImage(image, xs, ys, widths, heights, null); if (!getText().equals("")) { g2d.drawString(getText(), xs+widths+4, ys+heights-heights/4); } } //g.setColor(getBackground()); } //这个调用会画一个标签和焦点矩形。 //super.paintComponent(g); } public void setPoints(int x,int y,int width,int height){ this.xs=x; this.ys=y; this.widths=width; this.heights=height; } // 用简单的弧画按钮的边界。 protected void paintBorder(Graphics g) { if (isheight) { g.setColor(new Color(0, 0, 0, 100)); /* * g.drawOval(0, 0, getSize().width - 1, getSize().height - 1); */ g.drawRoundRect(0, 0, getSize().width-2, getSize().height-2, 1, 1); } } // 侦测点击事件 Shape shape; public boolean contains(int x, int y) { // 如果按钮改变大小,产生一个新的形状对象。 if (shape == null || !shape.getBounds().equals(getBounds())) { /*shape = new Ellipse2D.Float(0, 0, getWidth(), getHeight());*/ shape=new RoundRectangle2D.Double(0, 0, this.getWidth(), this.getHeight(), 3, 3); } return shape.contains(x, y); } }

我们再来看看发光的文字是怎么实现的例如这样的效果,就是调用以下方法实现的。

private void drawText(Graphics2D g2, int size, float opacity) { Composite oldComposite = g2.getComposite(); float preAlpha = 1.0f; if (oldComposite instanceof AlphaComposite && ((AlphaComposite) oldComposite).getRule() == AlphaComposite.SRC_OVER) { preAlpha = ((AlphaComposite) oldComposite).getAlpha(); } g2.setFont(getFont()); FontMetrics metrics = g2.getFontMetrics(); int ascent = metrics.getAscent(); int heightDiff = (metrics.getHeight() - ascent) / 2; g2.setColor(new Color(255,255,255,150)); double tx = 2.0; double ty = 2.0 + heightDiff - size; g2.translate(tx, ty); for (int i = -size; i <= size; i++) { for (int j = -size; j <= size; j++) { double distance = i * i + j * j; float alpha = opacity; if (distance > 0.0d) { alpha = (float) (1.0f / ((distance * size) * opacity)); } alpha *= preAlpha; if (alpha > 1.0f) { alpha = 1.0f; } g2.setComposite(AlphaComposite.SrcOver.derive(alpha)); g2.drawString(this.getText(), i + size-3, j + size + ascent-3); } } g2.setComposite(oldComposite); g2.setColor(Color.BLACK); g2.drawString(this.getText(), size-3, size + ascent-3); g2.translate(-tx, -ty); }

好友列表的实现很简单,使用JTree来实现的。有人要问这样的效果是怎样实现的,其实也不难主要是利用了树的样式将树平行化

package com.flyingwind.tutorial; import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.event.*; import javax.swing.plaf.*; import javax.swing.plaf.basic.*; import javax.swing.tree.*; public class TreeUI extends BasicTreeUI { private static JTree tree; private JViewport parentViewport; private VariableLayoutCache layoutCache; public static ComponentUI createUI(JComponent c) { return new TreeUI(); } public void installUI(JComponent c) { if ( c == null ) throw new NullPointerException( "null component passed to BasicTreeUI.installUI()" ); tree = (JTree)c; tree.addTreeSelectionListener(new TreeSelectionListener() { public void valueChanged(TreeSelectionEvent e) { layoutCache.invalidatePathBounds(e.getOldLeadSelectionPath()); layoutCache.invalidatePathBounds(e.getNewLeadSelectionPath()); updateSize(); tree.repaint(); } }); tree.addHierarchyListener(new HierarchyListener() { public void hierarchyChanged(HierarchyEvent e) { if (e.getID() == HierarchyEvent.HIERARCHY_CHANGED && (e.getChangeFlags() & HierarchyEvent.PARENT_CHANGED) != 0 && e.getChangedParent() instanceof JViewport) { parentViewport = (JViewport) e.getChangedParent(); } } }); super.installUI(c); } /** * 安装样式 */ protected void installDefaults() { super.installDefaults(); if(tree.getBackground() == null || tree.getBackground() instanceof UIResource) { tree.setBackground(UIManager.getColor("Tree.background")); } if(getHashColor() == null || getHashColor() instanceof UIResource) { setHashColor(UIManager.getColor("Tree.hash")); } if (tree.getFont() == null || tree.getFont() instanceof UIResource) tree.setFont( UIManager.getFont("Tree.font") ); setExpandedIcon(null); setCollapsedIcon(null); setLeftChildIndent(0); setRightChildIndent(0); LookAndFeel.installProperty(tree, "rowHeight", UIManager.get("Tree.rowHeight")); largeModel = (tree.isLargeModel() && tree.getRowHeight() > 0); Object scrollsOnExpand = UIManager.get("Tree.scrollsOnExpand"); if (scrollsOnExpand != null) { LookAndFeel.installProperty( tree, "scrollsOnExpand", scrollsOnExpand); } UIManager.getDefaults().put("Tree.paintLines", false); UIManager.getDefaults().put("Tree.lineTypeDashed", false); } protected AbstractLayoutCache createLayoutCache() { layoutCache = new VariableLayoutCache(); return layoutCache; } private class VariableLayoutCache extends VariableHeightLayoutCache { public Rectangle getBounds(TreePath path, Rectangle placeIn) { Rectangle rect = super.getBounds(path, placeIn); if (rect != null && parentViewport != null) { rect.width = parentViewport.getWidth() - 2; } return rect; } } }

至于圆角界面的实现大家可以看我以前写的博客,里边有介绍圆角界面的实现,还有靠边自动停靠等内容。为了实现颜色变换界面底层使用了单一的颜色。而上边的组件都是透明的,实现换色就很简单了,如果要换肤功能,也只需要在界面上多花一层透明的图片就可以了。

下面是聊天窗口:

上面的界面是Swing的,下面的是QQ的,效果已经很接近了。按钮部分没有做调整,大家可以自己发挥。聊天窗口制作很简单主要是窗口的布局比较麻烦左边的分割区用到了JSplitPane是下面的部分可以上下拖动。

来看看分割部分的代码:

JSplitPane topSplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT); topSplitPane.setBorder(null); // 删除缺省边界 topSplitPane.setOneTouchExpandable(false); topSplitPane.setOpaque(false); topSplitPane.setResizeWeight(0.7D); topSplitPane.setDividerSize(1);//设置分隔条大小 topSplitPane.setLeftComponent(messageShow); topSplitPane.setRightComponent(message); messagePanel.add(topSplitPane,BorderLayout.CENTER);

分割高度不能设置为0不然鼠标会找不到焦点。

然后是基本设置界面:

上面的界面是Swing的,下面的是QQ的,面板右边的区域制作很简单只需要加载一个CardLayout就可以了我在这里就不实现了,左边的面板使用了一个自己编写的组件,呵呵!自我觉得还挺像的,来看看代码吧!

class JGroupContainer extends JPanel { private JButton bttGroupTitle = new JButton(); private JPanel pMembers = new JPanel(); private JScrollPane sp; public JGroupContainer() { this(""); } public JGroupContainer(String name) { this(name, UIManager.getColor("Desktop.background")); } /** * @param name String 组名 * @param background Color 成员组件所在面板背景色 */ public JGroupContainer(String name, Color background) { bttGroupTitle.setText(name); bttGroupTitle.setFocusable(false); pMembers.setLayout(new GroupLayout(5, 5)); this.setLayout(new BorderLayout()); this.add(bttGroupTitle, BorderLayout.NORTH); pMembers.setBackground(background); Color thumbColor = UIManager.getColor("ScrollBar.thumb"); Color trackColor = UIManager.getColor("ScrollBar.track"); Color trackHighlightColor = UIManager.getColor( "ScrollBar.trackHighlight"); UIManager.put("ScrollBar.thumb", background); UIManager.put("ScrollBar.track", background); UIManager.put("ScrollBar.trackHighlight", background); sp = new JScrollPane(pMembers); sp.setHorizontalScrollBarPolicy( JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); this.add(sp, BorderLayout.CENTER); collapse(); UIManager.put("ScrollBar.thumb", thumbColor); UIManager.put("ScrollBar.track", trackColor); UIManager.put("ScrollBar.trackHighlight", trackHighlightColor); } /** * 设置间距 * @param hgap int 横间距 * @param vgap int 竖间距 */ public void setMemberGap(int hgap, int vgap) { pMembers.setLayout(new GroupLayout(hgap, vgap)); } /** * 取得组的标题按钮 * @return JButton */ public JButton getTitleButton() { return bttGroupTitle; } /** * 取得组的成员组件面板 * @return JPanel */ public JPanel getMembersContainer() { return pMembers; } /** * 收缩组 */ public void collapse() { sp.setVisible(false); this.revalidate(); } /** * 展开组 */ public void expand() { sp.setVisible(true); this.revalidate(); } /** * 设置组名 * @param name String 组名 */ public void setName(String name) { bttGroupTitle.setText(name); } /** * 取得组名 * @return String */ public String getName() { return bttGroupTitle.getText(); } /** * 添加一个成员组件 * @param index int 顺序号 * @param c Component 成员组件 */ public void addMember(int index, Component c) { pMembers.add(c, index); pMembers.doLayout(); } /** * 删除一个成员组件 * @param index int 顺序号 */ public void removeMember(int index) { pMembers.remove(index); pMembers.doLayout(); } /** * 取得一个成员组件 * @param index int 顺序号 * @return Component 成员组件 */ public Component getMember(int index) { return pMembers.getComponent(index); } /** * 取得全部成员组件 * @return Component[] 成员组件 */ public Component[] getMembers() { Component coms[] = new Component[getMemberCount()]; for (int i = 0; i < coms.length; i++) { coms[i] = pMembers.getComponent(i); } return coms; } /** * 取得成员组件总数 * @return int 总数 */ public int getMemberCount() { return pMembers.getComponentCount(); } /** * 重写的toString方法 * @return String */ public String toString() { return getName(); } } }

大致的QQ界面已经能实现的八九成了,如果大家有什么问题可以给我留言。

如果对java界面方面有兴趣的朋友欢迎加入4601398QQ群。

Swing制作高仿QQ界面包含主界面、聊天窗口、系统设置窗口|圆角界面|透明|颜色|渲染|换肤相关推荐

  1. 新版2022高仿QQ,利用Java swing1:1模仿qq编写的聊天程序

    基于MVC Swing的高仿QQ聊天软件 FakeQQ是一个基于MVC架构构建的GUI聊天项目.前端使用swing作为前端框架.服务端采用mysql存储数据,使用sorket传输数据,实现了一些基本的 ...

  2. 如何使用MFC编写自定义UI界面【附高仿QQ 2014登陆界面范例程序】

    地址: http://blog.csdn.net/hujkay作者:Jekkay Hu(34538980@qq.com)关键词:MFC, 编写异行窗体,自定义UI控件,VC++,异形控件,高仿QQ登陆 ...

  3. android+qq底部界面,Android 高仿QQ 界面滑动效果

    Android高仿QQ界面滑动效果 点击或者滑动切换画面,用ViewPager实现, 首先是布局文件: android:layout_width="match_parent" an ...

  4. 高仿QQ电脑管家8 界面

    去年发了高仿QQ2012登录界面,最近又优化了下代码,先看效果图 换肤 的代码和高仿QQ2012登录界面一样,代码请看那边; 这次主要是优化了控件的组合,和贴图方式 整体的框架: 首先是一个窗口,这里 ...

  5. C#\WPF高仿QQ音乐V12.8界面篇《2》

    动态效果图 获取源码 通过关注个人公众号:BigBearIT,如下 通过后台回复关键词:WPF高仿QQ音乐源码 即可获得源码下载地址. 后续版本更新后会在公众号通知 /**************** ...

  6. Android:高仿QQ头像截取升级版

    观看此篇文章前,请先阅读上篇文章:高仿QQ头像截取: 本篇之所以为升级版,是在截取头像界面添加了与qq类似的阴影层(裁剪区域以外的部分),且看效果图:   为了适应大家不同需求,这次打了两个包,及上图 ...

  7. 安卓高仿QQ头像截取升级版

    观看此篇文章前,请先阅读上篇文章:高仿QQ头像截取: 本篇之所以为升级版,是在截取头像界面添加了与qq类似的阴影层(裁剪区域以外的部分),且看效果图:   为了适应大家不同需求,这次打了两个包,及上图 ...

  8. 【Android】史上最简单,一步集成侧滑(删除)菜单,高仿QQ、IOS。

    本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布 转载请标明出处: http://blog.csdn.net/zxt0601/article/details/53157090 本文出 ...

  9. 高仿QQ即时聊天软件开发系列之三登录窗口用户选择下拉框

    上一篇高仿QQ即时聊天软件开发系列之二登录窗口界面写了一个大概的布局和原理 这一篇详细说下拉框的实现原理 先上最终效果图 一开始其实只是想给下拉框加一个placeholder效果,让下拉框在未选择未输 ...

最新文章

  1. RabbitMQ学习总结(3)——入门实例教程详解
  2. vue入门教程(二)
  3. 取得成本中心组、成本要素组层级的几个BAPI
  4. 【网络收集】order by 自定义排序
  5. java---连接池的学习
  6. java网络图片与二进制字符串相互转换
  7. 在视觉任务上大幅超越ReLU的新型激活函数
  8. andriod 获取attrs_Android:具有attrs.xml中对象引用的Custom View,...
  9. Python爬取拉勾网数据分析职位
  10. Causal Representation Learning for Out-of-Distribution Recommendation
  11. IT行业里的热门技术 | 热门IT技术项目分享 | 详细介绍一下机器人技术
  12. 云端数据丢失?谨记三大教训!|中机智库
  13. vue3 setup 怎么获取vue实例上的全局变量
  14. 罗克韦尔定时器的一些应用
  15. 七月 青春流浪的季节
  16. java实现三个和尚比较身高,得到最高的身高,单位:厘米(Scanner运用,三元运算符运用)
  17. MATLAB两种光复合在一起的杨氏双缝干涉的模拟仿真
  18. unity3D实践报告-忍者跑酷
  19. 关于产品的一些思考——腾讯之UIDesigner
  20. linux下稳定性测试软件详解

热门文章

  1. 微信多开器 Python窗口编程 隔离运行(一)
  2. 【pytorch】深度学习所需算力估算:flops及模型参数量
  3. ppt图片设计素材下载网站搭建模板
  4. ajax重要且常用参数
  5. 游戏服务器多钱一个月呢?
  6. 微软高级工程师带你 21 天入门机器学习
  7. es6怎么将对象转换为数组
  8. aptos中文版白皮书-前Facebook团队打造明星公链,三个优势:Move语言、Move虚拟机、合约可升级
  9. C-Tech Awards 2016最具价值大奖获奖名单新鲜出炉
  10. Office2007打开word和excel文件速度慢,在两个打开的word文件之间切换的速度慢,如何处理?...