Android 自定义View实现画背景和前景(ViewGroup篇)

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

        在定义ListView的Selector时候,有个drawSelectorOnTop的属性,如果drawSelectorOnTop为true的话,Selector的效果是画在List Item的上面(Selector是盖住了ListView的文字或者图片),即Foreground前景。如果drawSelectorOnTop为false的话,Selector的效果是画在List Item的下面,即Background背景。由于项目中恰好需要自定义View,需要实现此效果。

       本文借ListView的代码来剖析一下,

       ListView完成此部分功能在frameworks\base\core\java\android\widget\AbsListView.java文件中。

用mSelector即ListView要画的Selector(资源文件),而mSelectorRect则是想要画的区域。

 /*** Indicates whether the list selector should be drawn on top of the children or behind*/boolean mDrawSelectorOnTop = false; 决定画前景还是背景/*** The drawable used to draw the selector*/Drawable mSelector; ListView用中来显示Selector的Drawable,即ListSelector对应的XML文件/*** The current position of the selector in the list.*/int mSelectorPosition = INVALID_POSITION;/*** Defines the selector's location and dimension at drawing time*/Rect mSelectorRect = new Rect(); 用来画Selector的区域,即Selector画的位置

AbsListView中构造方法中有获取selector

Drawable d = a.getDrawable(com.android.internal.R.styleable.AbsListView_listSelector);if (d != null) {setSelector(d);}//默认为false,画的是背景mDrawSelectorOnTop = a.getBoolean(com.android.internal.R.styleable.AbsListView_drawSelectorOnTop, false);
下面看一下setSelector是如何实现的
/*** Controls whether the selection highlight drawable should be drawn on top of the item or* behind it.** @param onTop If true, the selector will be drawn on the item it is highlighting. The default*        is false.** @attr ref android.R.styleable#AbsListView_drawSelectorOnTop*/public void setDrawSelectorOnTop(boolean onTop) { //提供是否画前景或者背景的接口mDrawSelectorOnTop = onTop;}/*** Set a Drawable that should be used to highlight the currently selected item.** @param resID A Drawable resource to use as the selection highlight.** @attr ref android.R.styleable#AbsListView_listSelector*/public void setSelector(int resID) {setSelector(getResources().getDrawable(resID)); 设置listSelector的XML文件}public void setSelector(Drawable sel) {if (mSelector != null) {mSelector.setCallback(null);unscheduleDrawable(mSelector);}mSelector = sel;Rect padding = new Rect();sel.getPadding(padding);mSelectionLeftPadding = padding.left;mSelectionTopPadding = padding.top;mSelectionRightPadding = padding.right;mSelectionBottomPadding = padding.bottom;sel.setCallback(this); //需要给Selector设置CallbackupdateSelectorState(); }/*** Returns the selector {@link android.graphics.drawable.Drawable} that is used to draw the* selection in the list.** @return the drawable used to display the selector*/public Drawable getSelector() {return mSelector;}void updateSelectorState() {if (mSelector != null) {if (shouldShowSelector()) {mSelector.setState(getDrawableState());//更新Selector的状态} else {mSelector.setState(StateSet.NOTHING);}}}

这样就将Selector设置给ListView了,并且更新了drawable的状态。

接下来我们再看一下Android是如何将drawable画到ListView的Item上的。

在AbsListView中有个onTouchEvent的方法用来处理Touch事件,其中有一段代码就是确定Selector要画的区域。

if (mTouchMode == TOUCH_MODE_DOWN || mTouchMode == TOUCH_MODE_TAP) {final Handler handler = getHandler();if (handler != null) {handler.removeCallbacks(mTouchMode == TOUCH_MODE_DOWN ?mPendingCheckForTap : mPendingCheckForLongPress);}mLayoutMode = LAYOUT_NORMAL;if (!mDataChanged && mAdapter.isEnabled(motionPosition)) {mTouchMode = TOUCH_MODE_TAP;setSelectedPositionInt(mMotionPosition);layoutChildren();child.setPressed(true);//设置List Item状态为 pressedpositionSelector(mMotionPosition, child);//确定画Selector的区域setPressed(true); //设置ListView 的状态为pressedif (mSelector != null) {Drawable d = mSelector.getCurrent();if (d != null && d instanceof TransitionDrawable) {((TransitionDrawable) d).resetTransition();}}if (mTouchModeReset != null) {removeCallbacks(mTouchModeReset);}mTouchModeReset = new Runnable() {@Overridepublic void run() {mTouchMode = TOUCH_MODE_REST;child.setPressed(false);setPressed(false);if (!mDataChanged) {performClick.run();}}};postDelayed(mTouchModeReset,ViewConfiguration.getPressedStateDuration());} else {mTouchMode = TOUCH_MODE_REST;updateSelectorState();}return true;} else if (!mDataChanged && mAdapter.isEnabled(motionPosition)) {performClick.run();}}

接下来看看positionSelector的实现,


void positionSelector(int position, View sel) {if (position != INVALID_POSITION) {mSelectorPosition = position;}//设置Selector的区域为List Item View的边界final Rect selectorRect = mSelectorRect;   selectorRect.set(sel.getLeft(), sel.getTop(), sel.getRight(), sel.getBottom());if (sel instanceof SelectionBoundsAdjuster) {((SelectionBoundsAdjuster)sel).adjustListItemSelectionBounds(selectorRect);}positionSelector(selectorRect.left, selectorRect.top, selectorRect.right,selectorRect.bottom);final boolean isChildViewEnabled = mIsChildViewEnabled;if (sel.isEnabled() != isChildViewEnabled) {mIsChildViewEnabled = !isChildViewEnabled;if (getSelectedItemPosition() != INVALID_POSITION) {refreshDrawableState();//根据View状态更新drawable的状态}}}private void positionSelector(int l, int t, int r, int b) {mSelectorRect.set(l - mSelectionLeftPadding, t - mSelectionTopPadding, r+ mSelectionRightPadding, b + mSelectionBottomPadding);}

好了现在已经决定了将selector画在哪里,Selector的状态也已经更新OK。

还差一步没有做,那就是到底是将其怎么画上面的呢?

答案就在AbsListView.java里的dispatchDraw方法里面。

@Overrideprotected void dispatchDraw(Canvas canvas) {int saveCount = 0;final boolean clipToPadding = (mGroupFlags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK;if (clipToPadding) {saveCount = canvas.save();final int scrollX = mScrollX;final int scrollY = mScrollY;canvas.clipRect(scrollX + mPaddingLeft, scrollY + mPaddingTop,scrollX + mRight - mLeft - mPaddingRight,scrollY + mBottom - mTop - mPaddingBottom);mGroupFlags &= ~CLIP_TO_PADDING_MASK;}final boolean drawSelectorOnTop = mDrawSelectorOnTop;if (!drawSelectorOnTop) { //将Selector画为背景drawSelector(canvas);}super.dispatchDraw(canvas);// 用Canvas画ListViewif (drawSelectorOnTop) { //将Selector画为前景drawSelector(canvas);}if (clipToPadding) {canvas.restoreToCount(saveCount);mGroupFlags |= CLIP_TO_PADDING_MASK;}}private void drawSelector(Canvas canvas) {if (!mSelectorRect.isEmpty()) {final Drawable selector = mSelector;selector.setBounds(mSelectorRect);//设置drawable画的区域selector.draw(canvas); //使用canvas将drawable画上去}}

看到这里,想必大家都已经明白如何画前景和背景了吧。在dispatchDraw之前调用就是画前景,在dispatchDraw之后调用就是画背景。

另外补充一下,本文并没有介绍动画部分,有兴趣的可以自己研究下。

总结一下,实现这个功能需要有三个步骤:

1.设置Selector,并更新状态(初始化时候)

2.确定Selector画的区域,设置View的状态,根据View状态,更新Selector的状态(一般是对Event的处理方法中)

3.使用Canvas在dispatchDraw中,将Selector画上去,画Drawable的时候需要先设置区域,再调用drawable的draw方法。

后面我再将View如何画背景和前景补上,今天就先到这里吧。







      


转载于:https://my.oschina.net/shaorongjie/blog/202291

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/401943.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux的mmap内存映射机制解析

在讲述文件映射的概念时,不可避免的要牵涉到虚存(SVR 4的VM).实际上,文件映射是虚存的中心概念, 文件映射一方面给用户提供了一组措施,好似用户将文件映射到自己地址空间的某个部分,使用简单的内存访问指令读写文件;另一方面,它也可以用于内核的基本组织模式,在这种…

svn add Default@2x.png的文件含有@的文件名注意事项

为什么80%的码农都做不了架构师?>>> iOS的Icon里面,包含符号 ,svn add Icon2x.png,没法加进去。 解决的办法是,在文件名最后加一个,例如 svn add Icon2x.png 或者svn add Icon\2x.png\ 转载于…

Linux 设备驱动的固件加载

作为一个驱动作者, 你可能发现你面对一个设备必须在它能支持工作前下载固件到它里面. 硬件市场的许多地方的竞争是如此得强烈, 以至于甚至一点用作设备控制固件的 EEPROM 的成本制造商都不愿意花费. 因此固件发布在随硬件一起的一张 CD 上, 并且操作系统负责传送固件到设备自身…

Linux USB 驱动开发(二)—— USB 驱动几个重要数据结构

前面我们学习了USB 驱动的一个描述符,下面来学习 USB 驱动的几个重要数据结构 一、struct usb_interface 接口函数 [cpp] view plaincopy struct usb_interface { struct usb_host_interface *altsetting; struct usb_host…

Linux USB 驱动开发(三)—— 编写USB 驱动程序

前面学习了USB驱动的一些基础概念与重要的数据结构,那么究竟如何编写一个USB 驱动程序呢?编写与一个USB设备驱动程序的方法和其他总线驱动方式类似,驱动程序把驱动程序对象注册到USB子系统中,稍后再使用制造商和设备标识来判断是否…

Linux USB 驱动开发(一)—— USB设备基础概念

在终端用户看来,USB设备为主机提供了多种多样的附加功能,如文件传输,声音播放等,但对USB主机来说,它与所有USB设备的接口都是一致的。一个USB设备由3个功能模块组成:USB总线接口、USB逻辑设备和功能单元&am…

Linux USB 驱动开发(四)—— 热插拔那点事

学习USB热插拔之前,先学习一些USB的硬件知识: 一、USB基础概念 1、硬件知识(USB插座和插头) 在最初的标准里,USB接头有4条线:电源,D-,D,地线。我们暂且把这样的叫做标准的USB接头吧。后来OTG出现…

Linux USB 驱动开发(五)—— USB驱动程序开发过程简单总结

设备驱动程序是操作系统内核和机器硬件之间的接口,由一组函数和一些私有数据组成,是应用程序和硬件设备之间的桥梁。在应用程序看来,硬件设备只是一个设备文件,应用程序可以像操作普通文件一样对硬件设备进行操作。 设备驱动程序是…

android软键盘上推ui解决

为什么80%的码农都做不了架构师?>>> http://bbs.csdn.net/topics/340198955 android软键盘上推ui解决 good job 转载于:https://my.oschina.net/macleo/blog/204882

Linux USB 驱动开发实例(一) —— USB摄像头驱动实现源码分析

Spac5xx的实现是按照标准的USB VIDEO设备的驱动框架编写(其具体的驱动框架可参照/usr/src/linux/drivers/usb/usbvideo.c文件),整个源程序由四个主体部分组成: 设备模块的初始化模块和卸载模块,上层软件接口模块&#…

System Center 2012R2之SCVMM云部署SCOM(2-2)

SCVMM云部署SCOM安装过程1、在SCVMM中,使用WINDOWS SERVER 2012母盘创建云主机SCOM在SCVMM中先创建到一个私有云指定一个私有云名称选择资源主机指定逻辑网络默认选负载衡器跳过VIP模板跳过选择端口,下一步选择存储分类指定存储的VM路径各只读共享默认设…

Linux USB 驱动开发实例(二)—— USB 鼠标驱动注解及测试

参考2.6.14版本中的driver/usb/input/usbmouse.c。鼠标驱动可分为几个部分:驱动加载部分、probe部分、open部分、urb回调函数处理部分。 一、驱动加载部分 [cpp] view plaincopy static int __init usb_mouse_init(void) { int retval usb_register(&a…

MySQL5.6 更改字段属性仍旧会锁全表,注意这个坑!

如图:如果开发让修改表字段属性,建议用pt-online-schema-change。MySQL5.6的在线DDL会锁全表。注意这个坑。另外,增加、删除字段或索引不会锁全表,删除主键会锁全表。

Linux USB 驱动开发实例 (三)—— 基于USB总线的无线网卡浅析

回顾一下USB的相关知识 USB(Universal Serial Bus)总线又叫通用串行外部总线,它是20世纪90年代发展起来的。USB接口现在得到了广泛的应用和普及,现在的PC机中都带有大量的USB接口。它最大的特点就是方便通用、支持热插拔并且可以在一个接口上插上多个设备…

Linux 设备驱动开发思想 —— 驱动分层与驱动分离

前面我们学习I2C、USB、SD驱动时,有没有发现一个共性,就是在驱动开发时,每个驱动都分层三部分,由上到下分别是: 1、XXX 设备驱动 2、XXX 核心层 3、XXX 主机控制器驱动 而需要我们编写的主要是设备驱动部分&#xff0c…

CortexM0开发 —— UART时序分析

通用异步收发传输器(UniversalAsynchronousReceiver/Transmitter),通常称作UART,是一种异步收发传输器。将数据由串行通信与并行通信间作传输转换,作为并行输入成为串行输出的芯片UART是一种通用串行数据总线,用于异步通信。该总线…

CortexM0开发 —— LPC11C14的UART使用方法

LPC1100系列微控制器UART LPC1100系列Cortex-M0微控制器具有一个符合16C550工业标准的异步串行口(UART)。此口同时增加了调制解调器(Modem)接口,DSR、DCD和RI Modem信号是只用于LQFP48和PLCC44封装的管脚配置。 特性…

Linux SD卡驱动开发(一) —— SD 相关基础概念

一.SD/MMC卡基础概念 1.1.什么是MMC卡 MMC:MMC就是MultiMediaCard的缩写,即多媒体卡。它是一种非易失性存储器件,体积小巧(24mm*32mm*1.4mm),容量大,耗电量低,传输速度快,广泛应用于消费类电子产品中。 1.2.什么是SD卡…

Linux SD卡驱动开发(二) —— SD 卡驱动分析HOST篇

回顾一下前面的知识,MMC 子系统范围三个部分: HOST 部分是针对不同主机的驱动程序,这一部是驱动程序工程师需要根据自己的特点平台来完成的。 CORE 部分: 这是整个MMC 的核心存,这部分完成了不同协议和规范的实现,并为…

MVC应用程序显示RealPlayer(rm)视频

本篇博文是演示MVC应用程序显示RealPlayer视频。 客户端能观看到RealPlayer视频,前提条件是需要安装RealPlayer客户端,就是想看Falsh或理WMV视频一样,均要安装客户端或相关插件等。 Insus.NET实现方法,还是在控制器中Render RealP…