android--每日一问:如何实现自定义view?(代码片段)

Kevin-Dev Kevin-Dev     2022-12-26     754

关键词:

经典回答

回忆一下,你去面试时常被问到的自定义 View 方面的问题是那些。有没有:

  • invalidate 和 postInvalidate 方法的区别?
  • 自定义 View 的绘制流程?
  • View 的 Touch 事件分发流程?

因为在实际的工作中并不是每个人都会涉及UI的实现,所以有些人没有做过自定义 View 并不能否决这个人在 Android 开发上的能力,包括会问你这方面问题的面试官也可能并没有自定义 View 的经验。所以很多面试中,一般也就是问问如上面的那些机制方面的问题,看面试者是否有一个正确的认识。但如果一个人说他精通自定义View的话,不妨从细节上检验一下他。

在说我怎么检验面试者的自定义View水平之前,先来一道老外的面试题,大家不妨先自己试着敲一下代码看能不能实现。

效果界面如下:

这一道题其实把我们刚刚提到的面试常被问到的那些机制问题都涉及到了,你很容易就能查找和了解并可以很好的回答上那几个基础问题,但是你能做出这个自定义View吗?

这就是机制和现实的差距!你有必要了解机制(基础),但了解并不等于会了,会的过程需要一定的积累,只有融会贯通了才能轻而易举地完成这个老外的面试题。

我们简单剖析一下,这个可左右滑动的View中是一组子View组成的,每个子View(圆盘carousel)可以自定界面,但背景的样式是相似(每组背景只是颜色不一样),有点ListView中的Adapter味道,对滑动的过渡是有要求的,这个其实是更真实操作Touch的样例(没有人想看生硬的滑动效果)。那么我们要能实现上面的效果,需要注意以下几个点:

  • ViewGroup的布局(计算一行能显示的圆盘数量和大小)
    圆盘View的效果(背景和半透明过渡)
    左右列表和BaseAdapter
    圆盘View的滑动(需要自动回弹,保证滑动后要有一个圆盘处于中间位置)

  • View & ViewGroup
    自定义View(有时我们也可以叫自定义UI或者自定义UI组件),从实现或者分类上我觉得可以分为三类:

  • 直接继承View
    继承自ViewGroup
    对现有组件的扩展(如继承自TextView)
    第3种方式是比较容易的,因为父组件常常帮我处理了绘制、分发和Touch等事件,我们只需要加入一些特别的功能就行。比如继承自ImageView实现图片圆角显示。

第1和第2种方式,需要我们对前面提到的那些机制问题有一定的了解,也要搞清楚View和ViewGroup在哪些方面有什么区别。

ViewGroup继承自View,是一种特殊的View,可以理解成一种View的容器,它可以装其他的View或其他的ViewGroup。
ViewGroup需要控制子View如何布局,所以必须实现onLayout(在ViewGroup中是抽像方法)。
ViewGroup可以通过onInterceptTouchEvent拦截当前事件,再决定是否分发给子View处理。
ViewGroup默认是不会调用onDraw方法的,如果需要重绘容器的背景需要在构造函数调用setWillNotDraw(false)。

除此之外,要熟悉一些概念(或者类):Canvas, Paint, Matrix,Path & PathMeasure,贝塞尔曲线,SufaceView & OpenGL等等。是不是觉得概念太多了,是的,没有实际做过,你一定会这样想。让你为了面试去准备这么多东西,貌似也起不到多大的作用,面试官不一定会问(除非指明了招专门做自定义UI的开发)。而且就算面试官问了一个你准备好的主题,但他换一种说法问你,如果你只是准备过但并没有掌握的话,也不一定能答得上来。就拿PathMaesure来说,做为面试官一般不会直接问你:“这个PathMeasure有什么方法或者主要是做什么用的啊?”

而往往会用实例来看你的实现思路,例:“如何实现下图的这个箭头图标(png图片)围绕园周运动的自定义View”。如果你的做法不是使用PathMeasure,那么我也会很有兴趣想听听你的思路和实现方法。

灵活使用PathMeasure的常见的两个方法可以轻松的实现很多神奇的效果。
getSegment:截取整个Path的片段;
getPosTan:获取路径上的坐标点和对应点的切线坐标。

使用PathMeasure实现的核心代码:

    path = new Path();
    path.addCircle(0, 0, 200, Path.Direction.CW); //添加一个圆的path
    pathMeasure = new PathMeasure(path, false);

    float[] pos = new float[2];
    float[] tan = new float[2];
    pathMeasure.getPosTan(pathMeasure.getLength() * value, pos, tan);

    float degress = (float) (Math.atan2(tan[1], tan[0]) * 180.0 / Math.PI); // 计算箭头图片的旋转角度

如果面试者不了解PathMeasure的话,也可以问一下图片圆角(或称矩形圆角)或者圆形头像的实现方式,这个也是一个很常见的功能,效果如下图:

把原图直接做成圆角外,常见有三种方式实现:

  • 使用 Xfermode 混合图层;
  • 使用 BitmapShader;
  • 通过裁剪画布区域实现指定形状的图形(ClipPath)

你的朋友是不是也在准备面试呢?你可以把今天的题目分享给好友,或许你可以帮到他。

android--每日一问:自定义view的状态是如何保存的?(代码片段)

典型回答Android有一套标准的做法,做过自定义View的人很容易遇到这个问题,因为Activity转屏,或Home键到后台很容易在被系统销毁,恢复时我们肯定是希望看到View保持之前状态。提示:系统内存紧张时会主动... 查看详情

