高仿QQ源码 界面(3)

时间:2022-12-28 08:36:00

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

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

布局分析:

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

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

高仿QQ源码 界面(3)

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

高仿QQ源码 界面(3)

第三步: 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的初始化就可以放入此方法里执行

附上局部源码 高仿QQ源码 界面(3)

activity_plus布局如下

高仿QQ源码 界面(3)高仿QQ源码 界面(3)

这里因为要实现好友界面的二级折叠样式, 可以展开的ListView, 使用ExpandableListView控件代码如下 高仿QQ源码 界面(3)
contact布局如下 高仿QQ源码 界面(3)
因为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的构造器中传入三个参数, 下面是局部代码 高仿QQ源码 界面(3)
下面是初始化parentView 的方法, 英语很差 不能做到像Google那样见名知意, 高仿QQ源码 界面(3)
子布局View初始化方法 高仿QQ源码 界面(3)
设置Children的data数据源 高仿QQ源码 界面(3)

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

设置data数据源的 高仿QQ源码 界面(3)
初始化ExpandableListView 高仿QQ源码 界面(3)


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

首先注意的是, 用ViewPager+Fragment的时候使用的adapter不一样, Googl为开发者专门提供了一个FragmentPagerAdapter的抽象类来给开发者使用,下面是我的adapter的代码 高仿QQ源码 界面(3)
初始化ViewPager 高仿QQ源码 界面(3)

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

三个数组对象 高仿QQ源码 界面(3)

通过改变一个int类型的指示器的值来指向这三个数中的对象, 高仿QQ源码 界面(3)
l利用指示器pointer设置当前显示的pager 高仿QQ源码 界面(3)
RadioButton的监听事件 高仿QQ源码 界面(3)
ViewPager的监听事件 高仿QQ源码 界面(3)
比较懒, 直接把初始化RadioButton写成了一个方法, 类似的在控件比较多的情况用处还好, 这里体现不大 高仿QQ源码 界面(3)
设置打印自定义的log信息 高仿QQ源码 界面(3)
到此布局就结束了, 中间可能与有理解的错的, 欢迎讨论交流, 另外好多代码并没有使用ViewHolder进行优化 总结: 这样做确实可以实现那样的效果, 淡水有一点非常不好, 那就是, 每个Pager显示的标题应该是有没有Fragment类来决定的,. 不应该放到主的Activity中, 可以看到qq每个页面对应title显示的内容, 和右上角的描述也是不一样的, 所以这些title应该交给每个fragment来实现,可以把这些fragment必须要有的功能定义一个接口, 每个fragment页面实现这些接口, 自然也会去实现接口中所定义的方法, 这样比较符合设计模式, 当我后期再需要添加一个Fragment页面的时候就不需要修改很多的类,只需要实现自己定义的Fragment接口, 并且实现这些抽象方法就可以了, 相反若是没有对应接口来规范Fragment的话, 没添加一个pager就要修改与之关联的类, 引起很多麻烦, 也容易忘记修改造成不必要的bug, 本人学的并不好, 有什么不对的地方欢迎指出改正,一起交流学习进步