天天看點

Android View嵌套和事件傳遞手稿

隻是以寫這樣一個專題手稿,是平時在群裡很多群友經常問到當動态添加View,形成嵌套View,視覺效果做出來了,但是事件如何傳遞了呢,特别是View有層疊的效果,那麼事件又如何傳遞和處理,以及View如何在視窗中如何移動處理.

是以大緻寫一個專題手稿大緻介紹一下,還望不足之處希望不吝指正和批評,非常感謝.

工程下載下傳連結:http://pan.baidu.com/s/1i3M6ntj

首先的思路如下:

Android View嵌套和事件傳遞手稿
Android View嵌套和事件傳遞手稿

我先做一個實作如上圖所示的View嵌套層級的一個android 工程,步驟如下:

由于我每一次手稿專題都會用到一個行動代号,這裡的代号是 : Durian

<1> : 建立一個android工程如下:

Android View嵌套和事件傳遞手稿
Android View嵌套和事件傳遞手稿

<2> : 代碼如下:

/**  
* @Title: DurianSubFrameLayout.java
* @Package com.durian.view
* @Description: TODO
* @author zhibao.liu from durian organization
* @date 2015-12-22 下午02:47:13
* @version V1.0  
*/
package com.durian.viewgroup;

import android.app.Activity;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.view.Window;
import android.widget.Toast;
/**
 * @ClassName: DurianSubFrameLayout
 * @Description: TODO
 * @author zhibao.liu Freelancer
 * @email [email protected]
 * @date 2015-12-22 下午02:47:13
 *
 */
public class DurianMainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.durian_main);

        DisplayMetrics dm = new DisplayMetrics();

        getWindowManager().getDefaultDisplay().getMetrics(dm);

        Toast.makeText(DurianMainActivity.this,
                "heigth : " + dm.heightPixels + "width : " + dm.widthPixels,
                Toast.LENGTH_SHORT).show();
        //1824 1200
    }
}
           

這裡面我運作的機器是Nexus 平闆,而且重要在是螢幕的大小尺寸,是以下面設定尺寸都是随手設定的.

/**  
 * @Title: DurianViewGroup.java
 * @Package com.durian.view
 * @Description: TODO
 * @author zhibao.liu from durian organization
 * @date 2015-12-21 下午04:06:28
 * @version V1.0  
 */
package com.durian.view;

import android.content.Context;
import android.graphics.Color;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;

public class DurianViewGroup extends ViewGroup {

    private final static String TAG = "DurianViewGroup";

    private DurianLinearLayout mDurianLinearLayout;
    private DurianFrameLayout mDurianFrameLayout;

    private int mDisplayWidth = 1200;
    private int mDisplayHeight = 1824;

    private Button mButton;

    public DurianViewGroup(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        // TODO Auto-generated constructor stub
        initView(context);
    }

    public DurianViewGroup(Context context, AttributeSet attrs) {
        super(context, attrs);
        // TODO Auto-generated constructor stub
        initView(context);
    }

    public DurianViewGroup(Context context) {
        super(context);
        // TODO Auto-generated constructor stub
        initView(context);
    }

    private void initView(Context context) {

        Log.i(TAG, "mDisplayWidth : " + mDisplayWidth + " mDisplayHeight : "
                + mDisplayHeight);

        mDurianFrameLayout = new DurianFrameLayout(context);
        addView(mDurianFrameLayout);

        mButton = new Button(context);
        mButton.setText("Top Button");
        addView(mButton);

        setBackgroundColor(Color.BLUE);

    }

    /*
     * (non-Javadoc)
     * 
     * @see android.view.ViewGroup#onLayout(boolean, int, int, int, int)
     */
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        // TODO Auto-generated method stub
        for (int i = 0; i < getChildCount(); i++) {
            Log.i(TAG, "child : " + i);

            View child = getChildAt(i);
            if (child instanceof Button) {
                child.layout(mDisplayWidth / 2 - 250, mDisplayHeight / 2 + 500,
                        mDisplayWidth / 2 + 250, mDisplayHeight / 2 + 750);
            } else {
                child.layout(0, 0, mDisplayWidth, mDisplayHeight / 2 + 500);
            }

        }

    }

    @Override
    public boolean dispatchKeyEvent(KeyEvent event) {
        // TODO Auto-generated method stub
        return super.dispatchKeyEvent(event);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // TODO Auto-generated method stub
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        setMeasuredDimension(mDisplayWidth, mDisplayHeight);

    }

}
           

上面是頂層容器繼承ViewGroup的.

/**  
 * @Title: DurianFrameLayout.java
 * @Package com.durian.view
 * @Description: TODO
 * @author zhibao.liu from durian organization
 * @date 2015-12-22 上午09:17:11
 * @version V1.0  
 */
package com.durian.view;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.FrameLayout;

public class DurianFrameLayout extends FrameLayout {

    private final static String TAG = "DurianFrameLayout";
    private DurianLinearLayout mDurianLinearLayout;
    private DurianSubFrameLayout mDurianSubFrameLayout;

    private int mDisplayWidth = 1200;
    private int mDisplayHeight = 1824;

