自定义控件基础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,关闭的状态

)

③手指按下位置与用户预期不一样(用户希望的是移动的点是中间的点,而实际上是按下的点)

重新绘制的时候,左边距的数值>>调整>>左边距减去滑动块宽度的一半.

最后再对这个数据进行判断.

原文地址:https://www.cnblogs.com/adventurer/p/5642026.html