每日一问:简述view的绘制流程(代码片段)

Android开发中经常需要用一些自定义View去满足产品和设计的脑洞,所以View的绘制流程至关重要。网上目前有非常多这方面的资料,但最好的方式还是直接跟着源码进行解读,每日一问系列一直追求短平快,所以本文笔者尽量精简... 查看详情

android--每日一问:自定义一个类让其实现parcelable,大致流程是什么?(代码片段)

典型回答1).复写writeToParcel将对象数据序列化成一个Parcel对象(序列化之后成为Parcel对象.以便Parcel容器取出数据,其中flags标识有两种值:0或1。为1时标识当前对象需要作为返回值返回,不能立刻释放资源,即P... 查看详情

每日灵魂一问-如何实现文件上传?

前端请求头为content-type:multipart/form-datakoa-body实现文件上传constkoaBody=require('koa-body');app.use(koaBody({multipart:true,formidable:{maxFileSize:200*1024*1024//设置上传文件大小最大限制,默认2M},formLimit:'5mb',textL 查看详情

android--每日一问:如何设计一个照片上传app?

经典回答把自己放在一个面试官的角度,自己先实现一次这个App,然后自己总结一下你在这次实现中需要哪些能力、需要注意哪些事项。最后,再回过头来看,如果你是面试官,你希望面试者怎么回答才算是... 查看详情

android--每日一问:什么是binder?它是如何实现跨进程通信的?

经典回答Binder模糊了进程边界,淡化了进程间通信的过程,整个系统仿佛运行于同一个面向对象的程序之中。Linux进程基础为了保护进程空间不被别的进程破坏或者干扰,Linux的进程是相互独立的(进程隔离)&... 查看详情

android--每日一问:如何处理线程同步的问题?

知识点有可能很多人对插件并不了解,不过没关系,这个需求简单地说就是主线程要等待多个子线程全部完成工作后,才能继续执行。说到多线程的同步问题,面试多的人应该很容易被面试官问:Object的wait和n... 查看详情

android--每日一问:如何理解android中的context,它有什么用?

经典回答官方文档对于Context的解释:Interfacetoglobalinformationaboutanapplicationenvironment.ThisisanabstractclasswhoseimplementationisprovidedbytheAndroidsystem.Itallowsaccesstoapplication-specificresources 查看详情

android如何实现画板功能?(代码片段)

前言Android实现画板主要有2种方式,一种是用自定义View实现,另一种是通过Canvas类实现。当然自定义View内部也是用的Canvas。自定义View创建一个自定义View(推荐SurfaceView),在自定义View里通过Path对象记录手指... 查看详情

[github开源]android自定义view实现微信打飞机游戏(代码片段)

...写了很多自定义View理论方面的文章,具体可以参见《Android中自定义View、ViewGroup理论基础详解》。理论指导实践,本博文演示了如何通过自定义View实现微信打飞机游戏。全部源码已经开源到GitHub,如果觉得不错,... 查看详情

Android:如何在 Widget [Remote Views] 中使用自定义视图

】Android:如何在Widget[RemoteViews]中使用自定义视图【英文标题】:Android:HowtousecustomviewinWidget[RemoteViews]【发布时间】:2015-11-1623:20:06【问题描述】:你好所有的安卓开发者!我想在Widget中使用Arc进度视图来实现动作进度循环计时... 查看详情

android自定义view

...果,我们先来就来看看如何实现吧。分析确定宽高对一个Android自定义控件来说,一般都经过三个步骤onLayout()onMeasure()onDraw()onLayout明确子控件在父控件中的位置(本控件不需要重写),onMeasure是确定控件 查看详情

每日一问:layoutparams你知道多少?(代码片段)

...easureSpec和View自身的`LayoutParams共同决定的。我们在前面的每日一问:谈谈对MeasureSpec的理解把MeasureSpec的重点进行了讲解,其实另外一个LayoutParams同样是非常非常重要。从概念讲起LayoutParams,顾名思义,就是布局参数。而且大多 查看详情

android--每日一问:两个fragment之间如何进行通信?

...08;回调函数),可以组织UI和业务逻辑。但它不像Android的四大组件(Activity,Service,BroadcastReceiver和ContentProvider)那样可以独立存 查看详情

android--每日一问:如何理解android应用的进程?(代码片段)

知识点进程是一个动态的过程,每一个App的运行都是在一个独立的进程中,进程有自己独立的内存和数据空间,进程的名字就是App的packageName,这些进程都是从Zygote进程Fork出来的,并受AMS(ActivityManagerServic... 查看详情

android--每日一问:如何检测内存泄露,如何进行内存优化?(代码片段)

经典回答Android系统为每一个应用程序都设置了一个硬性的DalvikHeapSize最大限制阈值,这个阈值在不同的设备上会因为RAM大小不同而各有差异。如果你的应用占用内存空间已经接近这个阈值,此时再尝试分配内存的话,... 查看详情

自定义view

...知识,比如说自定义view的步骤、会涉及到哪些函数以及如何实现自定义属性,同时实现了一个很基础的自定义控件,一个自定义的计时器,需要看的人可以点击这个链接:http://www.cnblogs.com/YaoJianXun/p/5806926.html。  这次讲的是... 查看详情

android自定义view实现文本轮播效果

...效果。自定义View代码如下:packagead.scrolltextview;importandroid. 查看详情