高仿QQ源码 界面(3)

本文详细介绍了如何使用ViewPager和Fragment构建高仿QQ的界面,包括布局分析、数据源设置、Fragment的实现、Adapter的配置以及逻辑联动的实现步骤。重点讲解了在ViewPager中展示不同内容的Fragment,并通过适配器设置数据源。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

上次的图片莫名其妙的没了, 今天补上

分别为主布局, 和主布局的结构, 大致分为三个结构,RelativeLayout, ViewPager, RadioGroup, (这个布局只是能大概的模仿, 无法满足绝对的一样,因为之前没有考虑那么多, 后面会进行修改)
主布局
这是RadioButton的Style样式, 这样可以减少很多代码量, 方便后期更改, 最好养成这样的习惯
这是其中之一的ButtonSelector样式, , 通过checked属性来选择使用哪个drawable资源作为背景,其他几个类似

布局分析:

通过点击不同的RadioButton可以切换中间显示的内容布局, qq的不能通过滑动当前的页面来改变下面RadioButton的状态, 自己加上了这一点,中间的布局使用ViewPager+Fragment来实现, 通过点击RadioButton或者滑动pager来改变当前显示Fragment布局, 另外最上面的title的显示也是可以跟着联动的, 最初以为是简单的联动,显示个TextView,没发现title有一部分也是通过fragment加入, 这点先不去管, 先实现RadioButton, ViewPager,title这三个的联动就好,

第一步: 实现布局中的ViewPager , 不要选错了要是v4包下的ViewPager, 下面是局部代码


第二步: ViewPager数据源,由于是使用的ViewPager+Fragment实现的,所以List集合的泛型要用Fragment如下代码


第三步: 1. 实现ViewPager显示的Fragment类,这里写个四个pager ,所以使用了4个类继承自Fragment,重写其三个 方法,onCreate,当创建此类的时候执行, 最先执行的方法, 由系统调用,onCreateView,在onCreate之后执行,这个方法返回一个View对象, 这个对象就是要显示在pager中内容, 可以是任何的View对象, TextView, Button, ListView等等, View的子类对象,通过View的静态方法View.inflate(Context context, int resource,ViewGroup root), 三个参数分别是要调用该类的对象的Context, 可以直接通过getActivity()方法获取call此Activity的上下文, resource 是要显示的布局, 可以是简单的TextView, 也可以是复杂的ListView等, 这里为了实现分组等页面使用ListView布局, root是挂载到哪一个父布局之下, 这里填充的就是resource本身, 所以可以传入null, onActivityCreated() 创建Activity完成的时候调用(从名字推测是这样子的, 不得不说命名上Google做的还是很好的), 在onCreateView之后执行 对view的初始化就可以放入此方法里执行

附上局部源码 

activity_plus布局如下

这里因为要实现好友界面的二级折叠样式, 可以展开的ListView, 使用ExpandableListView控件代码如下

contact布局如下

因为ExpandableListView中也包含一个子ListView, 所以要使用BaseExpandableAdapter对象,创建MyBaseExpandableAdapter类,继承自BaseExpandableAdapter类, 实现其未实现的方法分别为 
1.getGroupCount(),返回父listView显示的数目, 
2.getChildrenCount(), 返回一个子ListView显示的数目
3.getGroup 返回值是Object类型, 需要返回父ListView中的data数据源
4.getChild() 返回值是Object类型, 需要返回子ListView中的data数据源
5.getGroupId() 返回一个long类型的值, 用于标示当前父ListView的item的ID
6.getChildId() 返回是一个long类型的值, 用于标示当前子ListView的timeID
7.hasStableIds() 返回一个boolean值, 返回true表示显示的布局会根据ID的改变而更新data显示, 这里返回true
8.getGroupView(), 返回父ListView显示的对象, 这里利用View.inflate方法, 引入布局并找到布里面的每个控件, 进行初始化, 赋值等操作
8.getChildView() 返回子ListView显示的对象, 这里利用View.inflate方法, 引入布局并找到布里面的每个控件, 进行初始化, 赋值等操作
9.isChildSelectable(), 设置子控件是否可以进行点击, 这里当然返回true , 若是返回false则无法点击展开后的子控件
其实这些方法很多看名字就大概知道方法的用处,  这里因为要俩个data数据源, 一个是控件的, 一个是展开后的子控件的, 所以adapter的构造器中传入三个参数, 下面是局部代码

下面是初始化parentView 的方法, 英语很差 不能做到像Google那样见名知意,

子布局View初始化方法

