android基于linux内核,也就是说操作系统层面的工作由linux来做。android程序运行于DalvikVM,这个设计来自于JVM。在这两方面,android主要是靠借鉴。android系统需要解决的重要工作就是全新的交互方式,区别于功能机时代的按键方式,而这个功能的实现,在用户层面来讲,就是View。所以,View的两个设计初衷就是要显示内容,以及响应事件。
我们从部分View的注释中可以看出来:
* This class represents the basic building block for user interface components. A View
* occupies a rectangular area on the screen and is responsible for drawing and
* event handling.
在Java中一般接口是用来规范行为用的,先看View实现的接口:
@UiThread
public class View implements Drawable.Callback, KeyEvent.Callback, AccessibilityEventSource {
//省略
}
在屏幕上点击Button时,android的inputManager接收到这个事件时,放在一个队列中,而另一个线程,负责从这个队列中拿出一个个事件进行分发。inputService通过pipe和binder,将这个事件交由于给windowMangerSevice,wms将这个事件交给ams,再通过binder将事件传给activity,activity交给phoneWindow,这个便是整个屏幕的rootview,最终,调用目标View的ontouchevent这个方法。如果是按下了back键,又是如何传递的呢?android的所有事件,包括触摸事件和按键事件,都是由同一个单独的进程来负责。所不同的是在client中,处理的对象不同。touch事件,是有先后顺序的(这个后面分析),而key事件,activity和view均可处理(均实现了KeyEvent.CallBack)。以onkeyDown为例
View中
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (KeyEvent.isConfirmKey(keyCode)) {
if ((mViewFlags & ENABLED_MASK) == DISABLED) {
return true;
}
if (event.getRepeatCount() == 0) {
// Long clickable items don't necessarily have to be clickable.
final boolean clickable = (mViewFlags & CLICKABLE) == CLICKABLE
|| (mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE;
if (clickable || (mViewFlags & TOOLTIP) == TOOLTIP) {
// For the purposes of menu anchoring and drawable hotspots,
// key events are considered to be at the center of the view.
final float x = getWidth() / 2f;
final float y = getHeight() / 2f;
if (clickable) {
setPressed(true, x, y);
}
checkForLongClick(0, x, y);
return true;
}
}
}
return false;
}
activity中:
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
if (getApplicationInfo().targetSdkVersion
>= Build.VERSION_CODES.ECLAIR) {
event.startTracking();
} else {
onBackPressed();
}
return true;
}
if (mDefaultKeyMode == DEFAULT_KEYS_DISABLE) {
return false;
} else if (mDefaultKeyMode == DEFAULT_KEYS_SHORTCUT) {
Window w = getWindow();
if (w.hasFeature(Window.FEATURE_OPTIONS_PANEL) &&
w.performPanelShortcut(Window.FEATURE_OPTIONS_PANEL, keyCode, event,
Menu.FLAG_ALWAYS_PERFORM_CLOSE)) {
return true;
}
return false;
} else if (keyCode == KeyEvent.KEYCODE_TAB) {
// Don't consume TAB here since it's used for navigation. Arrow keys
// aren't considered "typing keys" so they already won't get consumed.
return false;
} else {
// Common code for DEFAULT_KEYS_DIALER & DEFAULT_KEYS_SEARCH_*
boolean clearSpannable = false;
boolean handled;
if ((event.getRepeatCount() != 0) || event.isSystem()) {
clearSpannable = true;
handled = false;
} else {
handled = TextKeyListener.getInstance().onKeyDown(
null, mDefaultKeySsb, keyCode, event);
if (handled && mDefaultKeySsb.length() > 0) {
// something useable has been typed - dispatch it now.
final String str = mDefaultKeySsb.toString();
clearSpannable = true;
switch (mDefaultKeyMode) {
case DEFAULT_KEYS_DIALER:
Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse("tel:" + str));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
break;
case DEFAULT_KEYS_SEARCH_LOCAL:
startSearch(str, false, null, false);
break;
case DEFAULT_KEYS_SEARCH_GLOBAL:
startSearch(str, false, null, true);
break;
}
}
}
if (clearSpannable) {
mDefaultKeySsb.clear();
mDefaultKeySsb.clearSpans();
Selection.setSelection(mDefaultKeySsb,0);
}
return handled;
}
}
再看看KeyEvent中:
/** Whether key will, by default, trigger a click on the focused view. * @hide */
public static final boolean isConfirmKey(int keyCode) {
switch (keyCode) {
case KeyEvent.KEYCODE_DPAD_CENTER:
case KeyEvent.KEYCODE_ENTER:
case KeyEvent.KEYCODE_SPACE:
case KeyEvent.KEYCODE_NUMPAD_ENTER:
return true;
default:
return false;
}
}
可以看出,activity和view都可以处理key事件,只是view处理的如空格,换行等事件,而activity处理回退,全局搜索等事件.
public class MyView extends View {
private Paint paint;
public MyView(Context context) {
super(context);
}
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
paint = new Paint();
paint.setColor(Color.RED);
}
public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawCircle(getWidth()/2,getHeight()/2,200,paint);
}
}
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical">
<com.example.administrator.testview.MyView android:id="@+id/myview" android:layout_width="wrap_content" android:layout_height="wrap_content" />
LinearLayout>
这个例子说明了什么?
1.继承VIEW
2.在XML中添加,属性为wrap_content。
3.在第二个构造函数中初始化。
这不是个完整的的自定义VIEW,里面只重写了ondraw,没有重写onmeasure,也没有在xml中添加自定义属性。因为宽高是wrap_content,显示的效果就是全屏,如果要给出特定的宽高,就必须重写onmeasure。
我们来看看为什么。
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
这段是VIEW的onmeasure方法,最终结果便是setMeasuredDimension这个方法确定,你可以设定任意值,比如说setMeasuredDimension(100,100),这样大小就是100*100。
public static int getDefaultSize(int size, int measureSpec) {
int result = size;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
switch (specMode) {
case MeasureSpec.UNSPECIFIED:
result = size;
break;
case MeasureSpec.AT_MOST:
case MeasureSpec.EXACTLY:
result = specSize;
break;
}
return result;
}
从上面代码可以看出specMode为MeasureSpec.UNSPECIFIED的时候,也就是wrap_content的时候,给的是size,也就是默认值,来看看这个值是多少。
protected int getSuggestedMinimumWidth() {
return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());
}
protected int getSuggestedMinimumHeight() {
return (mBackground == null) ? mMinHeight : max(mMinHeight, mBackground.getMinimumHeight());
}
这下就真相大白了,默认的是MATCH_PARENT。所以为什么是全屏。