个人认为UC浏览器的主界面交互逻辑还是挺好的,界面过度流畅,动画具有引导性,美观大方。我们现在尝试实现它,先来一张美图:


我按照从入门到跑路的过程分以下步骤给你们讲故事:
静态布局搭建 ——》自定义根布局 ——》各个界面过渡动画实现 ——》下拉操作(贝塞尔背景)实现 ——》viewpager + tablayout ——》感悟+后续工作

下面开始表演

静态布局搭建

(1)图片资源

先告诉你个坏消息,解压UCBrowser.apk是没用哒。唉,一开始就奠定了这是个悲剧。怎么办呢?百度咯。这里我主要用了两个图标源(没钱只能用免费的啦)。
第一个是阿里的图标库,网址是:http://iconfont.cn/collections。
第二个是github上的一个开源项目:https://github.com/google/material-design-icons。
如果你的项目不是太复杂,这些资源基本上可以满足需求。找一个看上去差不多的图标,然后用强大的图片编辑工具(美图秀秀)做一些小小的修改,就可以露脸了。

(2)布局层次

整个主界面被一个UCRootView包裹,它继承自RelativeLayout,里面实现自己的事件传递逻辑,并定义滑动接口。rootview下有四个大的子view组件,分别是Head,NewsPager,Searchbar和Bottombar,这些都继承自BaseLayout(自定义的viewgroup),到目前为止我们的UC浏览器布局结构如下(如果看的眼花,别打我哈):