设置Children的data数据源

 第三步: 2.设置ViewPager的Adapter , 从前面的MyBaseExpandableAdapter中可以看出Adapter实际上是包含了ExpandableListView要显示的布局和布局上具体显示的data数据来源, 所以, 给ViewPager设置Adapter就是设置了布局和要显示的data数据源, 至于data数据在布局中是以何种方式显示的这就要看Adapter中对data数据的获取和设置了, 可以看上面的setChildrenData和initParentView方法, 下面是局部代码\

设置data数据源的

初始化ExpandableListView


第四步: 设置ViewPager的adapter, 看了上面的ExpandableListView设置adapter这个应该就简单多了

首先注意的是, 用ViewPager+Fragment的时候使用的adapter不一样, Googl为开发者专门提供了一个FragmentPagerAdapter的抽象类来给开发者使用,下面是我的adapter的代码

初始化ViewPager

第五步:实现逻辑联动, 因为这里的点击RadioButton和滑动ViewPager都能改变title的内容, 所以可以把这些都封装成一个方法, 并且把这三个的对象都放到三个数组中, 注意要让三个数组中的下标所对应的对象保证是一组的, 这样就可以通过访问这三个数组的第几个元素来实现三者之间的联动, 

三个数组对象


通过改变一个int类型的指示器的值来指向这三个数中的对象,

l利用指示器pointer设置当前显示的pager

RadioButton的监听事件

ViewPager的监听事件

比较懒, 直接把初始化RadioButton写成了一个方法, 类似的在控件比较多的情况用处还好, 这里体现不大

设置打印自定义的log信息

