View類是ViewGroup的父類,ViewGroup具有View的所有特性,ViewGroup主要用來充當View的容器,將其中的View作為自己孩子,並對其進行管理,當然孩子也可以是ViewGroup類型。
View類一般用於繪圖操作,重寫它的onDraw方法,但它不可以包含其他組件,沒有addView(View view)方法。
ViewGroup是一個組件容器,它可以包含任何組件,但必須重寫onLayout(boolean changed,int l,int t,int r,int b)和onMesure(int widthMesureSpec,int heightMesureSpec)方法. 否則ViewGroup中添加組件是不會顯示的。
<strong><span style="font-family:FangSong_GB2312;font-size:14px;">package com.example.testrefreshview;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new MyViewGroup(this));
}
public class MyViewGroup extends ViewGroup {
public MyViewGroup(Context context) {
super(context);
Button button1 = new Button(context);
button1.setText("button1");
Button button2 = new Button(context);
button2.setText("button2");
TextView textView = new TextView(context);
textView.setText("textView");
addView(button1);
addView(button2);
addView(textView);
}
@Override
protected void onLayout(boolean arg0, int arg1, int arg2, int arg3,
int arg4) {
}
}
}</span></strong>
View的layout(int left,int top,int right,int bottom)方法負責把該view放在參數指定位置,所以如果我們在自定義的ViewGroup::onLayout中遍歷每一個子view並用view.layout()指定其位置,每一個子View又會調用onLayout,這就構成了一個遞歸調用的過程
如果在ViewGroup中重寫onDraw方法,需要在構造方法中調用this.setWillNoDraw(flase); 此時,系統才會調用重寫過的onDraw(Canvas cancas)方法,否則系統不會調用onDraw(Canvas canvas)方法.
將上面代碼修改一下,就可以顯示出來。
<strong><span style="font-family:FangSong_GB2312;font-size:14px;">package com.example.testrefreshview;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new MyViewGroup(this));
}
public class MyViewGroup extends ViewGroup {
public MyViewGroup(Context context) {
super(context);
Button button1 = new Button(context);
button1.setText("button1");
Button button2 = new Button(context);
button2.setText("button2");
TextView textView = new TextView(context);
textView.setText("textView");
addView(button1);
addView(button2);
addView(textView);
}
@Override
protected void onLayout(boolean arg0, int arg1, int arg2, int arg3,
int arg4) {
int childCount = getChildCount();
int left = 0;
int top = 10;
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
child.layout(left, top, left + 60, top + 60);
top += 70;
}
}
}
}</span></strong>
再看一段代碼:
<strong><span style="font-family:FangSong_GB2312;font-size:14px;">@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int childCount = getChildCount();
//設置該ViewGroup的大小
int specSize_width = MeasureSpec.getSize(widthMeasureSpec);
int specSize_height = MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(specSize_width, specSize_height);
for (int i = 0; i < childCount; i++) {
View childView = getChildAt(i);
childView.measure(80, 80);
}
} </span></strong>
通過重寫onMeasure方法不但可以為ViewGroup指定大小,還可以通過遍歷為每一個子View指定大小,在自定義ViewGroup中添加上面代碼為ViewGroup中的每一個子View分配了顯示的寬高。
下面我們讓子View動起來吧,添加代碼如下:
<strong><span style="font-family:FangSong_GB2312;font-size:14px;">public boolean onTouchEvent(MotionEvent ev) {
final float y = ev.getY();
switch(ev.getAction()) {
case MotionEvent.ACTION_DOWN:
mLastMotionY = y;
break;
case MotionEvent.ACTION_MOVE:
int detaY = (int)(mLastMotionY - y);
mLastMotionY = y;
scrollBy(0, detaY);
break;
case MotionEvent.ACTION_UP:
break;
}
return true;
}</span></strong>
在上面用到了一個scrollBy方法,打開官方API可以看到View類有如下兩個方法:
這兩個函數貌似都是移動視圖的,那麼它們有什麼區別呢?帶著這個疑問我們向下看
首先 ,我們必須明白在Android View視圖是沒有邊界的,Canvas是沒有邊界的,只不過我們通過繪制特定的View時對 Canvas對象進行了一定的操作,例如 : translate(平移)、clipRect(剪切)等,以便達到我們的對該Canvas對象繪制的要求 ,我們可以將這種無邊界的視圖稱為“視圖坐標”-----它不受物理屏幕限制。通常我們所理解的一個Layout布局文件只是該視圖的顯示區域,超過了這個顯示區域將不能顯示到父視圖的區域中 ,對應的,我們可以將這種有邊界的視圖稱為“布局坐標”------ 父視圖給子視圖分配的布局(layout)大小。而且, 一個視圖的在屏幕的起始坐標位於視圖坐標起始處,如下圖所示。
由於布局坐標只能顯示特定的一塊內容,所以我們只有移動布局坐標的坐標原點就可以將視圖坐標的任何位置顯示出來。
<strong><span style="font-family:FangSong_GB2312;font-size:14px;"><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:background="#888888">
<TextView
android:id="@+id/txt"
android:layout_width="300dip"
android:layout_height="120dip"
android:background="#cccccc"
android:text="textview" />
<Button
android:id="@+id/btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="button" />
</LinearLayout></span></strong>