自定义控件基础01_菜单轮__viewPager_下拉框_自定义开关
Posted 抓根宝
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了自定义控件基础01_菜单轮__viewPager_下拉框_自定义开关相关的知识,希望对你有一定的参考价值。
1,自定义控件分类:
1.1组合控件:由安卓中原生的控件组合起来,配合动画达成的效果
1.2自定义控件
1.3组合控件案例演示:
案例:优酷菜单demo
三层圆环,按下menu键会通过动画效果消失在界面,点击小房子和中层圆环,最外层圆环消失
①布局实现:
三层相对布局相互叠加(因为图片背景是透明的,所以可以叠加显示)
由于三个布局是叠加显示的,所以这个菜单选项要使用一个占据焦点比较强的(不然有可能点击不到)ImageButton控件
控件上background=”@android:color/transparent”//透明色
②动画效果,三级菜单的隐藏和显示
动画效果,点击二级菜单,三级菜单旋转消失,再次点击旋转回来显示
每一层实际上是一个正方形(圆环在一个背景透明的正方形上),那么只要它做自身的的旋转,就能起到圆环旋转动画的实现,从界面消失,即以底部居中为中心左旋转180度,从界面出现,即以底部居中为中心右旋转180度,就能实现这个效果.
am.setFillAfter()//设置控件保持在动画结束的状态
am.setStartOffset()//设置延迟执行动画
一级菜单的主要是控制二级菜单,如果三级菜单也存在就一起控制,三级菜单先消失,二级菜单延迟消失.但是再次点击一级菜单并不会返回三级菜单
点击menu按钮,三个菜单全部消失,延迟有序
③额外:动画播放完了,圆环隐藏了,但是还可以被点击,在执行隐藏的时候设置按钮不可用
setEnable()//设置是否可用
动画没播放完就再次点击会终止未执行完的动画,所以,在执行隐藏或显示的动画时,设置一个监听器,如果动画播放完了,才能运行执行下一个动画(定义一个变量控制播放动画的数量)
1.4 ViewPager android3.0版本就已经含有此控件(实现图片轮播效果)
Support-v4.jar包包含了ViewPager控件,低版本就可以使用这个控件了
低版本开发的时候,导入viewPager,拷贝全类名使用该控件(类似自定义控件)
在布局文件中的颜色#66(透明度)66(R)66(G)66(B)
viewPager用法
①根据需求创建imageView的数量,并填充数据,设置图片
②viewPager.setAdapter(PagerAdapter)//设置对应的适配器就行了
如果适配器类参数名异常(argxx)需要导入源码(如果点入类之后找不到导入源码的按钮,就卸载v4包重新add一下,就能导入源码了)
源码路径sdk/extras/android/support/v4/src/java
适配器类中的几个重要方法
getCount()//返回的条目数
isViewFromObject(xx,xx) //判断是否使用缓存,如果返回的是true,使用缓存,不去调用instantiXX方法创建一个新对象,
简单来说:如果用户未滑动出界,该图片对象不改变,是否需要重新初始化一个对象来显示.
返回值推荐写法view==object//如果是一个对象就使用缓存,如果不是就创建一个新的
instantiateItem()//初始化一个条目
预加载功能:会预加载左右两张图片,即初始化左右两张图片
//初始化一个条目,把position对应的imageView添加到ViewPager中
viewPager.addView(imageViewList.get(position));
返回这个view;
destoryItem()//销毁一个条目,position当前被销毁的索引
//移除掉ViewPager中的ImageView
viewPager.removeView(imageViewList.get(position));
额外:如果出现找不到方法,但工程内又确实有jar包,去查配置build path,打钩即可
1.4.2 图片轮播的进度点和图片描述数组的实现
①在循环添加图片的时候,也给进度点对应的父控件添加一个小点,开发中有美工做,这里通过xml.
小圆点的创建
创建shape节点的xml文件
shape属性:shape=”oval” //圆形
子节点corners //弧度5dp即可
Solid //固定颜色
//记得要创建两个,一个带焦点,一个不带焦点的
②添加的时候,记得设置对应参数 设置宽高
View v = new (this)
v.setBacKGxxxxx(id);
LayoutParams params = new xxxx(w,h)//设置宽高
Params.xxxx//可以设置对应的参数,控件间的距离,设置是否被选中(设置一个状态选择器可以实现切换进度小圆点的效果)
v.setLayoutParams(params);//设置参数,必须要设置参数,否则无法显示
//最后添加进Linerlayout中
③图片描述信息的数据索引要匹配上适配器的图片数据
额外:设置默认的选中点,注意代码的顺序,要在设置完数据之后设置.
④根据viewPager切换状态来更改进度点状态和信息显示
viewPager.setOnPageChangeListener(this)//设置页面切换监听器
两个方法
onPageSelected()//当页面被选中(完全显示)的时候,触发此方法
更改进度点父节点的子节点集合中子孩子(进度点的状态)
更改文描述文本的显示
别忘了修改前一个节点的状态(拿个引用记录它,如果position直接+-的话,容易逻辑混乱)
onPageScrolled()//当页面卷曲的时候调用
1.4.3 伪无限循环和动态切换
①viewPager移动到第一个(0)或者最后一个的时候(最大索引),
就不会预加载上一个(-1)或 下一个(最大索引+1)了
所以,让返回的count很大就行(比如2g-1(Integer.Max_Value))(数学小技巧)
那么对应的索引就是count%集合长度//相当于倍数取余,可以对应到指定的索引
额外:初始化的时候,把默认选中的点为返回的count中间的值(这样↔滑动都不会出现停滞阻塞的情况了),要让选中的点在第一个位置,就让中间值再减去倍数取余
pageView.setCurrentItem()//设置默认选中的坐标
额外:设置默认选中点,实际上是调用了onPageSelected()方法,记得调换前一个点和当前点设置的位置,因为一开始这样两个都是0,所以要在后面要设置为选中.在前面的设置为未选中.
②动态切换
开启一个子线程,定期切换viewPager的图片(这是一个修改ui的操作).
但要注意的是,子线程在activity中不会被直接销毁,activity的ondestory()方法中设置一个变量去控制这个无限循环
额外:休眠的操作放到循环最后,就不会多输出一次(因为变量控制的时候,它如果已经睡眠了,还会多输出一次,所以这也算一个小优化)
2 组合控件:下拉选择框(ListView填充),参考效果:
2.1参考布局:
点击向下的箭头,出现下拉框,点击下拉框选项会把对应的号码填充到输入框中
下拉框实际上是popupWindow,内容是一个ListView
设置一个适配器,填充数据
把listView添加到popupWindow上面
PopupWindow pw = new PopupWindow(listview,w,h);
W:宽度:输入框的宽度,et.getWidth();
H;高度:整个屏幕都可以,设死也可以
//显示在界面上,显示在某个控件下
pw.showAsDropDown(输入框,偏移量x,偏移量y)
2.2 细节问题
①外形边框是listView的背景图片
②listView.setVerticalScrollBarEnable()//设置是否显示右侧垂直滑动条
如果全部条目都被删除了,就应该关闭popup.
③Button,imagebutton,checkbox这一类控件抢焦点能力都很强.
PopupWindow本身是不可以使用焦点的,设置可以使用焦点,不生效
原因:子条目的button或imageButton把焦点抢走了
//设置子控件不可以抢占父元素的事件,但是可以以块去分配事件
解决:给子条目LinearLayout布局:descendantFocusablity=”blocksDescXXX”
④popupWindow跟输入框之间有细微的空白
因为输入框本身是有边框宽度的,而获取的输入框宽度不包含它,所以稍微减少一点就行.
⑤点击其它地方也要让它会被关闭
popupWindow.setOutsideTouchable()//点击外部可以被关闭,单独设置不生效
popupWindow.setBackground(xxxx)//设置一个背景,才能让上面的设置生效
3.自定义控件(前面都是组合控件)
3.1 滑动开关,参考效果图,拖动方块可以实现开关切换
①安卓中每一个控件或者布局都是继承自View类(控件的爸爸),创建一个类去继承View
需要重写的构造方法
如果要在java中new出来,就重写只有Context 的构造
如果要在 布局文件中引用自定义控件时,使用有Context和atts的构造
一般两个都写.
②拷贝对应的图片
创建方法(都是公开,调用者可自定义,从这一角度考虑自定义控件功能的实现),
设置背景图片setSwitchBackgorundResource()//这个名字更容易理解,
设置滑动块的图片
设置当前开关的默认状态.
用对应的成员变量去记录下来,方便在绘制的时候处理.
③自定义控件的绘制
android中View的绘制流程
不要直接去覆盖,使用谷歌提供的回调接口
Measure(测量宽高信息) >>>Layout(排版/布局,包含子控件) >>>draw(绘制)
onMeasure(当测量时调用)>>>onLayout(布局时回调) >>>onDraw(当绘制时)
3.2 绘制控件
①在onMeasure方法中测量并设置自己的宽和高
重写onMeasure(xxx)方法
设置宽高和背景图片的宽和高一致
setMeasureDimension(w,h);//设置测量后的宽高
super.xxx要在最前面,不然不能生效
②没有子控件,不需要onLayout回调,在onDraw()方法中,把开关绘制出来
重写onDraw(Canvas)方法,super.onDraw()//可以删除掉
Canvas 画板,使用canvas所画出来的东西,都是作为当前控件,在屏幕上显示
//把背景图片平铺在当前控件上
Canvas.drawBitMap(图片,左,上,画笔)//0,0(左上角),null(绘制图片不需要画笔)
//根据当前状态currentState(成员变量)来绘制滑动块状态
如果为true,绘制在背景图片右边(左边距为背景宽减去开关宽,上边距为顶部0即可)
如果为false,绘制在背景图片的左边(左边距为0,上边距起点0即可)
3.3触摸移动改变状态
①重写onTouchEvent()//返回值为true消耗当前事件,自己处理,返回为false交给别人处理.
②判断事件类型
观察可知,移动的时候只改变X 轴值
记录按下的点,移动时候的点,两者的x轴值相减就是位移范围(正数向左,负数向右).
如果采用这种办法,需要记录下初始的位置,再对其进行修改
实际上,直接用移动的点数值给左边距设置数值即可.
当移动的点发生改变的时候,手动触发onDraw方法的重绘.
invaliadte()//刷新当前控件,会引起onDraw方法的调用.
③在onDraw()方法里,根据X轴的值来动态的修改滑动块的位置
Canvas.drawBitMap(滑动块图片,左边距,0上边距不变,null不需要画笔);
3.3.2用户体验问题
①会移出边界
限定宽度,滑动块的左边距不能小于0,如果小于0,绘制的时候就一直设置为0
左边距(滑动块的左边距)同样也不能大于(背景图片的宽度-滑动块的宽度),
如果大于,就一直为两者之差.
②移动的时候,如果在中间松开,不会自动归位
先判断是否在滑动中(不然滑动的时候就会直接跳位,体验不好)
松开的时候(设置为不在滑动中),根据左边距的值判断状态
如果不在滑动中,两种方法
(判断滑动块的左边距是否大于整个背景图片的四分之一,
如果大于就代表用户想要关闭它,如果小于就代表用户想要开启它.根据结果跳位)
(或者滑动块的中心点和背景图片的中心点值的比较,
如果滑动块中心点>背景图片中心点值,当前状态重置为true,打开的状态
如果小于,就重置为 false,关闭的状态
)
③手指按下位置与用户预期不一样(用户希望的是移动的点是中间的点,而实际上是按下的点)
重新绘制的时候,左边距的数值>>调整>>左边距减去滑动块宽度的一半.
最后再对这个数据进行判断.
以上是关于自定义控件基础01_菜单轮__viewPager_下拉框_自定义开关的主要内容,如果未能解决你的问题,请参考以下文章
项目一众筹网05_01_[树形结构开发]菜单维护-树形结构基础知识自关联zTree的介绍和使用如果可以尽量不要嵌套循环时间复杂度和空间复杂度的区别