Android好奇宝宝_番外篇_看脸的世界_05
来源:互联网 发布:ubuntu 一直等待安装 编辑:程序博客网 时间:2024/05/06 14:24
上一篇番外篇讲了一个炒鸡炒鸡简单的自定义ProgressBar,这一篇基于上一篇的基础扩展为SeekBar,没看过上一篇的,请先看一遍:传送门
先上效果图(2G内存的机子运行模拟器,所以有点卡):
这个效果之前不知道在哪里看到过,我也忘了。
下面进入正题:
测量大小和绘制部分沿用上一篇ProgressBar的,不清楚的请走上面的传送门。
对比上一篇的扩展:
(1)SeekBar能通过触摸改变刻度
(2)SeekBar上方添加一个显示当前刻度的浮动View(后面用FloatView表示)
(1)通过触摸改变刻度:
这个很容易实现,只要处理触摸事件,然后根据触摸坐标修改刻度并请求重绘就行了。
需要注意的一点就是要处理好临界状态,不然可能出现刻度为负值或者最大只能为99的情况。
因为这个很简单,就不再说了,代码会在等一下跟FloatView的实现一起贴出来,因为FloatView也需要触摸事件。
(2)浮动View的实现:
之前在网上看到这个效果时就在思考怎么实现。
刚开始时我想的是通过监听触摸事件,可以计算出FloatView的位置,然后在onDraw把它给画出来。
但是还没开始写我就发现问题了,就是这样的话FloatView就作为SeekBar的一部分,并且它的位置位于SeekBar的范围之外,这样的话FloatView是显示不出来的。
既然有问题,那么就思考解决的办法,我想到三个方法:
(1)动态改变SeekBar的高度,让FloatView可以显示出来。但会引发另一个问题,相邻的View会被挤压,所以该方法不可行。
(2)我记得ViewGroup有个属性可以让子view超过自身的显示范围,我可以在代码中类似这样设置:
((ViewGroup) getParent()).setClipChildren(true);
但这个方法问题更多,首先,你让你的直接父View允许你超过显示范围,但可能你的FloatView显示在你的直接父View的范围外了,这样你必须循环父View的父View,设置所有父View的clipChildren属性,这样可能会影响到页面内其它的View。所以这个方法也不建议采用,我们应该只显示FloatView并且避免在任何布局中对其它View造成影响。
(3)不在onDraw里画,用WindowManager来添加FloatView:
Bingo,就是这个了。用这个方法FloatView不属于SeekBar,甚至不属于这个页面,可以说是属于这个屏幕的,所以不会对页面内的任何View造成影响。更妙的是我们可以显示任何的View,不像在onDraw方法里能画的东西有限(其实在onDraw方法里也是可以画其它的View的,不过处理起来比较麻烦)。
其实我们常用的PopupWindow和现在很多应用都有的桌面悬浮窗都是这种方法。
好了,下面开始讲这种方法的实现,不清楚如何用WindowManager添加FloatView的参考我另一篇博文:传送门
(1)创建一个FloatView:
// 创建FloatViewfloatView = new TextView(getContext());floatView.setGravity(Gravity.CENTER);floatView.setBackgroundResource(R.drawable.shape_circle_blue);floatView.setTextColor(0XFFFFFFFF);floatViewWidth = (int) dp2px(40);floatViewHeight = floatViewWidth;
(2)初始化FloatView的LayoutParams:
// FloatView添加到Window的参数初始化floatLP = new LayoutParams();floatLP.width = floatViewWidth;floatLP.height = floatViewHeight;floatLP.gravity = Gravity.LEFT | Gravity.TOP;floatLP.format = PixelFormat.RGBA_8888;floatLP.windowAnimations = R.style.pppanim;
这里注意看windowAnimations属性,就是为FloatView添加入场和出场的动画效果的,添加的方式跟PopupWindow是一样的(它们都是通过WindowManager添加的),可以看下动画样式:
<style name="pppanim"> <item name="android:windowEnterAnimation">@anim/ppp</item> <item name="android:windowExitAnimation">@anim/pppout</item> </style>
具体的动画可以自己随便定义,Enter代表添加时的动画,Exit代表移除。
(3)开始显示FloatView:
FloatView的显示跟触摸事件挂钩。我们应该在Down事件时往Window中添加FloatView,Move事件时更新FloatView的位置和显示的刻度,在Up事件时从Window中移除FloatView。
注意:往Window中添加两次相同的View和试图移除未添加进Window的View都会产生异常。
@Overridepublic boolean onTouchEvent(MotionEvent event) {switch (event.getAction()) {case MotionEvent.ACTION_DOWN:// 获得状态栏高度statusHeight = getStatusHeight(mContext);// 修改刻度fontRectF.right = event.getX();changeProgress();// 修改FloatView显示文字floatView.setText(mProgress + "");// 修改FloatView的X和Y坐标// X坐标=当前触摸的X-FloatView的宽度/2+该ProgressBar的左边坐标floatLP.x = (int) event.getRawX() - floatViewWidth / 2;// Y坐标=相对屏幕触摸X坐标-前面根据屏幕密度计算出来的垂直间隔-状态栏高度-FloatView的高度floatLP.y = (int) event.getRawY() - mFloatVerticalSpacing - statusHeight - floatViewHeight;// 将FloatView添加进Windowwm.addView(floatView, floatLP);break;case MotionEvent.ACTION_MOVE:float newX = event.getX();// 临界处理if (newX < 0) {newX = 0;} else if (newX > mWidth) {newX = mWidth;}// 修改刻度fontRectF.right = newX;changeProgress();// 修改FloatView显示文字floatView.setText(mProgress + "");// 修改FloatView的X坐标// 临界处理,只有在触摸在Bar范围内才去更新if (event.getRawX() >= getLeft() && event.getRawX() <= getRight()) {floatLP.x = (int) event.getRawX() - floatViewWidth / 2;// 更新FloatView在window中的位置wm.updateViewLayout(floatView, floatLP);}break;case MotionEvent.ACTION_CANCEL:case MotionEvent.ACTION_UP:// 从window中移除FloatView// 在2.3版本模拟器中报IllegalArgumentException,尚未查明原因wm.removeView(floatView);break;}// 我们想处理触摸事件,所以这里要返回true,对触摸事件不清楚的,找我另一篇博文return true;}
具体看注释吧,我写得很详细。这里比较复杂的是计算FloatView的位置和临界处理。
这里要注意getX()和getRawX()的区别:
getX()是相对于这个SeekBar的坐标,getRawX()是相对于屏幕的坐标。
所以当我们计算刻度时,应该用getX()。而计算FloatView的位置时,我们的FloatView是添加进屏幕的而不是添加到SeekBar的,应该使用getRawX(),基本上用WindowManager添加View时,大多数情况下都应该用getRawX()。
发现也没什么好解释的,不懂的话下载Demo自己再研究研究,有点基础的建议直接自己试下实现,原理也很简单。
当然这个控件还有很多地方可以优化,比如:很多属性可以写成通过外部动态设置的,像画笔颜色,FloatView的外观,FloatView的动画,FloatView的垂直间隔。还有2.3模拟器在移除FloatView时不知道为什么报错了,又找不到真机测试,有知道原因的跪求评论指点。
Demo下载
- Android好奇宝宝_番外篇_看脸的世界_05
- Android好奇宝宝_番外篇_看脸的世界_01
- Android好奇宝宝_番外篇_看脸的世界_02
- Android好奇宝宝_番外篇_看脸的世界_03
- Android好奇宝宝_番外篇_看脸的世界_04
- Android好奇宝宝_番外篇_看脸的世界_06
- Android好奇宝宝_番外篇_看脸的世界_07
- Android好奇宝宝_番外篇_看脸的世界_08
- Android好奇宝宝_开山篇_解析ListView点击事件冲突原因
- Android好奇宝宝_06_聊一聊Android里的动画
- Android好奇宝宝_03_有点坑的GridView
- Android好奇宝宝_05_PopupWindow与悬浮窗
- Android好奇宝宝_09_Handler Looper Message
- Android好奇宝宝_10_RecyclerView+CardView+Palette
- Android好奇宝宝_11_SwipeRefreshLayout原理浅析
- 好奇宝宝对于灰度了解的随笔
- Android好奇宝宝_04_一个有3个功能的Adapter
- Unity_设计模式_状态模式_05
- SQL SERVER代理作业删除失败问题
- ios 生成json字符串并去掉里面的空格回车
- 将制定文件路径下的文件内容合并到一个文件
- ClipDrawable的用法(图片切割效果)
- 数据库事务隔离级别定义
- Android好奇宝宝_番外篇_看脸的世界_05
- 织梦dedecms教程-登录后台提示验证码错误的解决方法
- 集合中的Collections排序和Map排序
- Java中String字符去空格和挑选出数字
- java 弱引用集合类WeakHashMap
- Spring3 MVC
- MyEclipse设置默认注释的格式
- windows7下gvim打开UTF8无bom文件中文乱码问题的解决
- Android:点击EditText文本框之外任何地方隐藏键盘的解决办法