    private Button mButton;

    public DurianFrameLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        // TODO Auto-generated constructor stub
        initView(context);
    }

    public DurianFrameLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        // TODO Auto-generated constructor stub
        initView(context);
    }

    public DurianFrameLayout(Context context) {
        super(context);
        // TODO Auto-generated constructor stub
        initView(context);
    }

    private void initView(Context context) {

        mDurianLinearLayout = new DurianLinearLayout(context);
        addView(mDurianLinearLayout);

        mDurianSubFrameLayout = new DurianSubFrameLayout(context);
        addView(mDurianSubFrameLayout);

        mButton = new Button(context);
        mButton.setText("Zero FrameLayout Button");
        addView(mButton);

    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right,
            int bottom) {
        // TODO Auto-generated method stub
        super.onLayout(changed, left, top, right, bottom);
        for (int i = 0; i < getChildCount(); i++) {

            Log.i(TAG, "*** count i : " + i);
            View child = getChildAt(i);

            if (child instanceof Button) {
                child.layout(mDisplayWidth/2-250, (i + 0) * mDisplayHeight / 4, mDisplayWidth/2+250, (i + 1) * mDisplayHeight / 4);
            } else {
                child.layout(0, i * (mDisplayHeight / 4), mDisplayWidth,
                        (i + 1) * mDisplayHeight / 4);
            }

        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // TODO Auto-generated method stub
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        setMeasuredDimension(mDisplayWidth, mDisplayHeight / 2+500);

    }

}
           
/**  
 * @Title: DurianLinearLayout.java
 * @Package com.durian.view
 * @Description: TODO
 * @author zhibao.liu from durian organization
 * @date 2015-12-21 下午05:27:56
 * @version V1.0  
 */
package com.durian.view;

import android.content.Context;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.LinearLayout;

import com.durian.viewgroup.R;

public class DurianLinearLayout extends LinearLayout {

    private final static String TAG = "DurianLinearLayout";

    private Button mLinearButton;

    private int mDisplayWidth = 1200;
    private int mDisplayHeight = 1824;

    public DurianLinearLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        // TODO Auto-generated constructor stub
    }

    public DurianLinearLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        // TODO Auto-generated constructor stub
    }

    public DurianLinearLayout(Context context) {
        super(context);
        // TODO Auto-generated constructor stub
        initView(context);
    }

    private void initView(Context context) {

        Log.i(TAG, "mDisplayWidth : " + mDisplayWidth + " mDisplayHeight : "
                + mDisplayHeight);

        mLinearButton = new Button(context);
        mLinearButton.setText("First Linear Button");
        addView(mLinearButton);

        setBackgroundDrawable(context.getResources().getDrawable(
                R.drawable.layout_backgroud));

    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        // TODO Auto-generated method stub
        super.onLayout(changed, l, t, r, b);

        for (int i = 0; i < getChildCount(); i++) {

            Log.i(TAG, "*** count i : " + i);
            View child = getChildAt(i);
            child.layout(mDisplayWidth / 2 - 200, 0, mDisplayWidth / 2 + 200,
                    120);

        }

    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // TODO Auto-generated method stub
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        setMeasuredDimension(mDisplayWidth, mDisplayHeight / 2);
    }

}
           
/**  
* @Title: DurianSubFrameLayout.java
* @Package com.durian.view
* @Description: TODO
* @author zhibao.liu from durian organization
* @date 2015-12-22 下午02:47:13
* @version V1.0  
*/
package com.durian.view;

import com.durian.viewgroup.R;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.FrameLayout;

/**
 * @ClassName: DurianSubFrameLayout
 * @Description: TODO
 * @author zhibao.liu Freelancer
 * @email [email protected]
 * @date 2015-12-22 下午02:47:13
 *
 */
public class DurianSubFrameLayout extends FrameLayout {

    private final static String TAG="DurianSubFrameLayout";
    private int mDisplayWidth=1200;
    private int mDisplayHeight=1824;
    
    private Button mButton;
    
    private DurianSubLinearLayout mDurianSubLinearLayout;
    
    public DurianSubFrameLayout(Context context, AttributeSet attrs,
            int defStyle) {
        super(context, attrs, defStyle);
        // TODO Auto-generated constructor stub
        initView(context);
    }

    public DurianSubFrameLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        // TODO Auto-generated constructor stub
        initView(context);
    }

    public DurianSubFrameLayout(Context context) {
        super(context);
        // TODO Auto-generated constructor stub
        initView(context);
    }

    private void initView(Context context){
        
        mButton=new Button(context);
        mButton.setText("Second SubFrameLayout Button");
        addView(mButton);
        
        mDurianSubLinearLayout=new DurianSubLinearLayout(context);
        addView(mDurianSubLinearLayout);
        
        setBackgroundDrawable(context.getResources().getDrawable(
                R.drawable.alpha_background));
        
        
    }
    
    @Override
    protected void onLayout(boolean changed, int left, int top, int right,
            int bottom) {
        // TODO Auto-generated method stub
        super.onLayout(changed, left, top, right, bottom);
        for (int i = 0; i < getChildCount(); i++) {

            Log.i(TAG, "*** count i : " + i);
            View child = getChildAt(i);
            
            if( child instanceof Button){
                child.layout(mDisplayWidth/2-250, i*mDisplayHeight /12, mDisplayWidth/2+250, (i+1)*mDisplayHeight /12);
            }else{
                child.layout(0, i*mDisplayHeight /12, mDisplayWidth, (i+1)*mDisplayHeight /6);
            }            
            
            

        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // TODO Auto-generated method stub
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        setMeasuredDimension(mDisplayWidth, mDisplayHeight/4);
    }

}
           
