概述
自定义控件类别
- 继承已有控件进行拓展
- 组合已有控件进行拓展
View和ViewGroup的区别
自定义控件创建步骤
- 测量(onmeasure(),如果自定义控件继承了已有的viewGroup(如:frameLayout),那么不需要自己实现,继承的viewGroup会自动实现)
- 布局(onlayout(),只有在自定义ViewGroup才使用)
- 绘制(ondraw(),自定义ViewGroup并不需要)
滑动开关(继承自View)
原理
- 自定义控件:继承方式
事例:
- 原理两个图片,通过控制上面图片移动来达到效果
实现onmeasure()
实现ondraw()
注:
invalidate()可以让ondraw()重新绘制,用于刷新视图
处理触摸
原理:
- 实现触摸方法,获取当前X
- 把X作为ondraw()中canvas.drawBitmap()中x的位置
- 通过触摸事件的move来不断invalidate()来刷新视图
处理menu点击事件
-
重写onKeyDown方法
-
通过if判断按键标示
注:
return true拦截事件,自定义处理 -
进一步处理…..
自定义下拉选择
原理
效果图:
原理:组合editText和一个三角图片以及一个listView,当点击图片时,通过如下方法来实现:
注:
- showAsDropDown方法在指定的控件下显示
- 三个参数:指定显示的view,以及view的x,y的偏移量
- 记得判断popupWindow是否存在:
处理点击和删除
去除listView的滚动条属性和设置背景
点击及删除
注:删除后,不要忘记调用notifyDataSetChanged来更新view。
view视图实时更新
解决view高度实时更新和没有项目时消失
下拉标志隐藏
在项目为零时,需要同时隐藏下拉图片
解决popupWindow焦点获取
设置点击外部
popupWindow默认不获取点击焦点
需要设置为:true
点击项目传值到文本框中
注意
解决方案:
- 尽量不在在listView中使用以上控件
- 如果必须使用的话,需要在listView的item布局的根布局添加如下属性:
设置之后,button获取焦点,item中其它控件也可以获取焦点
轮播图(ViewPager)
pagerAdpter
viewPager的数据适配器
必须 实现:
- getCount() //view数目
- isViewFromObject() //判断是否新建view
-
destroyItem() //销毁过载view
-
instantiateItem() //设置数据源
判断是否需要去创建(true:不创建,使用缓存):
注:最多会缓存3个view视图
ellipsize属性
textView中设置文字超过控件显示区域省略号位置的属性
轮播图下面标识点
实现:使用view包含图片排列实现
代码:
实现轮播图无限循环(伪)
原理:使用取余的方法:
值为:
注:
在PagerAdapter的继承类的getCount中返回整数的最大值,来与数据list的项数求余,实现无限循环,由于这种循环有极限,但几乎不可能到极限,但在数学这不是无限但趋近于无限,可以称之为伪无限循环。
解决反向滑动问题
思路:把原来取得整数最大值取一半,通过设置viewPager的setCurrentItem方法把viewpager的项设在一半,为了进一步解决值可能不是第一项(即取余数不为0),需要做如下处理:
解决自动循环的问题
思路:使用hander来解决:在初始化时通过延时发送
sendEmptyMessageDelayed(int what, long delayMillis)
来循环滑动视图,然后进行递归来无限自动循环(不必担心循环完:1 year = 31536000s)
下拉刷新
public class MyListView extends ListView {
public MyListView(Context context)
{
super(context);
init();
}
public MyListView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init(){
}
}
注:在实现继承空间时,不要忘记重写构造函数(为此曾经找了一天bug╮(╯_╰)╭)特别是public MyListView(Context context, AttributeSet attrs)
View.inflate(this, R.layout.layout_header, null);
myList.addHeaderView(headerView);
注:该view的添加需要在setAdapter方法前执行
getHeight和getMeasuredHeigght区别
- getHeight()可以使用监听器来监听view是否渲染完成
- 自定义控件中主要使用getMeasuredHeight()
使用getMeasuredHeight
下拉逻辑:通过触摸检测来设置pading
只用在if (paddingValue > -heightView && getFirstVisiblePosition() == 0)即下拉值小于headerView的高度且可见项为第一个时。
case MotionEvent.ACTION_MOVE:
int onMoveY = (int) ev.getY();
int distanceY = onMoveY - onDownY;
int paddingValue = -heightView + distanceY;
System.out.println("paddingValue的值为:" + paddingValue);
if (paddingValue > -heightView && getFirstVisiblePosition() == 0) {
headerView.setPadding(0, paddingValue, 0, 0);
System.out.println("touch_move");
return true;
}
通过状态码来更新UI
主要的三种状态:
- 下拉刷新
- 松手刷新
- 正在刷新
注:如果控件附加了动画效果的话,在进行操作的时候最好先清除动画: iv_pull.clearAnimation();
因为操作可能与动画产生冲突
注:不要忘记处理refreshing状态下触摸事件
如下:
case MotionEvent.ACTION_MOVE:
//如果处于正在刷新状态,操作失效
if (mCurrent == REFRESHING_REFRESH) {
break;
}
获取当前时间
private String getCurrentTime() {
SimpleDateFormat sdf = new SimpleDateFormat("yy-MM-dd HH:mm:ss");
return sdf.format(new Date());
}
注:yy-MM-dd 这些大小写不能错!!
该方法要放到刷新完成时
代码:
getCurrentTime.java
定时阻塞线程代码:
定时阻塞线程.java
滑动状态监听
状态码:
/**
* The view is not scrolling. Note navigating the list using the trackball counts as
* being in the idle state since these transitions are not animated.
*/
//闲置状态,手指松开瞬间
public static int SCROLL_STATE_IDLE = 0;
/**
* The user is scrolling using touch, and their finger is still on the screen
*/
//手指滑动中
public static int SCROLL_STATE_TOUCH_SCROLL = 1;
/**
* The user had previously been scrolling using touch and had performed a fling. The
* animation is now coasting to a stop
*/
//快速滑动状态,快速滑动后松开瞬间
public static int SCROLL_STATE_FLING = 2;
尾部显示原理
在拉到最后一个数据项且手指滑动松开
注:setSelection()设置listView选中项,以1开头。可以显示到选中项的地方,选中项一般(如果可以)位于屏幕第一项。
注:listview.add()在最后追加项目
侧栏自定义(继承自ViewGroup)
三方库
github:SlidingMenu
自定义控件实现步骤
View和viewGrup的区别
drawablePadding
注:drawablePadding设置drawable图片与文本之间的距离
java
android:drawablePadding="15dp"
drawableLeft:在控件左边显示图片资源
right,top等相似
标题栏高度
注:常见标题栏高度为55dp-60dp之间
关于控件的gravity属性(textview.gravity)
指设置控件内部的子项目,所以textview的gravity设置会对textview的text属性起作用,因为text是textview
控件大小(dp,sp,px)
文字的尺寸一律用sp单位,非文字的尺寸一律使用dp单位。例如textSize="16sp"、layout_width="60dp";偶尔需要使用px单位,例如需要在屏幕上画一条细的分隔线时
onFinishInflate()
getLayoutParams()
getLayoutParams().width
getLayoutParams().height
注:通过view的getLayoutParams()方法可以获取高和宽,在onmeasure中使用,如:
viewgroup的onlayout()
用来设置子view在viewGroup中的位置
注:在该函数中子view的layout()方法中的四个函数分别是子view的左上角和右下角坐标。
viewgroup的onmeasure()
该函数传入的宽和高是viewgroup自身的数据
让控件移动的方法
- 在viewgroup中可以通过layout()来不断改变位置来移动。
- offsetTopAndBottom(offset)和offsetLeftAndRight(offset)
- scrollTo()和scrollBy()
关于scrollTo()和scrollBy()
注:scrollTo()移动的不是ViewGroup而是可视视图,就是眼睛看到的屏幕。
见:
Android scrollTo() scrollBy() Scroller讲解及应用 - 推酷
注:scrollTo()移动是瞬间的,并没有附加动画效果
关于实现onmeasure方法
实现侧滑动画
1.自定义动画实现
2.scroller实现
- 实例化scroller()对象
- 使用startscroll来控制移动
- 调用invalidate()刷新
注:
- startscroll滚动值:正值向左移动,反之亦是
- 资料:[google 关于startscroll()的使用](http://https://developer.android.google.cn/reference/android/widget/Scroller.html#startScroll(int, int, int, int, int)
触摸事件分发机制
解决触摸冲突问题
通过判断移动值(X > 某移动值),来改变onInterceptTouchEvent的返回值(true或false)来决定是否拦截触摸事件。
事例: