基础知识

View是什么?

View类是所有用来构建用户界面的组件的基类
一个View对象占用屏幕上的一个矩形的区域,负责界面的绘制和事件处理
可以说,手机屏幕上所有看得见和都是View对象

ViewGroup是什么?

ViewGroup是view的一个子类,是各种布局的基类
在ViewGroup里面可以包含多个子View和ViewGroup
ViewGroup的作用是控制子view的布局

public abstract class ViewGroup extends View
implements ViewParent, ViewManager

ViewGroup 实现了ViewManager
里面有三个方法进行添加ziview
public void addView(View view, ViewGroup.LayoutParams params);
public void updateViewLayout(View view, ViewGroup.LayoutParams params);
public void removeView(View view);

手机屏幕界面的根是什么?

其实手机屏幕界面的根是一个PhoneWindow类里面的一个内部类DecorView对象
DecorView类是继承于FrameLayout类,所以手机屏幕界面的根本质
就是一个FrameLayout,里面包含了子视图对象,
其中我们最能理解和接收的可能就是里面id为content的FrameLayout子视图
为什么呢?因为我们一直在用
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
比如我们创建工程的时候都会进行调用setContentView()

View和Activity的区别

view是能显示到手机屏幕中的UI组件
Activity是四大组件中唯一能和用户进行直接交互的应用组件
Activity只是控制和管理View,真正显示和处理事件的是View本身

显示ContentView的方式

@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
/*
* 1. setContentView(int layoutId)
* 2. setContentView(View view)
* 2.1. 动态加载布局文件得到视图对象
* 2.2. 动态创建视图对象
*/
//2.1. 动态加载布局文件得到视图对象
//View view = View.inflate(this, R.layout.activity_main, null);

//2.2. 动态创建视图对象
TextView view = new TextView(this);
view.setBackgroundColor(Color.RED);
view.setText("动态创建的");
setContentView(view);
}

View及子类的生命周期

创建对象

创建对象的方式:
1.可以是new出来的
2.加载布局文件,这样如果是自定义的view必须写全类名标签

创建的流程:
首先可以通过两个构造方法进行创建
MyTextView(Context context)
MyTextView(Context context, AttributeSet attrs)

在布局加载中有两个回调方法
onFinishInflate()
onAttachedToWindow()

/**
* 只有加载布局文件的方式才会调用
* 重写它: 用于得到子View对象
*/
@Override
protected void onFinishInflate()
{
Log.e("TAG", " onFinishInflate()");
super.onFinishInflate();
}

/**
* 无论new还是布局都会调用此方法
* 重写它: 用于得到子View对象
*/
@Override
protected void onAttachedToWindow()
{
Log.e("TAG", " onAttachedToWindow()");
super.onAttachedToWindow();
}

注意:
在Activity里面有个onResume()方法,这个方法的执行时间是:
在onFinishInflate()之后,onAttachedToWindow()之前

@Override
protected void onResume()
{
Log.i("TAG", "onResume()");
super.onResume();
}

测量

作用:
计算和确定视图的宽高

执行的流程:
经历两个方法:
measure():系统在这个方法里面测量计算出当前视图的宽高,
这个方法是final,不能被重写
onMeasure():当measure()计算出视图的宽高,就会调用此方法,
在这个方法中默认保存视图的宽高,重写这个方法可以
做一些想要的工作,比如让它保存我们指定的宽高

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

int measuredHeight = this.getMeasuredHeight();
int measuredWidth = this.getMeasuredWidth();
Log.e("TAG", "onMeasure() measuredHeight="+measuredHeight+" measuredWidth="+measuredWidth);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}

布局

作用:
确定视图显示的坐标.

流程方法:
layout(l,t,r,b);一样,也不会重写这个方法,指挥调用视图对象的这个方法
指定新的显示位置
onlayout(); 会重写这个方法,这个方法主要是确定子视图的位置,上面的
layout()是确定当前视图的位置,还有如果视图的位置发生改变
或者强制重新布局,就会调用这个方法
强制重新布局的方法:View.requestLayout()
可以看下layout的实现,还有它调用onLayout的条件
if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED)
这个就是确定这个视图有没有改变或者有没有强制重新布局
@SuppressWarnings({"unchecked"})
public void layout(int l, int t, int r, int b) {
int oldL = mLeft;
int oldT = mTop;
int oldB = mBottom;
int oldR = mRight;
boolean changed = isLayoutModeOptical(mParent) ?
setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);
if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
onLayout(changed, l, t, r, b);
mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED;

ListenerInfo li = mListenerInfo;
if (li != null && li.mOnLayoutChangeListeners != null) {
ArrayList<OnLayoutChangeListener> listenersCopy =
(ArrayList<OnLayoutChangeListener>)li.mOnLayoutChangeListeners.clone();
int numListeners = listenersCopy.size();
for (int i = 0; i < numListeners; ++i) {
listenersCopy.get(i).onLayoutChange(this, l, t, r, b, oldL, oldT, oldR, oldB);
}
}
}
mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;
}