/**  
* @Title: DurianSubLinearLayout.java
* @Package com.durian.view
* @Description: TODO
* @author zhibao.liu from durian organization
* @date 2015-12-22 下午03:11:33
* @version V1.0  
*/
package com.durian.view;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;

/**
 * @ClassName: DurianSubLinearLayout
 * @Description: TODO
 * @author zhibao.liu Freelancer
 * @email [email protected]
 * @date 2015-12-22 下午03:11:33
 *
 */
public class DurianSubLinearLayout extends LinearLayout {

    private final static String TAG="DurianSubLinearLayout";
    private int mDisplayWidth=1200;
    private int mDisplayHeight=1824;
    
    private Button mButton;
    
    public DurianSubLinearLayout(Context context, AttributeSet attrs,
            int defStyle) {
        super(context, attrs, defStyle);
        // TODO Auto-generated constructor stub
        initView(context);
    }

    public DurianSubLinearLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        // TODO Auto-generated constructor stub
        initView(context);
    }

    public DurianSubLinearLayout(Context context) {
        super(context);
        // TODO Auto-generated constructor stub
        initView(context);
    }

    private void initView(Context context){
        
        mButton=new Button(context);
        mButton.setText("Third Sublinear button");
        addView(mButton);
        
    }
    
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        // TODO Auto-generated method stub
        super.onLayout(changed, l, t, r, b);
        for (int i = 0; i < getChildCount(); i++) {

            Log.i(TAG, "*** count i : " + i);
            View child = getChildAt(i);
            child.layout(mDisplayWidth/2-250, 100, mDisplayWidth/2+250, mDisplayHeight /12+100);

        }
        
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // TODO Auto-generated method stub
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        setMeasuredDimension(mDisplayWidth, mDisplayHeight/4);
    }

}
           

Activity的布局檔案如下:

<RelativeLayout 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"
    tools:context=".DurianMainActivity" >

    <com.durian.view.DurianViewGroup
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        >
        
    </com.durian.view.DurianViewGroup>

</RelativeLayout>
           

程式中涉及到的View的背景:alhpa_background.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
    
    <stroke
        android:width="2dip"
        android:color="#ffff00"/>

</shape>
           

layout_backgrond.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
    
    <solid 
        android:color="#ffffffff"/>
    <stroke
        android:width="2dip"
        android:color="#ff00ff"/>

</shape>
           

其他的基本上都還好, 運作程式如下:

Android View嵌套和事件傳遞手稿
Android View嵌套和事件傳遞手稿

做出上面的程式,主要涉及到兩個需要實作的方法:

@Override
    protected void onLayout(boolean changed, int left, int top, int right,
            int bottom)
           
@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
           

每一個 View在建構時,都會執行onLayout(...)這個方法,在這個方法裡面,通過:

for (int i = 0; i < getChildCount(); i++) {

            Log.i(TAG, "*** count i : " + i);
            View child = getChildAt(i);

            if (child instanceof Button) {
                child.layout(mDisplayWidth / 2 - 250, i * mDisplayHeight / 12,
                        mDisplayWidth / 2 + 250, (i + 1) * mDisplayHeight / 12);
            } else {
                child.layout(0, i * mDisplayHeight / 12, mDisplayWidth, (i + 1)
                        * mDisplayHeight / 6);
            }

        }
           

可以獲得這個容器中所有子View的對象,并且通過view.layout重新确定在這個容器中的位置,同時需要注意,view.layout(....)四個參數是相對直接父容器而言的,即相對的,和android顯示螢幕的尺寸有關系,但是沒有直接的關系.

至于onMeasure(...)這個方式,在我的程式裡面直接設定參量進去了,但是實際上面需要做一下變換:

setMeasuredDimension(mDisplayWidth, mDisplayHeight / 4);
           

一般情況可以如下:

//how to get actural this container size , please read following :
        int specSize_Widht = MeasureSpec.getSize(widthMeasureSpec);  
        int specSize_Heigth = MeasureSpec.getSize(heightMeasureSpec);
           

獲得到的specSize_Width和specSize_Height的尺寸實際仍然需要參考上一層父容器中onlayout中指定的尺寸大小,最終能夠顯示在螢幕,最終是由父容器中的Onlayout中配置設定的尺寸大小決定的.

既然上面給出了那麼層級的按鈕,可想而知,是用來處理事件的,下一篇繼續.

代碼參考:

android系統源代碼 :

View.java,以及ViewRoot.java等部分.