![](http://upload-images.jianshu.io/upload_images/8719000-34ed1cb1a093e47f.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
(3)布局搭建

布局的搭建对各位同学来说应该是信手拈来吧,基本上就是玩各种layout,我就来张图吧,大家依葫芦画瓢

![](http://upload-images.jianshu.io/upload_images/8719000-975e730bed9f2d68.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 写到这,我们的基本布局组件就搭建好了。接下来我就应该探讨如何让这些界面动起来。

自定义根布局(UCRootView)

因为uc浏览器手势交互比较多,android原生的layout是满足不了我们的需求的,一个字,干!!!
当然这里最重要的还是android的事件分发机制,不熟悉的同学可以看看这篇文章:http://www.jianshu.com/p/e99b5e8bd67b
首先我们先确定对外的接口,因为很多界面牵扯到位置、大小、透明度等属性的变化,都有一个起始值和最终值,我们规定这个变化是0——>1的过程。

    public interface ScrollStateListener{void onStartScroll();void onScroll(float rate);void onEndScroll();void onTouch(float x,float y);//手指位置}

接口我们用一个List来管理,view可以实现接口,当需要监听时,我们的rootview把这些view(接口)加进来,不需要的时候移除掉就可以了。

    public void attachScrollStateListener(ScrollStateListener listener){mListeners.add(listener);}public void removeScrollStateListener(ScrollStateListener listener){mListeners.remove(listener);}

当我们需要通知各个View变化时,遍历我们的集合,依次调用即可

    private void onStartScroll(){for(ScrollStateListener listener : mListeners){listener.onStartScroll();}}private void onScroll(float rate){for(ScrollStateListener listener : mListeners){listener.onScroll(rate);}}

紧接着我们需要判断手指动作,以此来决定rootview是否要拦截此事件。

首先重写onInterceptTouchEvent:

 @Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) {if(getChildCount() < 0){Log.e(TAG,"There are no children to scroll");return super.onInterceptTouchEvent(ev);}final int action = ev.getAction();switch (action & MotionEvent.ACTION_MASK){case MotionEvent.ACTION_DOWN:mLastMotionY = ev.getY();mLastMotionX = ev.getX();case MotionEvent.ACTION_MOVE: {determineScrollingStart(ev);Log.i(TAG,"onInterceptTouchEvent :: ACTION_MOVE");break;}}return mTouchState != TOUCH_STATE_REST;}

determineScrollingStart()方法里主要是判断手指移动距离是否超过我们规定的值,如果超过,定性为滑动。逻辑如下:

    private boolean determineScrollingStart(MotionEvent ev, float touchSlopScale) {//touchSlopScale的值是1.0f。boolean scroll = false;final float y = ev.getY();// final float x = ev.getX();float deltaY = y - mLastMotionY;final int yDiff = (int) Math.abs(deltaY);final int touchSlop = Math.round(touchSlopScale * mTouchSlop);boolean yMoved = yDiff > touchSlop;Log.i(TAG,"determineScrollingStart :: touchSlop =:" + touchSlop+",xDiff =:" + yDiff);if (yMoved) {// 这里的mMode记录当前界面是处于网站导航展示(NORMAL_MODE)状态还是处于新闻列表状态(NEWS_MODE)if(mMode == NEWS_MODE){return false;}mTouchState = TOUCH_STATE_SCROLLING;onStartScroll();//通知view滑动开始scroll = true;}return scroll;}

因为目前只实现了竖向的滑动处理,所以只判断了y,后期再把x加上。
rootview是否拦截事件用mTouchState != TOUCH_STATE_REST判断,目前有两种状态:TOUCH_STATE_REST——正常状态,TOUCH_STATE_SCROLLING——滑动状态。后面如果把横向加进来可能要做区分了。

然后重写onTouchEvent

 @Overridepublic boolean onTouchEvent(MotionEvent ev) {if (getChildCount() <= 0) return super.onTouchEvent(ev);acquireVelocityTrackerAndAddMovement(ev);final int action = ev.getAction();float y = ev.getY();float x = ev.getX();onTouch(x,y);//更新手指位置switch (action & MotionEvent.ACTION_MASK) {case MotionEvent.ACTION_DOWN:{Log.i(TAG,"onTouchEvent :: ACTION_DOWN");break;}case MotionEvent.ACTION_MOVE:{if (mTouchState == TOUCH_STATE_SCROLLING) {float deltaY = y - mLastMotionY;float deltaX = x - mLastMotionX;mTotalMotionY += deltaY;//记录总滑动距离if(Math.abs(deltaY) >= 1.0f) {float rate = mTotalMotionY / mFinalDistance;//计算滑动进度,其中mFinalDistance为起始与最终位置的距离。onScroll(rate);//通知view更新}} else {determineScrollingStart(ev);}Log.i(TAG,"onTouchEvent :: ACTION_MOVE mTouchState =:" +mTouchState);mLastMotionY = y;mLastMotionX = x;//attachToFinal()方法判断是否到达目的地return attachToFinal();}case MotionEvent.ACTION_CANCEL:case MotionEvent.ACTION_UP:checkPoint();//手指离开屏幕后检测是否到达目的地Log.i(TAG,"onTouchEvent :: ACTION_UP");break;}return true;}private boolean attachToFinal(){if(mMode == NEWS_MODE){return mTotalMotionY >= 0;}return -mTotalMotionY >= mFinalDistance;}

当我们手指离开屏幕之后还没到达指定位置怎么办,这里我采用handle通知view继续更新:

    private void init(){final ViewConfiguration configuration = ViewConfiguration.get(mContext);mTouchSlop = configuration.getScaledTouchSlop();mHandler = new Handler(Looper.getMainLooper()){@Overridepublic void handleMessage(Message msg) {if(msg.what == MSG_FLING){//FLING_SPEED = 50int speed = mMode == NORMAL_MODE ? -FLING_SPEED : FLING_SPEED;mTotalMotionY += speed;onScroll(mTotalMotionY / mFinalDistance);//继续更新viewcheckPoint();}super.handleMessage(msg);}};}///...///private void checkPoint() {if(mTouchState == TOUCH_STATE_REST){return;}if(!attachToFinal()){mHandler.sendEmptyMessage(MSG_FLING);} else {mHandler.removeMessages(MSG_FLING);if(mMode == NORMAL_MODE) {mTotalMotionY = -mFinalDistance;onScroll(-1.0f);mMode = NEWS_MODE;} else {mTotalMotionY = 0;onScroll(0.0f);mMode = NORMAL_MODE;}onEndScroll();resetTouchState();//重置触摸状态。}}

写到这,我们的事件处理逻辑算是差不多了,对了UC浏览器点击主页按钮要回到网站导航状态,怎么实现呢,很简单

    public void back2Normal(){mTouchState = TOUCH_STATE_SCROLLING;checkPoint();}

大功告成,以后就用这个布局生孩子了。我们来看一下效果:



今天先写到这,接下来一篇我们将接着水以下效果的实现:


注:这个项目是我在工作之余写着玩的,代码有空优化,欢迎打我。
项目地址:https://github.com/zibuyuqing/UCBrowser

敌人还有五秒到达战场….

转载请注明:http://blog.csdn.net/zibuyuqing/article/details/79094503

尝试写个UC浏览器(布局篇)相关推荐

  1. 尝试写个UC浏览器(主页交互篇)

    这是个看脸的时代,如果你没有个Beautiful Face(B脸),都不好意思写博客.继上一次我通宵加班(钥匙锁家里了,门也砸了)给大家介绍了UC浏览器基本布局的实现(轻点这里),帅气的我又来水文了. ...

  2. 尝试写个UC浏览器(堆叠视图A)

    背景:快过年了,问题那个多呀,最近手都敲出老茧了,上班打个卡都要识别几分钟,不知道身为程序猿的你是不是有同样的感受.唉,不说了,老子名下还有200多个bug... 既然前面已经吹了两次逼( 布局篇 和 ...

  3. 前端面试题之浏览器原理篇

    前端面试题之浏览器原理篇 一.浏览器安全 1. 什么是 XSS 攻击? (1)概念 (2)攻击类型 2. 如何防御 XSS 攻击? 3. 什么是 CSRF 攻击? (1)概念 (2)攻击类型 4. 如 ...

  4. 自定义Behavior的艺术探索-仿UC浏览器主页

    出处:http://www.jianshu.com/p/f7989a2a3ec2 前言&效果预览 最近几个周末基本在研究CoordinatorLayout控件和自定义Behavior当中,这期 ...

  5. Behavior实现UC浏览器首页动画效果

    老规矩,还是先上效果图 github地址 前面我也写过一篇关于UC浏览器首页滑动动画效果的文章UC浏览器首页滑动动画实现,只不过这篇文章是通过自定义View的方式实现这个滑动效果.最近在看Behavi ...

  6. HTML布局篇之双飞翼(圣杯)布局

    最近在写页面的时候,总是为布局头疼,倒不是不能布出来,就是感觉不系统,没有成一个体系的感觉.所以决定自己写博文,梳理一下思路. 常用的布局方式大致可以分为三种: 浮动布局 Float 负边距(双飞翼) ...

  7. 一步一步构建手机WebApp开发——页面布局篇

    继上一篇:一步一步构建手机WebApp开发--环境搭建篇过后,我相信很多朋友都想看看实战案例,这一次的教程是页面布局篇,先上图: 如上图所示,此篇教程便是教初学者如何快速布局这样的页面.废话少说,直接 ...

  8. web前端布局篇(切图)

    web前端布局篇(切图) 这里的切图不是指ps里面的切图,而是指web前端工程师将设计图转化为静态页面的过程. 在布局的时候,我们通常会以设计图为蓝本,然后将设计图拆分,变成一个一个的HTML标签,搭 ...

  9. 当我尝试写一个自动写小说的AI,长路漫漫的踩坑之路 ToT

    起因 事情是这样的,前几天我在刷B站的时候看到一个大佬用训练了一个自动写高考作文的AI 链接: https://www.bilibili.com/video/BV1pr4y1w7uM 那我就想既然别人 ...

最新文章

  1. 中国唯一一座没有高楼大厦的新一线城市,也太佛了吧
  2. Metasploit(一)--Meterpreter的命令速查表
  3. Promise 简介
  4. gcn语义分割_ICCV Oral 2019:152层GCN大幅加深图卷积网络的方法,点云分割任务效果显著...
  5. STM32 电机教程 28 - ST MCLIB实战之 位置闭环控制
  6. [SDOI2017]数字表格
  7. CodeForces - 1288E Messenger Simulator(树状数组)
  8. java swing 串口_ComTest 接收串口数据,并显示在文本框内,通过JavaSwing实现 Develop 265万源代码下载- www.pudn.com...
  9. 解读|数据分析的发展和演变经过哪几个阶段
  10. (转)Spring4.2.5+Hibernate4.3.11+Struts2.3.24整合开发
  11. js正则表达式匹配字符串与优化过程
  12. c++ 实现outlook itemsend_2021智能C端冬季科创训练营作业已发布,请注意查收~
  13. 【CF870F】Paths 分类讨论+数学
  14. 寄存器PLC地址与寄存器modbus协议地址
  15. 监控系统中的几种服务器,监控系统各种服务器
  16. 关于地图矢量下载器的使用感受
  17. 从此甩掉光驱 U盘安装系统最详攻略(转自腾讯数码)
  18. 装系统时的UEFI模式
  19. 升级Android8.0系统原来APP图标变为小机器人的问题
  20. ARM嵌入式的定义和开发工具介绍

热门文章

  1. 香港银行牌照申请日记-1
  2. php随机生成密码函数
  3. 工业界推荐_Industrial RS(5)
  4. 计算机科学与技术学士论文,计算机科学与技术毕业论文
  5. 更改Windows 7中WMP12默认的视频解码器
  6. python turtle 画动漫人物_python之turtle使用:画一颗美美哒的树
  7. spring.factories详解
  8. 精准营销 工业品网络营销系列讲座第一课 张进老师主讲
  9. 服装生产erp都有哪些功能?该如何选服装生产erp?
  10. C++ 50 家企业校招面经