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

在定义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

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

  1. android自定义壁纸制作,Android 自定义View实现画背景和前景(ViewGroup篇)

    在定义ListView的Selector时候,有个drawSelectorOnTop的属性,如果drawSelectorOnTop为true的话,Selector的效果是画在List Item的上面( ...

  2. Android自定义View之画圆环(进阶篇:圆形进度条)

    前言: 如果你想读懂或者更好的理解本篇文章关于自定义圆环或圆弧的内容.请你务必提前阅读下Android自定义View之画圆环(手把手教你如何一步步画圆环).在这篇文章中,详细描述了最基本的自定义圆环的 ...

  3. Android自定义View之画圆环(手把手教你如何一步步画圆环)

    关于自定义View: 好了,吐槽时间到.自定义view是Android开发知识体系中的重点,也是难点.好多小伙伴(也包括我)之前对自定义view也是似懂非懂.那种感觉老难受了.因此作为社会主义好青年, ...

  4. Android自定义view之ViewPager指示器——2

    Android自定义view之ViewPager指示器--2 上一篇<Android自定义view之ViewPager指示器--1>中我们一起写了测量和布局的流程.本篇我们继续讲解事件分发 ...

  5. android xml画圆,Android自定义View画圆功能

    本文实例为大家分享了Android自定义View画圆的具体代码,供大家参考,具体内容如下 引入布局 xmlns:tools="http://schemas.android.com/tools ...

  6. Android 自定义view 图片编辑(画圆,画长方形,手势缩放)

    Android 自定义view 图片编辑(画圆,画长方形,手势缩放) package com.bridgetek.yqm.view; import android.annotation.Suppres ...

  7. 手把手带你画一个漂亮蜂窝view Android自定义view

    上一篇做了一个水波纹view  不知道大家有没有动手试试呢点击打开链接 这个效果做起来好像没什么意义,如果不加监听回调 图片就能直接替代.写这篇博客的目的是锻炼一下思维能力,以更好的面多各种自定义vi ...

  8. 【朝花夕拾】Android自定义View之(一)手把手教你看懂View绘制流程——向源码要答案

    前言 原文:Android自定义View之(一)手把手教你看懂View绘制流程--向源码要答案 View作为整个app的颜值担当,在Android体系中占有重要的地位.深入理解Android View ...

  9. android view 渐变动画,Android自定义view渐变圆形动画

    本文实例为大家分享了Android自定义view渐变圆形动画的具体代码,供大家参考,具体内容如下 直接上效果图 自定义属性 attrs.xml文件 创建一个类 ProgressRing继承自 view ...

最新文章

  1. 作为大数据和云计算学习的一个序吧
  2. excel怎么删除浏览记录 方法介绍
  3. 常见的新算法的标准测试函数(Ackely,Rastrigin,Griewangk,SumSquartes,Sphere,Quartic,Schwefel' Problem等)
  4. C++ STL 教程
  5. 从零开始学前端:对象序列化与反序列化、冒泡排序、数组去重 --- 今天你学习了吗?(JS:Day11)
  6. MFC初探 —— 设置软件开机自启
  7. 2019年最新资料!共7T!
  8. PHP如何在照片下面写一行字_如何使用php分别插入照片和文字?
  9. ## 在webapp上使用input:file, 指定capture属性调用默认相机,摄像,录音功能
  10. 世界八大最顶尖的工业软件强国
  11. 高校舆情分析python_微博的高校舆情监控系统设计
  12. 新版虚拟服务器,新版tplink路由器虚拟服务器(端口映射)设置教程
  13. 八爪鱼 八爪鱼采集器四大采集模式
  14. 两个表格内容怎么合并
  15. Java 调用ffmpeg 实现视频编辑
  16. 根据IP地址获取地理位置
  17. matlab决策树模型过程,利用MATLAB统计工具箱进行决策树分类的一个例子
  18. 能用网络但是个个计算机不能连上,为什么无线网络连接上却不能上网,小编教你电脑连上无线网却不能上网怎么办...
  19. leetcode简单1742/867
  20. 如何用matlab生成一个单位正交矩阵?

热门文章

  1. VMware的屏幕太小
  2. C++ STL 容器之stack简单使用
  3. 抽象类(纯虚函数、虚函数)和虚基类(虚继承)
  4. POJ 3696 欧拉函数+快速幂
  5. 从零开始学C++之STL(八):函数对象、 函数对象与容器、函数对象与算法
  6. stm32机械臂资料含视频
  7. Java高级工程师必看系列,已拿到offer
  8. 现在做Android开发有前途吗?复习指南
  9. 什么原因成就了一位优秀的程序员?(转)
  10. 手机相机自动识别语音提示