绘制

作用:
画出视图的样子

经过的流程:
1.public void draw(Canvas canvas)
这个方法是绘制视图的通用部分,确定绘制的流程,一般不会被重写
里面的Canvas参数就是画布
那么和它配套的类有一个:Paint类,对应是画笔类
draw(Canvas canvas)方法里面有一些流程:
/*
* Draw traversal performs several drawing steps which must be executed
* in the appropriate order:
*
* 1. Draw the background
* 2. If necessary, save the canvas' layers to prepare for fading
* 3. Draw view's content
* 4. Draw children
* 5. If necessary, draw the fading edges and restore layers
* 6. Draw decorations (scrollbars for instance)
*/
就是首先画背景,
然后如果需要,就保存画布的一些边缘的部分
然后画视图的内容
然后画孩子的内容
然后如果需要,绘制淡入淡出的边缘并恢复图层
然后画一些装饰,例如滚动条

比如第四步:画孩子,就是调用了dispatchDraw(canvas);
// Step 4, draw the children
dispatchDraw(canvas);

2.protected void onDraw(Canvas canvas)
这个方法就需要重写了,这个方法绘制自己需要的样子,
一些具体的View都重写了这个方法
强制重绘:
这个是将视图对象重新进行绘制,只要使用
视图对象来调用下面两个方法其中的一个就能重新绘制了
invalidate() :只能在主线程执行
postInvalidate() :可以在主线程和分线程执行

事件处理

流程方法:
1.dispatchTouchEvent(MotionEvent event) :分发事件
2.在ViewGroup里面:public boolean onInterceptTouchEvent(MotionEvent ev)
事件拦截,只有返回true就会进行拦截
拦截成功后事件就不会再向内层分发,而是交给当前的视图处理
3..public boolean onTouchEvent(MotionEvent event) :处理事件,消费事件

public void requestDisallowInterceptTouchEvent(boolean disallowIntercept)
这个分发就是请求不拦截触摸事件
一般是父视图拦截触摸事件,然后子视图想叫父视图不要拦截这些事件
所以一般写成:view.getParent().requestDisallowInterceptTouchEvent(true);
分发:将TouchEvent对象从Activity对象开始,由外向内分发给对应的布局和子view对象

处理:就是OnTouchListener的onTouch()方法和onTouchEvent()回调方法

消费:就是消费,没了

拦截:onInterceptTouchEvent()执行返回true,就会拦截事件,事件就不会传到子view对象

反拦截:view.getParent().requestDisallowInterceptTouchEvent(true);,
让父亲不要再拦截本来应该传到子view的事件

死亡

死亡时间:
视图对象被移除,或者在Activity死亡之前

经过流程:
protected void onDetachedFromWindow()
就是结束之前执行上面这个方法

ListView优化问题

不优化

这样每次getView()都会执行
convertView = View.inflate(MainActivity.this,R.layout.item_simple_adapter, null);
问题:效率低下,快速滑动会卡顿,数据很多的时候甚至内存溢出

复用converterView优化

就是多了复用converterView
if(converterView==null)
{
converterView = View.inflate(MainActivity.this,R.layout.item_simple_adapter, null);
}

问题:
每次执行getView()都需要执行convertView.findViewById(R.id.iv_item_icon);得到子View

使用ViewHolder减少findViewById次数优化

/**
* 返回指定下标所对应的item的View对象 position : 下标
* convertView : 可复用的缓存Item视图对象,
* 前n+1个为null parent : ListView对象
*/
@Override
public View getView(int position, View convertView, ViewGroup parent)
{
// 1. 创建或得到对应当前行的一个ViewHolder对象
ViewHolder holder = null;
if (convertView == null)
{
Log.e("TAG", "getView() " + position);
holder = new ViewHolder();
convertView = View.inflate(MainActivity.this,R.layout.item_simple_adapter, null);
holder.imageView = (ImageView) convertView.findViewById(R.id.iv_item_icon);
holder.nameTV = (TextView) convertView.findViewById(R.id.tv_item_name);
holder.priceTV = (TextView) convertView.findViewById(R.id.tv_item_content);
// 将holder对象保存到convertView上
convertView.setTag(holder);
} else
{
holder = (ViewHolder) convertView.getTag();
}
// 2. 得到当前行的数据对象
ShopInfo shopInfo = data.get(position);
// 3. 给ViewHolder对象的视图设置数据
holder.imageView.setImageResource(shopInfo.getIcon());
holder.nameTV.setText(shopInfo.getName());
holder.priceTV.setText(shopInfo.getContent());

return convertView;
}

class ViewHolder
{// 视图的容器类
public ImageView imageView;
public TextView nameTV;
public TextView priceTV;
}