到此布局就结束了, 中间可能与有理解的错的, 欢迎讨论交流, 另外好多代码并没有使用ViewHolder进行优化
总结: 这样做确实可以实现那样的效果, 淡水有一点非常不好, 那就是, 每个Pager显示的标题应该是有没有Fragment类来决定的,. 不应该放到主的Activity中, 可以看到qq每个页面对应title显示的内容, 和右上角的描述也是不一样的, 所以这些title应该交给每个fragment来实现,可以把这些fragment必须要有的功能定义一个接口, 每个fragment页面实现这些接口, 自然也会去实现接口中所定义的方法, 这样比较符合设计模式, 当我后期再需要添加一个Fragment页面的时候就不需要修改很多的类,只需要实现自己定义的Fragment接口, 并且实现这些抽象方法就可以了, 相反若是没有对应接口来规范Fragment的话, 没添加一个pager就要修改与之关联的类, 引起很多麻烦, 也容易忘记修改造成不必要的bug, 本人学的并不好, 有什么不对的地方欢迎指出改正,一起交流学习进步
高仿QQ聊天 登录界面 import java.awt.BorderLayout; import java.awt.Color; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.File; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JScrollPane; import javax.swing.JTextField; import javax.swing.JTextPane; import javax.swing.UIManager; import javax.swing.text.BadLocationException; import javax.swing.text.SimpleAttributeSet; import javax.swing.text.StyleConstants; import javax.swing.text.StyledDocument; public class Test extends JFrame { private JScrollPane scrollPane = null; // 滚动 private JTextPane text = null; private Box box = null; // 放输入组件的容器 private JButton b_insert = null, b_remove = null, b_icon = null; // 插入按钮;清除按钮;插入图片按钮 private JTextField addText = null; // 文字输入框 // 字体名称;字号大小;文字样式;文字颜色;文字背景颜色 private JComboBox fontName = null, fontSize = null, fontStyle = null, fontColor = null, fontBackColor = null; private StyledDocument doc = null; // 非常重要插入文字样式就靠它了 public Test() { super("JTextPane Test"); try { // 使用Windows的界面风格 UIManager.setLookAndFeel ("com.sun.java.swing.plaf.windows.WindowsLookAndFeel"); } catch (Exception e) { e.printStackTrace(); } text = new JTextPane(); text.setEditable(false); // 不可录入 doc = text.getStyledDocument(); // 获得JTextPane的Document scrollPane = new JScrollPane(text); addText = new JTextField(18); String[] str_name = { "宋体", "黑体", "Dialog", "Gulim" }; String[] str_Size = { "12", "14", "18", "22", "30", "40" }; String[] str_Style = { "常规", "斜体", "粗体", "粗斜体" }; String[] str_Color = { "黑色", "红色", "蓝色", "黄色", "绿色" }; String[] str_BackColor = { "无色", "灰色", "淡红", "淡蓝", "淡黄", "淡绿" }; fontName = new JComboBox(str_name); // 字体名称 fontSize = new JComboBox(str_Size); // 字号 fontStyle = new JComboBox(str_Style); // 样式 fontColor = new JComboBox(str_Color); // 颜色 fontBackColor = new JComboBox(str_BackColor); // 背景颜色 b_insert = new JButton("插入"); // 插入 b_remove = new JButton("清空"); // 清除 b_icon = new JButton("图片"); // 插入图片 b_insert.addActionListener(new ActionListener() { // 插入文字的事件 public void actionPerformed(ActionEvent e) { insert(getFontAttrib()); addText.setText(""); } }); b_remove.addActionListener(new ActionListener() { // 清除事件 public void actionPerformed(ActionEvent e) { text.setText(""); } }); b_icon.addActionListener(new ActionListener() { // 插入图片事件 public void actionPerformed(ActionEvent arg0) { JFileChooser f = new JFileChooser(); // 查找文件 f.showOpenDialog(null); insertIcon(f.getSelectedFile()); // 插入图片 } }); box = Box.createVerticalBox(); // 竖结构 Box box_1 = Box.createHorizontalBox(); // 横结构 Box box_2 = Box.createHorizontalBox(); // 横结构 box.add(box_1); box.add(Box.createVerticalStrut(8)); // 两行的间距 box.add(box_2); box.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8)); // 8个的边距 // 开始将所需组件加入容器 box_1.add(new JLabel("字体:")); // 加入标签 box_1.add(fontName); // 加入组件 box_1.add(Box.createHorizontalStrut(8)); // 间距 box_1.add(new JLabel("样式:")); box_1.add(fontStyle); box_1.add(Box.createHorizontalStrut(8)); box_1.add(new JLabel("字号:")); box_1.add(fontSize); box_1.add(Box.createHorizontalStrut(8)); box_1.add(new JLabel("颜色:")); box_1.add(fontColor); box_1.add(Box.createHorizontalStrut(8)); box_1.add(new JLabel("背景:")); box_1.add(fontBackColor); box_1.add(Box.createHorizontalStrut(8)); box_1.add(b_icon); box_2.add(addText); box_2.add(Box.createHorizontalStrut(8)); box_2.add(b_insert); box_2.add(Box.createHorizontalStrut(8)); box_2.add(b_remove); this.getRootPane().setDefaultButton(b_insert); // 默认回车按钮 this.getContentPane().add(scrollPane); this.getContentPane().add(box, BorderLayout.SOUTH); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); this.setSize(600, 400); this.setLocationRelativeTo(null); this.setVisible(true); addText.requestFocus(); } /** * 插入图片 * * @param icon */ private void insertIcon(File file) { text.setCaretPosition(doc.getLength()); // 设置插入位置 text.insertIcon(new ImageIcon(file.getPath())); // 插入图片 insert(new FontAttrib()); // 这样做可以换行 } /** * 将文本插入JTextPane * * @param attrib */ private void insert(FontAttrib attrib) { try { // 插入文本 doc.insertString(doc.getLength(), attrib.getText() + "\n", attrib.getAttrSet()); } catch (BadLocationException e) { e.printStackTrace(); } } /** * 获取所需要的文字设置 * * @return FontAttrib */ private FontAttrib getFontAttrib() { FontAttrib att = new FontAttrib(); att.setText(addText.getText()); att.setName((String) fontName.getSelectedItem()); att.setSize(Integer.parseInt((String) fontSize.getSelectedItem())); String temp_style = (String) fontStyle.getSelectedItem(); if (temp_style.equals("常规")) { att.setStyle(FontAttrib.GENERAL); } else if (temp_style.equals("粗体")) { att.setStyle(FontAttrib.BOLD); } else if (temp_style.equals("斜体")) { att.setStyle(FontAttrib.ITALIC); } else if (temp_style.equals("粗斜体")) { att.setStyle(FontAttrib.BOLD_ITALIC); } String temp_color = (String) fontColor.getSelectedItem(); if (temp_color.equals("黑色")) { att.setColor(new Color(0, 0, 0)); } else if (temp_color.equals("红色")) { att.setColor(new Color(255, 0, 0)); } else if (temp_color.equals("蓝色")) { att.setColor(new Color(0, 0, 255)); } else if (temp_color.equals("黄色")) { att.setColor(new Color(255, 255, 0)); } else if (temp_color.equals("绿色")) { att.setColor(new Color(0, 255, 0)); } String temp_backColor = (String) fontBackColor.getSelectedItem(); if (!temp_backColor.equals("无色")) { if (temp_backColor.equals("灰色")) { att.setBackColor(new Color(200, 200, 200)); } else if (temp_backColor.equals("淡红")) { att.setBackColor(new Color(255, 200, 200)); } else if (temp_backColor.equals("淡蓝")) { att.setBackColor(new Color(200, 200, 255)); } else if (temp_backColor.equals("淡黄")) { att.setBackColor(new Color(255, 255, 200)); } else if (temp_backColor.equals("淡绿")) { att.setBackColor(new Color(200, 255, 200)); } } return att; } public static void main(String args[]) { new Test(); } /** * 字体的属性类 */ private class FontAttrib { public static final int GENERAL = 0; // 常规 public static final int BOLD = 1; // 粗体 public static final int ITALIC = 2; // 斜体 public static final int BOLD_ITALIC = 3; // 粗斜体 private SimpleAttributeSet attrSet = null; // 属性集 private String text = null, name = null; // 要输入的文本和字体名称 private int style = 0, size = 0; // 样式和字号 private Color color = null, backColor = null; // 文字颜色和背景颜色 /** * 一个空的构造(可当做换行使用) */ public FontAttrib() { } /** * 返回属性集 * * @return */ public SimpleAttributeSet getAttrSet() { attrSet = new SimpleAttributeSet(); if (name != null) StyleConstants.setFontFamily(attrSet, name); if (style == FontAttrib.GENERAL) { StyleConstants.setBold(attrSet, false); StyleConstants.setItalic(attrSet, false); } else if (style == FontAttrib.BOLD) { StyleConstants.setBold(attrSet, true); StyleConstants.setItalic(attrSet, false); } else if (style == FontAttrib.ITALIC) { StyleConstants.setBold(attrSet, false); StyleConstants.setItalic(attrSet, true); } else if (style == FontAttrib.BOLD_ITALIC) { StyleConstants.setBold(attrSet, true); StyleConstants.setItalic(attrSet, true); } StyleConstants.setFontSize(attrSet, size); if (color != null) StyleConstants.setForeground(attrSet, color); if (backColor != null) StyleConstants.setBackground(attrSet, backColor); return attrSet; } /** * 设置属性集 * * @param attrSet */ public void setAttrSet(SimpleAttributeSet attrSet) { this.attrSet = attrSet; } /* 后面的注释就不写了,一看就明白 */ public String getText() { return text; } public void setText(String text) { this.text = text; } public Color getColor() { return color; } public void setColor(Color color) { this.color = color; } public Color getBackColor() { return backColor; } public void setBackColor(Color backColor) { this.backColor = backColor; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getSize() { return size; } public void setSize(int size) { this.size = size; } public int getStyle() { return style; } public void setStyle(int style) { this.style = style; } } }
源码描述: 一、源码描述 GG是QQ高仿版,包括客户端和服务端,可在广域网部署使用,目前推出的最新版本为4.3。 GG 4.3版本增加的主要功能是:在视频聊天的过程中,随时可以启动录制,录制将把聊天双方的视频图像和双方的声音都录制下来生成一个标准的mp4文件,该mp4文件可用任意的影音播放器打开播放。 二、功能介绍 01、注册、登录、查找用户、添加好友、好友列表。 02、自拍头像。 03、文字聊天、字体设置、GIF动态表情、窗口震动、截图、手写板、登录状态(在线、离开、忙碌、勿打扰、隐身)、输入提醒) 04、群功能:创建群、加入群、退出群、群聊天 05、文件传送、文件夹传送(支持断点续传) 06、语音视频聊天 07、远程磁盘 08、远程协助 09、共享桌面(可以指定要共享的桌面区域) 10、可靠的P2P 11、网盘 12、离线消息 13、离线文件 14、托盘闪动:跟QQ完全一样,当接收到消息时,托盘会闪动对应好友的头像。点击头像,将弹出与好友的聊天框。 15、最近联系人列表 16、系统设置:开机自动启动、麦克风设备索引、摄像头设备索引,叉掉主窗口时关闭程序还是隐藏窗口。 17、聊天记录:支持本地保存和服务器端保存两种方式。 18、好友分组:新增/删除分组,修改分组名称,改变好友的所属分组。 19、打开聊天窗口时,自动显示上次交谈的最后一句话。 20、输入提醒:像QQ一样,当对方正在输入消息时,我这边的聊天框可以看到对方“正在输入”的提示。 21、自动记录:GG2014会自动记录上次打开的主界面的位置、大小;最后一次打开的聊天窗口的大小;最后一次设定的字体的颜色、大小等。 22、主窗体靠边自动隐藏。 23、录制视频聊天。 三、注意事项 1.当前版本服务端默认配置为内存虚拟数据库版本,不需要安装数据库。 2、开发环境为Visual Studio 2010,无数据库,使用.net 2.0开发。 2.将GG2014.Server文件夹拷贝到服务器上,运行GG2014.Server.exe。 3.修改客户端配置文件GG2014.exe.config中ServerIP配置项的值为服务器的IP。 4.运行客户端,注册帐号登录试用。 5.内置测试帐号为 10000,10001,10002,10003,10004;密码都是 1。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值