1.Android控件架构
下图是UI界面架构图,每一个Activity都有一个Window对象,一般是由PhoneWindow类来实现的。
PhoneWindow将DecorView做为整个应用窗口的根View,DecorView将屏幕分红两部分:TitleView和ContentView。
ContentView其实是一个FrameLayout,里面容纳的就是咱们在xml布局文件中定义的布局。linux
为何调用requestWindowFeature()方法必定要在setContentView()方法调用以前?
当程序在onCreate()方法中调用setContentView()方法后,ActivityManagerService会回调onResume()方法,此时系统才会将整个DecorView添加到PhoneWindow中,并让其显示出来,从而完成界面的绘制。android
2.View的测量:MeasureSpec和测量模式
MeasureSpec是一个32位的int值,其中高2位是测量的模式,低30位是测量的大小 (使用位运算是为了提升效率和节省空间)
测量模式有三种:
(1)EXACTLY
:精确值模式,属性设置为精确数值或者match_parent
时,系统使用的是EXACTLY
模式
(2)AT_MOST
:最大值模式,属性设置为wrap_content
时,系统使用的是AT_MOST
模式
(3)UNSPECIFIED
:不指定大小测量模式,一般状况下在绘制自定义View时才会用到git
View类默认的onMeasure()方法只支持EXACTLY模式,因此若是在自定义View的时候不重写onMeasure方法的话,就只能使用EXACTLY模式。自定义View能够响应你指定的具体的宽高值或者是match_parent属性,可是,若是要让自定义View支持wrap_content属性的话,那么就必需要重写onMeasure方法来指定wrap_content时view的大小。github
重写onMeasure方法的最终工做就是把测量后的宽高值做为参数设置给setMeasuredDimension方法。canvas
@Override |
3.View和ViewGroup的绘制
View的onDraw()方法包含一个参数Canvas
对象,使用这个Canvas对象就能够进行绘图了。架构
一般状况下,Canvas对象的建立须要传入参数Bitmap
,为何呢?
这是由于传进去的Bitmap与经过这个Bitmap建立的Canvas画布是牢牢联系在一块儿的,这个Bitmap用来存储全部绘制在Canvas上的像素信息,当使用Bitmap建立Canvas以后,后面调用全部的Canvas.drawXXX方法都发生在这个Bitmap上。ide
ViewGroup一般不须要绘制,由于它自己没有须要绘制的东西,若是不指定ViewGroup的背景颜色,那么ViewGroup的onDraw方法都不会被调用。可是,ViewGroup会调用dispatchDraw方法来绘制其子view,其过程一样是经过遍历全部子view并调用子view的绘制方法来完成绘制工做的。布局
4.自定义View(ViewGroup)
三种自定义View的方式:
(1)对现有控件进行扩展
对现有控件进行扩展的代码结构一般以下:post
@Override |
例如,书中对TextView进行扩展代码节选字体
private void initView() { |
(2)经过组合来实现新的控件
这种方式一般须要继承一个合适的ViewGroup,再给它添加指定功能的控件,从而组合成新的复合控件。
[项目中通常使用这种方式建立应用内统一的提示信息界面,能够是提示正在加载,也能够是提示数据出错了等]
例如,书中的TopBar例子:
public class TopBar extends RelativeLayout { |
(3)重写View来实现全新的控件
建立自定义View的难点在于绘制控件和实现交互,一般须要继承View类,并重写onDraw、onMeasure等方法来实现绘制逻辑,同时经过重写onTouchEvent等触控事件方法来实现交互逻辑。
[这类自定义View是比较经常使用的,本身之前也写过几个简单的例子,参见AnnotationView和ProgressView项目,或者参考以前的博文Android Text View with Custom Font,一个能够自定义字体的TextView]
例如,书中的弧线展现图例子
@Override |
5.事件拦截机制分析 [后面有专门对Android事件拦截机制分析的部分,此处略过]
1.使用ViewHolder模式提升效率
这种方式是必需要用的!下面的例子是一个常见的使用ViewHolder而且包含多个item type的Adapter例子:
public class ChatItemListViewAdapter extends BaseAdapter { |
2.listview的一些属性设置
(1)设置分隔线android:divider=""@android:color/white"
android:dividerHeight="10dp"
android:divider="@null"
(设置分隔线透明)
(2)隐藏滚动条android:scrollbars="none"
(3)取消item的点击效果android:listSelector="@android:color/transparent"
3.listview的一些方法设置
(1)设置listview显示在第几项listview.setSelection(n);
这个方法相似scrollTo瞬间完成移动,平滑移动可使用下面的方式listview.smoothScrollBy(distance, duration);
listview.smoothScrollByOffset(offset);
listview.smoothScrollToPosition(index);
(2)处理空listviewlistview.setEmptyView(View)
4.动态修改listview
在使用adapter的notifyDataSetChanged方法时,必须保证传进adapter的数据list和发生数据变化的list是同一个对象,不然将没法看到效果。
5.listview滑动监听
监听listview的滑动事件的方法有两种:一个是OnTouchListener来实现监听,另外一个是使用OnScrollListener来实现监听。
例如,书中实现了一个监听listview上下滑动事件操纵toolbar显示和隐藏效果的例子:
public class ScrollHideListView extends Activity { |
监听listview的OnScrollListener的通常实现
mListView.setOnScrollListener(new AbsListView.OnScrollListener() { |
得到当前可视的item的位置等信息的便捷方法
mListView.getLastVisiblePosition();//获取可视区域最后一个item的id |
1.获取坐标值的各类方法
图片来自Android中的坐标系以及获取坐标的方法
2.实现滑动的基本思想
当触摸view时,系统记下当前触摸点坐标;当手指移动时,系统记下移动后的触摸点坐标,从而获取到相对于前一次坐标点的偏移量,并经过偏移量来修改view的坐标,这样不断重复,从而实现滑动过程。
3.经常使用的滑动实现方法
(1)修改view的left、top、right和bottom的值:调用layout
方法或者offsetLeftAndRight
等方法
绝对坐标系下
// 绝对坐标方式 |
视图坐标系下
// 视图坐标方式 |
(2)修改布局参数LayoutParams:修改子view的getLayoutParams或者使用ViewGroup.MarginLayoutParams
子view的getLayoutParams获得的LayoutParams须要和父ViewGroup的Layout类型一致,若是使用ViewGroup.MarginLayoutParams的话那就方便一些,不须要考虑父ViewGroup的具体类型。
@Override |
(3)使用scrollTo和scrollBy方法
scrollTo和scrollBy方法移动的是view的content,即让view的内容移动。若是在ViewGroup中使用scrollTo或者scrollBy方法,那么移动的是全部子view。但若是在view中使用,那么移动的将是view的内容,例如TextView,content就是它的文本;ImageView,content就是它的drawable对象。
@Override |
(4)使用Scroller实现平滑效果
前面的滑动都不是平滑的,而Scroller是能够实现平滑效果的,它的实现原理很简单,其实就是不断调用scrollTo和scrollBy方法来实现view的平滑移动,由于人眼的视觉暂留特性看起来就是平滑的。
使用Scroller主要有三个步骤:
1.初始化Scroller对象,通常在view初始化的时候同时初始化scroller;
2.重写view的computeScroll
方法,computeScroll
方法是不会自动调用的,只能经过invalidate->draw->computeScroll
来间接调用,实现循环获取scrollX和scrollY的目的,当移动过程结束以后,Scroller.computeScrollOffset
方法会返回false,从而中断循环;
3.调用Scroller.startScroll
方法,将起始位置、偏移量以及移动时间(可选)做为参数传递给startScroll
方法。
例如,书中给出的例子,子view在被拖动以后会自动平滑移动到原来的位置
private void ininView(Context context) { |
(5)属性动画 [后面有专门对Android动画分析的部分,此处略过]
(6)使用ViewDragHelper
ViewDragHelper类使用较少,它是support库中DrawerLayout和SlidingPaneLayout内部实现的重要类!
以前读过相似侧边栏菜单的实现代码(SlidingMenu),我的感受ViewDragHelper实际上是更高层次的封装,将这类效果所需的接口暴露出来以简化相似的开发工做,书中给了一个例子,介绍了ViewDragHelper的使用,实现了相似侧边栏菜单的效果
public class DragViewGroup extends FrameLayout { |