1987WEB视界-分享互联网热点话题和事件

您现在的位置是:首页 > 网络工具 > 正文

网络工具

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

1987web2023-12-02网络工具62

作者:zibuyuqing链接:http://jianshu.com/p/af2dcf4f6522著作权归作者所有,本文经作者授权转载

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

相似度是不是很高O(∩_∩)O? 如果你喜欢或者想和我一起完成这个项目,请github一波(欢迎star):

https://github.com/zibuyuqing/UCBrowser

由于页面管理(堆叠视图)实现起来很复杂,我放到下一篇水文中。这篇文章将按照以下顺序讲故事:

1.UCRootView简介(照顾没看过第一篇的同学:轻点这里)2.自定义基本父布局(BaseLayout)3.网页导航栏(HeadLayout)视图更新4.贝塞尔背景(BezierLayout)实现5.底部菜单栏(Bottombar)视图更新6.其他view简析

下面开始表演

UCRootView简介

UCRootView是这些可滑动布局(除了堆叠视图)的Parent,里面重写了onInterceptTouchEvent和onTouchEvent方法,定义了通用滑动接口:

publicinterfaceScrollStateListener{voidonStartScroll();voidonScroll(floatrate);voidonEndScroll();voidonTouch(floatx,floaty);//手指位置}

滑动状态通过滑动距离与目标距离之间的比值rate来表示。在这个布局下,所有子布局的位置、大小,透明度等属性通过rate来控制。今天重点不是讲这个,如果想具体了解,请点这里。

自定义基本父布局(BaseLayout)

从上图可以看出,BaseLayout是UC浏览器主页面所有View组件的父类,里面定义了基本的移位(translate,目前只针对Y方向)、缩放(scale)、渐变(alpha)等方法,所有的view的属性变化都是基于Rootview传过来的rate计算得到,子view通过扩展父类实现不同场景的界面切换效果。

移位

控制开关:mTranslateEnable方向:Y初始化:

/**
     *
     *@paramfrom 起始位置
     *@paramto 最终位置
     */publicvoidinitTranslationY(intfrom,intto){
        mFromPosition = from;
        mToPosition = to;
        setTranslationY(from);
        mDistance = from - to;
    }

计算TransY:

/**
     * 
     *@paramrate 滑动的相对比率
     *@return*/privatefloatcalculateTransY(floatrate){
        Log.i(TAG,"rate :: =;"+ rate);returnmFromPosition + mDistance * rate;
    }

调用:在 onScroll(rate)方法中调用 setTranslationY(calculateTransY(rate));

缩放

控制开关:mScaleEnable方向:X,Y初始化:

publicvoidinitScale(floatstartScale,floatendScale){
        mStartScale = startScale;
        mEndScale = endScale;
        setScaleX(startScale);
        setScaleY(startScale);
        mScale = endScale - startScale;
    }

计算Scale:

privatefloatcalculateScale(floatrate){returnmStartScale + mScale * rate;
    }

调用:

privatevoidsetScaleXY(floatrate){
        setScaleX(calculateScale(rate));
        setScaleY(calculateScale(rate));
    }

渐变同上,很简单。这样就把最基本的父布局写完了O(∩_∩)O哈哈~,然后我们就要写各个部件的更新啦。

网页导航栏(HeadLayout)视图更新

这张图展示了UC浏览器HeadLayout布局结构,其中的BezierLayout包裹了天气栏、搜索栏、导航栏,其下方是网站列表,刷新进入UC头条提示默认不可见。现在打开手机UC浏览器,一起向上滑,预备,走!我们看到,当向上滑时,整个layout逐渐变小,并且小幅度向上移动,同时变黑,怎么实现这个效果呢?我们来分解一下:

变小 —— scale,上移 —— transY,变黑 —— foreground(黑色)改变Alpha渐显。

初始化

mUCHeadLayout.setTranslateEnable(true);// 可移动mUCHeadLayout.initTranslationY(0, -100);// 小幅移动

开始移动

@OverridepublicvoidonStartScroll(){// mUCCoverLayout 为下拉时所看到的提示上滑进入UC头条的布局mUCCoverLayout.setVisibility(VISIBLE);
        mUCCoverLayout.setAlpha(0.f);super.onStartScroll();
    }

更新视图

@OverridepublicvoidonScroll(floatrate){if(rate >0) {// 下拉// 显示提示语并下移mCoverTip.setTranslationY(100* Math.abs(rate));// 提示布局逐渐显现mUCCoverLayout.setAlpha(rate *1.5f);
        }else{// 上滑// 隐藏提示布局mUCCoverLayout.setVisibility(GONE);// foreground 逐渐显现,布局变黑mForeground.setAlpha((int) (ALPHA_255 * Math.abs(rate)));floatadjustRate =1.0f + rate *0.05f;// 布局内容逐渐变小mCategoryContain.setScaleX(adjustRate);
            mCategoryContain.setScaleY(adjustRate);
            mWebsiteContain.setScaleX(adjustRate);
            mWebsiteContain.setScaleY(adjustRate);
        }super.onScroll(rate);
    }

正如之前所说,整个过程受相对滑动比率rate控制,这样就实现了我们的HeadLayout界面更新,接下来我们看其下拉时背景效果实现。

贝塞尔背景(BezierLayout)实现

关于贝塞尔的相关知识感兴趣的可以百度一波哈。我想告诉你ScrollStateListener接口中的onTouch(float x, float y)方法就是为了这货添加的,因为我们在开始构建贝塞尔曲线的时候要有控制点,总不能写死吧。当用户向下滑动时,我们动态的更新布局大小(Y方向),并把我们的控制点始终放在底部,贝塞尔曲线的起始点和终止点高度(Y)更新慢于控制点,就可以达到效果了。

设置画笔

mPaint =newPaint();
        mPaint.setColor(mThemeColor);
        mPaint.setAntiAlias(true);// 抗锯齿mPaint.setStyle(Paint.Style.FILL);// 填充

初始化位置

mEdgeHeight = mHeight;// mEdgeHeight 为左右两个边的高度mControlPoint =newPoint(0,mHeight);// 初始位置为整个视图的底部,这样一开始画出来是条直线

绘制贝塞尔区域

@OverrideprotectedvoiddispatchDraw(Canvas canvas){
        drawBg(canvas);super.dispatchDraw(canvas);
    }privatevoiddrawBg(Canvas canvas){
        mPath.reset();// 顶部开始mPath.moveTo(0,0);
        mPath.lineTo(0,mEdgeHeight);// 贝塞尔曲线mPath.quadTo(mControlPoint.x,mControlPoint.y,mScreenWidth,mEdgeHeight);
        mPath.lineTo(mScreenWidth,0);// 闭合mPath.lineTo(0,0);
        canvas.drawPath(mPath,mPaint);
    }

这里用到的drawPath方法,大家可以详细了解一下Path的用法,很多炫酷效果是用这货弄的。

当手指下滑时,我们开始变弯了。

开始滑动并设置控制点

@OverridepublicvoidonStartScroll(){
        mStartScroll =true;super.onStartScroll();
    }publicvoidtouch(floatx,floaty){
        mControlPoint.set((int) x, mControlPoint.y);
        invalidate();
    }

更新视图

@Overridepublic void onScroll(float rate) {if(!mStartScroll){return;}// 获取 LayoutParams 根据滑动状态动态更新视图大小if(mLayoutParams == null){mLayoutParams = getLayoutParams();}if(rate >=0) {// 下拉// FINAL_DISTANCE 为最大能滑动的距离intdis = (int) (FINAL_DISTANCE * rate);// 左右边界更新速度是控制点的0.5倍mEdgeHeight = (int) (mHeight + dis *0.5f);//控制点更新mControlPoint.set(mControlPoint.x, mHeight + dis);// 视图内容改变大小,位置和透明度mContain.setScaleX(1.0f- rate *0.2f);mContain.setScaleY(1.0f- rate *0.2f);mContain.setTranslationY(dis *0.5f);mContain.setAlpha(1.0f- rate *1.5f);}else{// 上滑mControlPoint.set(0,mHeight);}// 改变视图大小mLayoutParams.height = mControlPoint.y;setLayoutParams(mLayoutParams);requestLayout();super.onScroll(rate);}

结束

@OverridepublicvoidonEndScroll(){
        mStartScroll =false;super.onEndScroll();
    }

上面注释的很详细,我们来看一下效果,不虚,不虚 O(∩_∩)O

底部菜单栏(Bottombar)视图更新

底部菜单栏视图更新原理和上面一样,只不过要根据menu位置计算更新速率,还有横向滑动。效果可以看前面的高清无码动图,这里我直接贴代码了。

计算中间Menu横向移动TransX

privatefloatcalculateMenuBtnTransX(floatrate){floatdis = mScreenWidth /5;return- dis * rate;
    }

计算新闻Menu竖向移动TransY

上滑时我们会看到新闻按钮(头条、视频、订阅)会根据不同速率依次滑动到指定位置,计算方法如下:

privatefloatcalculateNewsBtnTransY(intfinalY,floatrate,floatvelocity){// velocity 是调整速率floatadjustRate = rate * velocity;
        rate = adjustRate < -1.0f ? -1.0f : adjustRate;return0+ finalY * rate;
    }

计算Menu透明度

privatefloatcalculateBtnAlpha(floatrate){return1.0f + rate;
    }

更新视图

@Overridepublic void onScroll(float rate) {Log.(TAG,"onScroll :: rate =:"+ rate);if(rate >0){return;}//第1,2,4个按钮渐隐ivForward.setAlpha(calculateBtnAlpha(rate));ivBack.setAlpha(calculateBtnAlpha(rate));flWindowNum.setAlpha(calculateBtnAlpha(rate));// 第三个按钮移动到第四个位置ivMenu.setTranslationX(calculateMenuBtnTransX(rate));// 新闻按钮依次上升出现tvSubscribe.setTranslationY(calculateNewsBtnTransY(mHalfHeight,rate,1.0f));tvVideo.setTranslationY(calculateNewsBtnTransY(mHalfHeight,rate,1.5f));tvHeadline.setTranslationY(calculateNewsBtnTransY(mHalfHeight ,rate,2.0f));}

其他view简析

除了以上view组件,我们还要顶部搜索条(searchbar),新闻分类标签(newsTab)的更新没说呢,别急哈,很简单,用BaseLayout做这两个view组件的父布局,然后init一波就可以了,以searchbar为例

写布局

xml version="1.0"encoding="utf-8"?><com.zibuyuqing.ucbrowser.base.BaseLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:background="@color/themeBlue"android:padding="8dp"android:layout_height="@dimen/dimen_48dp"><ImageViewandroid:layout_gravity="center"android:src="@drawable/ic_searchbar_book"style="@style/SearchBoxImageIconStyle"/><TextViewandroid:gravity="center"android:textSize="13dp"android:textColor="@color/windowBg"android:text="UC头条"android:layout_width="wrap_content"android:layout_height="match_parent"/><LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:background="@drawable/search_box_bg"android:padding="6dp"android:layout_marginLeft="8dp"android:layout_height="32dp"><ImageViewandroid:layout_gravity="center_vertical"android:layout_width="14dp"android:layout_height="14dp"android:layout_alignParentStart="true"android:layout_marginLeft="8dp"android:src="@drawable/ic_search"/><TextViewandroid:textColor="@color/windowBg"android:gravity="center"android:layout_marginLeft="8dp"android:textSize="13dp"android:text="搜索你感兴趣的内容"android:layout_width="wrap_content"android:layout_height="match_parent"/>LinearLayout>com.zibuyuqing.ucbrowser.base.BaseLayout>

初始化

// 可移动mTopSearchBar.setTranslateEnable(true);// 这方法是在view layout 之后获取大小,避免获取的大小全是 0mTopSearchBar.getViewTreeObserver().addOnGlobalLayoutListener(newViewTreeObserver.OnGlobalLayoutListener() {@OverridepublicvoidonGlobalLayout(){mTopSearchBar.getViewTreeObserver().removeOnGlobalLayoutListener(this);// 初始化移动参数mTopSearchBar.initTranslationY(-mTopSearchBar.getHeight(),0);}});

大功告成,是不是很简单

结尾

文章写得很详细,能看到这个地方也算难为你了,给你点赞(也给我点个吧,顺手牵个赞O(∩_∩)O),如果你喜欢我的博客,请关注一下,我会不断将有意思的事情写出来,你也可以给我发一些需求,我来帮你实现或者给些建议,大家一起学习一起进步。项目地址:https://github.com/zibuyuqing/UCBrowser,欢迎踩踏。

往期干货

1

Android一步一步剖析+实现仿支付宝手势密码自定义View

2

理解Android硬件加速原理的小白文

3

Android项目总结(3)-登录页图片循环过渡播放动画效果

如果你觉得本文对你有帮助,请分享给更多的人

关注【Android技术杂货铺】,每天都有Android 干货文章分享!

长按识别二维码关注