您的位置:首页 > 移动开发 > Android开发

Android View嵌套和事件传递手稿

2015-12-22 16:36 573 查看
只所以写这样一个专题手稿,是平时在群里很多群友经常问到当动态添加View,形成嵌套View,视觉效果做出来了,但是事件如何传递了呢,特别是View有层叠的效果,那么事件又如何传递和处理,以及View如何在视窗中如何移动处理.

所以大致写一个专题手稿大致介绍一下,还望不足之处希望不吝指正和批评,非常感谢.

工程下载链接:http://pan.baidu.com/s/1i3M6ntj

首先的思路如下:



我先做一个实现如上图所示的View嵌套层级的一个android 工程,步骤如下:

由于我每一次手稿专题都会用到一个行动代号,这里的代号是 : Durian

<1> : 新建一个android工程如下:



<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 warden_sprite@foxmail.com
* @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 warden_sprite@foxmail.com
* @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 warden_sprite@foxmail.com
* @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>

其他的基本上都还好, 运行程序如下:



做出上面的程序,主要涉及到两个需要实现的方法:

@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等部分.



内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: