本章內(nèi)容預(yù)覽:
3.1 Android控件架構(gòu)
3.2 View的測(cè)量
3.3 View的繪制
3.4 ViewGroup的測(cè)量
3.5 ViewGroup的繪制
3.6 自定義View
??????3.6.1 對(duì)現(xiàn)有控件進(jìn)行拓展
??????3.6.2 創(chuàng)建復(fù)合控件
??????3.6.2 重寫View來實(shí)現(xiàn)全新的控件
3.7 自定義ViewGroup
3.8 事件攔截機(jī)制分析
正文
3.1 Android控件架構(gòu)
Android的每個(gè)控件都是占一塊矩形的區(qū)域,大致的分兩類,View和ViewGroup,ViewGroup控件作為父控件可以包含多個(gè)View控件,并管理其包含的View控件整個(gè)界面上的控件形成了一個(gè)樹形結(jié)構(gòu),也就是我們常說的控件樹,上層控件負(fù)責(zé)下層控件的測(cè)量和繪制,并且傳遞交互事件,通過findviewbyid()這個(gè)方法來獲取,其實(shí)就是遍歷查找,在樹形圖的頂部都有一個(gè)ViewParent對(duì)象,這就是控制核心,所有的交互管理事件都是由它統(tǒng)一調(diào)度和分配,從而進(jìn)行整個(gè)視圖的控制


我們可以看到,每個(gè)activity都有一個(gè)window對(duì)象,在Android中,window對(duì)象通常由一個(gè)phonewindow去實(shí)現(xiàn)的,phonewindow將一個(gè)DecorView設(shè)置為整個(gè)窗口的根View,DecorView作為窗口界面的頂層視圖,封裝了一些通用的方法,可以說,DecorView將要顯示的內(nèi)容都給了phonewindow,這里面所有的View監(jiān)聽,都是通過winsowmanagerService來接收的,通過相應(yīng)的回調(diào)來OnClicListener,在顯示上,他將屏幕分成了兩部分,一個(gè)title一個(gè)content,看到這里,大家應(yīng)該能看到一個(gè)熟悉的界面ContentView,它是一個(gè)ID為content分framelayout,activity_main.xml就是設(shè)置在這個(gè)framelayout里面

在代碼中當(dāng)程序onCreate()時(shí),也就設(shè)置了layout,執(zhí)行完后,activitymanagerservice會(huì)直接調(diào)用onResume,這個(gè)時(shí)候系統(tǒng)會(huì)把整個(gè)DecorView添加到phonewindow,從而最終完成界面的繪制。
3.2 View的測(cè)量
Android系統(tǒng)在繪制View前,必須先對(duì)View進(jìn)行測(cè)量,即告訴系統(tǒng)該畫一個(gè)多大的View。這個(gè)過程在onMeasure( )中進(jìn)行
MeasureSpec:
幫助測(cè)量View
MeasureSpec是一個(gè)32位的int值,高2位為測(cè)量的模式,低30位為測(cè)量的大小.
測(cè)量的模式:
- EXACTLY:精確值模式
當(dāng)layout_width或layout_height屬性值為特定值,或者為match_parent,則系統(tǒng)使用的是EXACTY- AT_MOST:最大值模式
當(dāng)layout_width或layout_height屬性值為wrap_content時(shí),此時(shí)控件的尺寸不超過父控件允許的最大尺寸- UNSPECIFIED:
不指定大小,View想要多大就多大,通常情況下在繪制自定義View時(shí)才使用。
注意: View默認(rèn)的onMeasure()只支持EXACTLY模式,(具體值,match_parent),如果自定義View想要支持wrap_content屬性,則應(yīng)該重新onMeasure()來指定wrap_content時(shí)的大小。
如何進(jìn)行View的測(cè)量
- 重寫onMeasure()方法,把測(cè)量到的寬高值作為參數(shù)傳遞給setMeasuredDimension()
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(measureWidth(widthMeasureSpec),measureHeight(heightMeasureSpec));
}
如何自定義測(cè)量值
int specMode=MeasureSpec.getMode(measureSpec);
int specSize=MeasureSpec.getSize(measureSpec);
當(dāng)specMode為EXACTLY,直接使用指定的specSize,當(dāng)specMode為其它兩種模式,需要給它默認(rèn)的大小。特別當(dāng)指定wrap_content屬性,即AT_MOST模式,則需要取出我們指定的大小和sepcSize中最小的一個(gè)來作為最終的觀測(cè)值。
模板代碼:
private int measureWidth(int measureSpec) {
int result=0;
int specMode=MeasureSpec.getMode(measureSpec);
int specSize=MeasureSpec.getSize(measureSpec);
if(specMode==MeasureSpec.EXACTLY){
result=specSize;
Log.e("measureWidth","MeasureSpec.EXACTLY");
}else {
result=200;
if(specMode==MeasureSpec.AT_MOST){
result=Math.min(200,specSize);
Log.e("measureWidth","MeasureSpec.AT_MOST");
}
}
return result;
}
運(yùn)行結(jié)果展示:













3.3 View的繪制
重寫onDraw()方法
3.4 ViewGroup的測(cè)量
當(dāng)ViewGroup的大小為wrap_content,ViewGroup需要對(duì)所有子View進(jìn)行遍歷(從而調(diào)用子View的onMeasure()方法來獲得每個(gè)子View的測(cè)量值),以便獲得所有子View的大小,從而確定自己的大小。
而在其他模式下則通過具體的指定值來確定自身大小。
重寫onLayout()擺放子View的位置
3.5 ViewGroup的繪制
ViewGroup在一般情況下是不會(huì)繪制的,因?yàn)樗旧頉]有需要繪制的東西,如果不是指定ViewGroup的背景顏色,他連onDraw()都不會(huì)調(diào)用,但是ViewGroup會(huì)使用dispatchDraw()來繪制其他子View,其過程同樣是遍歷所喲普的子View,并調(diào)用子View的繪制方法來完成繪制的
3.6 自定義View
View中重要的回調(diào)方法:
- onFinishInflate():從XML加載組件后回調(diào)
- onSizeChanged():組件大小改變時(shí)回調(diào)
- onMeasure():回調(diào)該方法來進(jìn)行測(cè)量
- onLayout():回調(diào)該方法來確定顯示的位置
- onTouchEvent():監(jiān)聽到觸摸事件時(shí)回調(diào)
實(shí)現(xiàn)自定義View的三種方式:
- 對(duì)現(xiàn)有控件進(jìn)行拓展
- 通過組合實(shí)現(xiàn)新的控件
- 重寫View實(shí)現(xiàn)新的控件
3.6.1 對(duì)現(xiàn)有控件進(jìn)行拓展
一般通過重寫onDraw()方法
@Override
protected void onDraw(Canvas canvas) {
//在回調(diào)父類之前,實(shí)現(xiàn)自己的邏輯,對(duì)textview來說就是繪制文本內(nèi)容前
super.onDraw(canvas);
//在回調(diào)父類之后,實(shí)現(xiàn)自己的邏輯,對(duì)textview來說就是繪制文本內(nèi)容后
}
3.6.2 創(chuàng)建復(fù)合控件
創(chuàng)建一個(gè)復(fù)合控件可以很好的創(chuàng)建出具有重要功能的控件集合,這種方式經(jīng)常需要繼承一個(gè)合適的ViewGroup,再給他添加指定功能的控件,從而組成一個(gè)新的合適的控件,通過這種方式創(chuàng)建的控件,我們一般都會(huì)給他指定的一些屬性,讓他具有更強(qiáng)的擴(kuò)展性,下面就以一個(gè)TopBar為例子,講解如何創(chuàng)建復(fù)合控件
3.6.2.1 定義屬性
res --- values --- attrs.xml
<declare-styleable name="TopBar">
<attr name="title" format="string" />
<attr name="titleTextSize" format="dimension" />
<attr name="titleTextColor" format="color" />
<attr name="leftTextColor" format="color" />
<attr name="leftBackground" format="reference|color" />
<attr name="leftText" format="string" />
<attr name="rightTextColor" format="color" />
<attr name="rightBackground" format="reference|color" />
<attr name="rightText" format="string" />
</declare-styleable>
3.6.2.2 獲取屬性值
// 通過這個(gè)方法,將你在atts.xml中定義的declare-styleable
// 的所有屬性的值存儲(chǔ)到TypedArray中
TypedArray ta = context.obtainStyledAttributes(attrs,
R.styleable.TopBar);
// 從TypedArray中取出對(duì)應(yīng)的值來為要設(shè)置的屬性賦值
mLeftTextColor = ta.getColor(
R.styleable.TopBar_leftTextColor, 0);
3.6.2.3 組合控件
mLeftButton = new Button(context);
mRightButton = new Button(context);
mTitleView = new TextView(context);
// 為創(chuàng)建的組件元素賦值
// 值就來源于我們?cè)谝玫膞ml文件中給對(duì)應(yīng)屬性的賦值
mLeftButton.setTextColor(mLeftTextColor);
mLeftButton.setBackground(mLeftBackground);
mLeftButton.setText(mLeftText);
mRightButton.setTextColor(mRightTextColor);
mRightButton.setBackground(mRightBackground);
mRightButton.setText(mRightText);
mTitleView.setText(mTitle);
mTitleView.setTextColor(mTitleTextColor);
mTitleView.setTextSize(mTitleTextSize);
mTitleView.setGravity(Gravity.CENTER);
// 為組件元素設(shè)置相應(yīng)的布局元素
mLeftParams = new LayoutParams(
LayoutParams.WRAP_CONTENT,
LayoutParams.MATCH_PARENT);
mLeftParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT, TRUE);
// 添加到ViewGroup
addView(mLeftButton, mLeftParams);
mRightParams = new LayoutParams(
LayoutParams.WRAP_CONTENT,
LayoutParams.MATCH_PARENT);
mRightParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT, TRUE);
addView(mRightButton, mRightParams);
mTitlepParams = new LayoutParams(
LayoutParams.WRAP_CONTENT,
LayoutParams.MATCH_PARENT);
mTitlepParams.addRule(RelativeLayout.CENTER_IN_PARENT, TRUE);
addView(mTitleView, mTitlepParams);
3.6.2.4 定義接口
// 接口對(duì)象,實(shí)現(xiàn)回調(diào)機(jī)制,在回調(diào)方法中
// 通過映射的接口對(duì)象調(diào)用接口中的方法
// 而不用去考慮如何實(shí)現(xiàn),具體的實(shí)現(xiàn)由調(diào)用者去創(chuàng)建
public interface topbarClickListener {
// 左按鈕點(diǎn)擊事件
void leftClick();
// 右按鈕點(diǎn)擊事件
void rightClick();
}
3.6.2.5 暴露接口給調(diào)用者 實(shí)現(xiàn)接口回調(diào) 提供public方法設(shè)置
// 暴露一個(gè)方法給調(diào)用者來注冊(cè)接口回調(diào)
// 通過接口來獲得回調(diào)者對(duì)接口方法的實(shí)現(xiàn)
public void setOnTopbarClickListener(topbarClickListener mListener) {
this.mListener = mListener;
}
3.6.2.6 引用UI模板
在布局文件中引入命名空間:
xmlns:custom="http://schemas.android.com/apk/res-auto"
<com.imooc.systemwidget.TopBar
android:id="@+id/topBar"
android:layout_width="match_parent"
android:layout_height="40dp"
custom:leftBackground="@drawable/blue_button"
custom:leftText="Back"
custom:leftTextColor="#FFFFFF"
custom:rightBackground="@drawable/blue_button"
custom:rightText="More"
custom:rightTextColor="#FFFFFF"
custom:title="自定義標(biāo)題"
custom:titleTextColor="#123412"
custom:titleTextSize="10sp"/>
public class TopBar extends RelativeLayout {
// 包含topbar上的元素:左按鈕、右按鈕、標(biāo)題
private Button mLeftButton, mRightButton;
private TextView mTitleView;
// 布局屬性,用來控制組件元素在ViewGroup中的位置
private LayoutParams mLeftParams, mTitlepParams, mRightParams;
// 左按鈕的屬性值,即我們?cè)赼tts.xml文件中定義的屬性
private int mLeftTextColor;
private Drawable mLeftBackground;
private String mLeftText;
// 右按鈕的屬性值,即我們?cè)赼tts.xml文件中定義的屬性
private int mRightTextColor;
private Drawable mRightBackground;
private String mRightText;
// 標(biāo)題的屬性值,即我們?cè)赼tts.xml文件中定義的屬性
private float mTitleTextSize;
private int mTitleTextColor;
private String mTitle;
// 映射傳入的接口對(duì)象
private topbarClickListener mListener;
public TopBar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public TopBar(Context context) {
super(context);
}
public TopBar(Context context, AttributeSet attrs) {
super(context, attrs);
// 設(shè)置topbar的背景
setBackgroundColor(0xFFF59563);
// 通過這個(gè)方法,將你在atts.xml中定義的declare-styleable
// 的所有屬性的值存儲(chǔ)到TypedArray中
TypedArray ta = context.obtainStyledAttributes(attrs,
R.styleable.TopBar);
// 從TypedArray中取出對(duì)應(yīng)的值來為要設(shè)置的屬性賦值
mLeftTextColor = ta.getColor(
R.styleable.TopBar_leftTextColor, 0);
mLeftBackground = ta.getDrawable(
R.styleable.TopBar_leftBackground);
mLeftText = ta.getString(R.styleable.TopBar_leftText);
mRightTextColor = ta.getColor(
R.styleable.TopBar_rightTextColor, 0);
mRightBackground = ta.getDrawable(
R.styleable.TopBar_rightBackground);
mRightText = ta.getString(R.styleable.TopBar_rightText);
mTitleTextSize = ta.getDimension(
R.styleable.TopBar_titleTextSize, 10);
mTitleTextColor = ta.getColor(
R.styleable.TopBar_titleTextColor, 0);
mTitle = ta.getString(R.styleable.TopBar_title);
// 獲取完TypedArray的值后,一般要調(diào)用
// recyle方法來避免重新創(chuàng)建的時(shí)候的錯(cuò)誤
ta.recycle();
mLeftButton = new Button(context);
mRightButton = new Button(context);
mTitleView = new TextView(context);
// 為創(chuàng)建的組件元素賦值
// 值就來源于我們?cè)谝玫膞ml文件中給對(duì)應(yīng)屬性的賦值
mLeftButton.setTextColor(mLeftTextColor);
mLeftButton.setBackground(mLeftBackground);
mLeftButton.setText(mLeftText);
mRightButton.setTextColor(mRightTextColor);
mRightButton.setBackground(mRightBackground);
mRightButton.setText(mRightText);
mTitleView.setText(mTitle);
mTitleView.setTextColor(mTitleTextColor);
mTitleView.setTextSize(mTitleTextSize);
mTitleView.setGravity(Gravity.CENTER);
// 為組件元素設(shè)置相應(yīng)的布局元素
mLeftParams = new LayoutParams(
LayoutParams.WRAP_CONTENT,
LayoutParams.MATCH_PARENT);
mLeftParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT, TRUE);
// 添加到ViewGroup
addView(mLeftButton, mLeftParams);
mRightParams = new LayoutParams(
LayoutParams.WRAP_CONTENT,
LayoutParams.MATCH_PARENT);
mRightParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT, TRUE);
addView(mRightButton, mRightParams);
mTitlepParams = new LayoutParams(
LayoutParams.WRAP_CONTENT,
LayoutParams.MATCH_PARENT);
mTitlepParams.addRule(RelativeLayout.CENTER_IN_PARENT, TRUE);
addView(mTitleView, mTitlepParams);
// 按鈕的點(diǎn)擊事件,不需要具體的實(shí)現(xiàn),
// 只需調(diào)用接口的方法,回調(diào)的時(shí)候,會(huì)有具體的實(shí)現(xiàn)
mRightButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mListener.rightClick();
}
});
mLeftButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mListener.leftClick();
}
});
}
// 暴露一個(gè)方法給調(diào)用者來注冊(cè)接口回調(diào)
// 通過接口來獲得回調(diào)者對(duì)接口方法的實(shí)現(xiàn)
public void setOnTopbarClickListener(topbarClickListener mListener) {
this.mListener = mListener;
}
/**
* 設(shè)置按鈕的顯示與否 通過id區(qū)分按鈕,flag區(qū)分是否顯示
*
* @param id id
* @param flag 是否顯示
*/
public void setButtonVisable(int id, boolean flag) {
if (flag) {
if (id == 0) {
mLeftButton.setVisibility(View.VISIBLE);
} else {
mRightButton.setVisibility(View.VISIBLE);
}
} else {
if (id == 0) {
mLeftButton.setVisibility(View.GONE);
} else {
mRightButton.setVisibility(View.GONE);
}
}
}
// 接口對(duì)象,實(shí)現(xiàn)回調(diào)機(jī)制,在回調(diào)方法中
// 通過映射的接口對(duì)象調(diào)用接口中的方法
// 而不用去考慮如何實(shí)現(xiàn),具體的實(shí)現(xiàn)由調(diào)用者去創(chuàng)建
public interface topbarClickListener {
// 左按鈕點(diǎn)擊事件
void leftClick();
// 右按鈕點(diǎn)擊事件
void rightClick();
}
3.6.3 重寫View實(shí)現(xiàn)全新的控件
弧線展示圖:

public class MyScrollView extends ViewGroup {
private int mScreenHeight;
private Scroller mScroller;
private int mLastY;
private int mStart;
private int mEnd;
public MyScrollView(Context context) {
super(context);
initView(context);
}
public MyScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
initView(context);
}
public MyScrollView(Context context, AttributeSet attrs,
int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView(context);
}
private void initView(Context context) {
WindowManager wm = (WindowManager) context.getSystemService(
Context.WINDOW_SERVICE);
DisplayMetrics dm = new DisplayMetrics();
wm.getDefaultDisplay().getMetrics(dm);
mScreenHeight = dm.heightPixels;
mScroller = new Scroller(context);
}
@Override
protected void onLayout(boolean changed,
int l, int t, int r, int b) {
int childCount = getChildCount();
// 設(shè)置ViewGroup的高度
MarginLayoutParams mlp = (MarginLayoutParams) getLayoutParams();
mlp.height = mScreenHeight * childCount;
setLayoutParams(mlp);
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
if (child.getVisibility() != View.GONE) {
child.layout(l, i * mScreenHeight,
r, (i + 1) * mScreenHeight);
}
}
}
@Override
protected void onMeasure(int widthMeasureSpec,
int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int count = getChildCount();
for (int i = 0; i < count; ++i) {
View childView = getChildAt(i);
measureChild(childView,
widthMeasureSpec, heightMeasureSpec);
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mLastY = y;
mStart = getScrollY();
break;
case MotionEvent.ACTION_MOVE:
if (!mScroller.isFinished()) {
mScroller.abortAnimation();
}
int dy = mLastY - y;
if (getScrollY() < 0) {
dy = 0;
}
if (getScrollY() > getHeight() - mScreenHeight) {
dy = 0;
}
scrollBy(0, dy);
mLastY = y;
break;
case MotionEvent.ACTION_UP:
mEnd = getScrollY();
int dScrollY = mEnd - mStart;
if (dScrollY > 0) {
if (dScrollY < mScreenHeight / 3) {
mScroller.startScroll(
0, getScrollY(),
0, -dScrollY);
} else {
mScroller.startScroll(
0, getScrollY(),
0, mScreenHeight - dScrollY);
}
} else {
if (-dScrollY < mScreenHeight / 3) {
mScroller.startScroll(
0, getScrollY(),
0, -dScrollY);
} else {
mScroller.startScroll(
0, getScrollY(),
0, -mScreenHeight - dScrollY);
}
}
break;
}
postInvalidate();
return true;
}
@Override
public void computeScroll() {
super.computeScroll();
if (mScroller.computeScrollOffset()) {
scrollTo(0, mScroller.getCurrY());
postInvalidate();
}
}
}
音頻條形圖

public class VolumeView extends View {
private int mWidth;
private int mRectWidth;
private int mRectHeight;
private Paint mPaint;
private int mRectCount;
private int offset = 5;
private double mRandom;
private LinearGradient mLinearGradient;
public VolumeView(Context context) {
super(context);
initView();
}
public VolumeView(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
}
public VolumeView(Context context, AttributeSet attrs,
int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView();
}
private void initView() {
mPaint = new Paint();
mPaint.setColor(Color.BLUE);
mPaint.setStyle(Paint.Style.FILL);
mRectCount = 12;
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mWidth = getWidth();
mRectHeight = getHeight();
mRectWidth = (int) (mWidth * 0.6 / mRectCount);
mLinearGradient = new LinearGradient(
0,
0,
mRectWidth,
mRectHeight,
Color.YELLOW,
Color.BLUE,
Shader.TileMode.CLAMP);
mPaint.setShader(mLinearGradient);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
for (int i = 0; i < mRectCount; i++) {
mRandom = Math.random();
float currentHeight = (float) (mRectHeight * mRandom);
canvas.drawRect(
(float) (mWidth * 0.4 / 2 + mRectWidth * i + offset),
currentHeight,
(float) (mWidth * 0.4 / 2 + mRectWidth * (i + 1)),
mRectHeight,
mPaint);
}
postInvalidateDelayed(300);
}
}
3.7 自定義ViewGroup
這個(gè)管理子View的管理者,我們來定義一下,通常我們自定義ViewGroup是需要onMeasure()來測(cè)量的,然后重寫onLayout()來確定位置,重寫onTouchEvent()來相應(yīng)事件.這里我們定義一個(gè)類似系統(tǒng)ScrollView的效果,首先,我們測(cè)量子View
@Override
protected void onMeasure(int widthMeasureSpec,
int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int count = getChildCount();
for (int i = 0; i < count; ++i) {
View childView = getChildAt(i);
measureChild(childView,
widthMeasureSpec, heightMeasureSpec);
}
}
接下來我們要對(duì)子View的位置計(jì)算,讓每一個(gè)View放置的時(shí)候都是全屏,這樣我們就滑動(dòng),我們這樣來設(shè)置ViewGroup的高度
@Override
protected void onLayout(boolean changed,
int l, int t, int r, int b) {
int childCount = getChildCount();
// 設(shè)置ViewGroup的高度
MarginLayoutParams mlp = (MarginLayoutParams) getLayoutParams();
mlp.height = mScreenHeight * childCount;
setLayoutParams(mlp);
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
if (child.getVisibility() != View.GONE) {
child.layout(l, i * mScreenHeight,
r, (i + 1) * mScreenHeight);
}
}
}
響應(yīng)事件、滑動(dòng)事件
@Override
public boolean onTouchEvent(MotionEvent event) {
int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mLastY = y;
mStart = getScrollY();
break;
case MotionEvent.ACTION_MOVE:
if (!mScroller.isFinished()) {
mScroller.abortAnimation();
}
int dy = mLastY - y;
if (getScrollY() < 0) {
dy = 0;
}
if (getScrollY() > getHeight() - mScreenHeight) {
dy = 0;
}
scrollBy(0, dy);
mLastY = y;
break;
case MotionEvent.ACTION_UP:
mEnd = getScrollY();
int dScrollY = mEnd - mStart;
if (dScrollY > 0) {
if (dScrollY < mScreenHeight / 3) {
mScroller.startScroll(
0, getScrollY(),
0, -dScrollY);
} else {
mScroller.startScroll(
0, getScrollY(),
0, mScreenHeight - dScrollY);
}
} else {
if (-dScrollY < mScreenHeight / 3) {
mScroller.startScroll(
0, getScrollY(),
0, -dScrollY);
} else {
mScroller.startScroll(
0, getScrollY(),
0, -mScreenHeight - dScrollY);
}
}
break;
}
postInvalidate();
return true;
}
最后~
@Override
public void computeScroll() {
super.computeScroll();
if (mScroller.computeScrollOffset()) {
scrollTo(0, mScroller.getCurrY());
postInvalidate();
}
}
3.8 事件攔截機(jī)制分析

1.點(diǎn)擊View:

2.ViewGroupA覺得任務(wù)很簡(jiǎn)單,自己處理就可以。在onInterceptTouchEvent()返回true

3.ViewGroupA不想做這個(gè)任務(wù),ViewGroupB想做。

4.View罷工了,不想做任務(wù)了,也不用向上級(jí)報(bào)告任務(wù),onTouchEvent直接返回true

5.View不返回true了,ViewGroupB的onTouchEvent也返回 true

大功告成?。?!下班!??!