精华内容
参与话题
问答
  • android studio的安装,史上最详细(超多图)!!

    万次阅读 多人点赞 2019-06-11 14:10:36
    android studio的安装,史上最详细!!欢迎前来观看,感觉有用就点波关注吧!custom 欢迎前来观看,感觉有用就点波关注吧! 1、首先下载Android studio安装包,可以从 http://www.android-studio.org/ 下载最新...

    android studio的安装,史上最详细!!

    欢迎前来观看,感觉有用就点波关注吧!

    1、首先下载Android studio安装包,可以从 http://www.android-studio.org/ 下载最新版本,这里采用3.5.2版本进行演示,安装包大小718 MB.
    2.
    在这里插入图片描述

    3.下载后进行安装
    将按照下列步骤进行:
    在这里插入图片描述
    在这里插入图片描述
    下面一步是安装路径的选择,个人建议内存大的就不要乱换了,小编这里就放在了
    D:\android\android studio
    D:\android\android studio
    D:\android\android studio
    文件夹自己新建就好
    (说三遍,图中有误!!!!)
    在这里插入图片描述
    在这里插入图片描述
    到此基础部分已经完成。

    4.下面一定要逐步完成,一步一步仔细操作:

    点选第二个
    在这里插入图片描述
    然后会有各种提示,下一步到这里

    安装选这个
    custom
    后续可以更改安装位置,否则,只能装在C盘
    在这里插入图片描述
    选择主题风格:(小编喜欢黑色siiiiiii…,你们自行选择)
    在这里插入图片描述
    下方安装路径如果需要更改的话
    一定要改为没有标点符号,(空格之类千万不要有,另外,中文不能出现!!!!!本文所有路径,不能出现中文!!!!新手按照我的去安装就好,就不需要费心改变了)
    勾选内容参考图中
    在这里插入图片描述
    内存的选择,我个人是16G的内存条啦,我就选了4096MB你们根据情况选择就好,最好不要低于2G(2048MB)
    在这里插入图片描述
    到此下一步,开始疯狂跑安装:
    在这里插入图片描述
    当然是选择完成啦!
    终于见到了曙光!!
    下图!
    在这里插入图片描述

    5.开始演示第一个hello world程序!
    点击上图的start a new balabala…
    演示为空白案例,不做其他模板展示!
    在这里插入图片描述
    项目名称,域名,语言,API等级,根据需要选择就好
    在这里插入图片描述
    继续!
    第一次安装会进行环境配置,会进行gradle
    (这里可能很多同胞会失败,原因是在远程下载的gradle被墙了,可以参考我的另一篇博客android studio的安装(补充篇gradle失败的问题)
    耐心等待就好

    分析完成之后。点击右上角的锤子,然后点击三角形
    在这里插入图片描述
    由于第一次安装,需要配置一个新的模拟器
    (不懂模拟器没事,按照步骤来就好哈哈哈)

    创建一个新虚拟设备
    在这里插入图片描述
    选择想要的模板类型,强烈建议图中的两个,其他的,,,好丑哈哈
    在这里插入图片描述
    继续选择图片素材
    需要下载1个G左右!
    下面按照图片进行操作即可!
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    完成!
    下面一步改名无关紧要直接完成即可!
    此时前面那个选择虚拟设备画面已经有了一个设备,进行勾选!

    在这里插入图片描述
    看见这个画面
    恭喜你,安卓旅程出发!
    在这里插入图片描述
    默认即为helloworld !第一个程序完成!
    不用任何操作!
    在这里插入图片描述

    展开全文
  • 掌握Android Studio环境搭建,虚拟设备配置,程序设计和程序调试方法。
  • Android 手把手教您自定义ViewGroup(一)

    万次阅读 多人点赞 2014-08-02 09:26:27
    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/38339817 , 本文出自:【张鸿洋的博客】...说白了,就是教大家如何自定义ViewGroup,如果你对自定义ViewGroup还不是很了解,或者正想学习如何自

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/38339817 , 本文出自:【张鸿洋的博客】

    最近由于工作的变动,导致的博客的更新计划有点被打乱,希望可以尽快脉动回来~

    今天给大家带来一篇自定义ViewGroup的教程,说白了,就是教大家如何自定义ViewGroup,如果你对自定义ViewGroup还不是很了解,或者正想学习如何自定义,那么你可以好好看看这篇博客。

    1、概述

    在写代码之前,我必须得问几个问题:

    1、ViewGroup的职责是啥?

    ViewGroup相当于一个放置View的容器,并且我们在写布局xml的时候,会告诉容器(凡是以layout为开头的属性,都是为用于告诉容器的),我们的宽度(layout_width)、高度(layout_height)、对齐方式(layout_gravity)等;当然还有margin等;于是乎,ViewGroup的职能为:给childView计算出建议的宽和高和测量模式 ;决定childView的位置;为什么只是建议的宽和高,而不是直接确定呢,别忘了childView宽和高可以设置为wrap_content,这样只有childView才能计算出自己的宽和高。

    2、View的职责是啥?

    View的职责,根据测量模式和ViewGroup给出的建议的宽和高,计算出自己的宽和高;同时还有个更重要的职责是:在ViewGroup为其指定的区域内绘制自己的形态。

    3、ViewGroup和LayoutParams之间的关系?

    大家可以回忆一下,当在LinearLayout中写childView的时候,可以写layout_gravity,layout_weight属性;在RelativeLayout中的childView有layout_centerInParent属性,却没有layout_gravity,layout_weight,这是为什么呢?这是因为每个ViewGroup需要指定一个LayoutParams,用于确定支持childView支持哪些属性,比如LinearLayout指定LinearLayout.LayoutParams等。如果大家去看LinearLayout的源码,会发现其内部定义了LinearLayout.LayoutParams,在此类中,你可以发现weight和gravity的身影。

    2、View的3种测量模式

    上面提到了ViewGroup会为childView指定测量模式,下面简单介绍下三种测量模式:

    EXACTLY:表示设置了精确的值,一般当childView设置其宽、高为精确值、match_parent时,ViewGroup会将其设置为EXACTLY;

    AT_MOST:表示子布局被限制在一个最大值内,一般当childView设置其宽、高为wrap_content时,ViewGroup会将其设置为AT_MOST;

    UNSPECIFIED:表示子布局想要多大就多大,一般出现在AadapterView的item的heightMode中、ScrollView的childView的heightMode中;此种模式比较少见。

    注:上面的每一行都有一个一般,意思上述不是绝对的,对于childView的mode的设置还会和ViewGroup的测量mode有一定的关系;当然了,这是第一篇自定义ViewGroup,而且绝大部分情况都是上面的规则,所以为了通俗易懂,暂不深入讨论其他内容。

    3、从API角度进行浅析

    上面叙述了ViewGroup和View的职责,下面从API角度进行浅析。

    View的根据ViewGroup传人的测量值和模式,对自己宽高进行确定(onMeasure中完成),然后在onDraw中完成对自己的绘制。

    ViewGroup需要给View传入view的测量值和模式(onMeasure中完成),而且对于此ViewGroup的父布局,自己也需要在onMeasure中完成对自己宽和高的确定。此外,需要在onLayout中完成对其childView的位置的指定。

    4、完整的例子

    需求:我们定义一个ViewGroup,内部可以传入0到4个childView,分别依次显示在左上角,右上角,左下角,右下角。

    1、决定该ViewGroup的LayoutParams

    对于我们这个例子,我们只需要ViewGroup能够支持margin即可,那么我们直接使用系统的MarginLayoutParams

    @Override
    	public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs)
    	{
    		return new MarginLayoutParams(getContext(), attrs);
    	}

    重写父类的该方法,返回MarginLayoutParams的实例,这样就为我们的ViewGroup指定了其LayoutParams为MarginLayoutParams。

    2、onMeasure

    在onMeasure中计算childView的测量值以及模式,以及设置自己的宽和高:

    /**
    	 * 计算所有ChildView的宽度和高度 然后根据ChildView的计算结果,设置自己的宽和高
    	 */
    	@Override
    	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
    	{
    		/**
    		 * 获得此ViewGroup上级容器为其推荐的宽和高,以及计算模式
    		 */
    		int widthMode = MeasureSpec.getMode(widthMeasureSpec);
    		int heightMode = MeasureSpec.getMode(heightMeasureSpec);
    		int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);
    		int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);
    
    
    		// 计算出所有的childView的宽和高
    		measureChildren(widthMeasureSpec, heightMeasureSpec);
    		/**
    		 * 记录如果是wrap_content是设置的宽和高
    		 */
    		int width = 0;
    		int height = 0;
    
    		int cCount = getChildCount();
    
    		int cWidth = 0;
    		int cHeight = 0;
    		MarginLayoutParams cParams = null;
    
    		// 用于计算左边两个childView的高度
    		int lHeight = 0;
    		// 用于计算右边两个childView的高度,最终高度取二者之间大值
    		int rHeight = 0;
    
    		// 用于计算上边两个childView的宽度
    		int tWidth = 0;
    		// 用于计算下面两个childiew的宽度,最终宽度取二者之间大值
    		int bWidth = 0;
    
    		/**
    		 * 根据childView计算的出的宽和高,以及设置的margin计算容器的宽和高,主要用于容器是warp_content时
    		 */
    		for (int i = 0; i < cCount; i++)
    		{
    			View childView = getChildAt(i);
    			cWidth = childView.getMeasuredWidth();
    			cHeight = childView.getMeasuredHeight();
    			cParams = (MarginLayoutParams) childView.getLayoutParams();
    
    			// 上面两个childView
    			if (i == 0 || i == 1)
    			{
    				tWidth += cWidth + cParams.leftMargin + cParams.rightMargin;
    			}
    
    			if (i == 2 || i == 3)
    			{
    				bWidth += cWidth + cParams.leftMargin + cParams.rightMargin;
    			}
    
    			if (i == 0 || i == 2)
    			{
    				lHeight += cHeight + cParams.topMargin + cParams.bottomMargin;
    			}
    
    			if (i == 1 || i == 3)
    			{
    				rHeight += cHeight + cParams.topMargin + cParams.bottomMargin;
    			}
    
    		}
    		
    		width = Math.max(tWidth, bWidth);
    		height = Math.max(lHeight, rHeight);
    
    		/**
    		 * 如果是wrap_content设置为我们计算的值
    		 * 否则:直接设置为父容器计算的值
    		 */
    		setMeasuredDimension((widthMode == MeasureSpec.EXACTLY) ? sizeWidth
    				: width, (heightMode == MeasureSpec.EXACTLY) ? sizeHeight
    				: height);
    	}

    10-14行,获取该ViewGroup父容器为其设置的计算模式和尺寸,大多情况下,只要不是wrap_content,父容器都能正确的计算其尺寸。所以我们自己需要计算如果设置为wrap_content时的宽和高,如何计算呢?那就是通过其childView的宽和高来进行计算。

    17行,通过ViewGroup的measureChildren方法为其所有的孩子设置宽和高,此行执行完成后,childView的宽和高都已经正确的计算过了

    43-71行,根据childView的宽和高,以及margin,计算ViewGroup在wrap_content时的宽和高。

    80-82行,如果宽高属性值为wrap_content,则设置为43-71行中计算的值,否则为其父容器传入的宽和高。

    3、onLayout对其所有childView进行定位(设置childView的绘制区域)

    // abstract method in viewgroup
    	@Override
    	protected void onLayout(boolean changed, int l, int t, int r, int b)
    	{
    		int cCount = getChildCount();
    		int cWidth = 0;
    		int cHeight = 0;
    		MarginLayoutParams cParams = null;
    		/**
    		 * 遍历所有childView根据其宽和高,以及margin进行布局
    		 */
    		for (int i = 0; i < cCount; i++)
    		{
    			View childView = getChildAt(i);
    			cWidth = childView.getMeasuredWidth();
    			cHeight = childView.getMeasuredHeight();
    			cParams = (MarginLayoutParams) childView.getLayoutParams();
    
    			int cl = 0, ct = 0, cr = 0, cb = 0;
    
    			switch (i)
    			{
    			case 0:
    				cl = cParams.leftMargin;
    				ct = cParams.topMargin;
    				break;
    			case 1:
    				cl = getWidth() - cWidth - cParams.leftMargin
    						- cParams.rightMargin;
    				ct = cParams.topMargin;
    
    				break;
    			case 2:
    				cl = cParams.leftMargin;
    				ct = getHeight() - cHeight - cParams.bottomMargin;
    				break;
    			case 3:
    				cl = getWidth() - cWidth - cParams.leftMargin
    						- cParams.rightMargin;
    				ct = getHeight() - cHeight - cParams.bottomMargin;
    				break;
    
    			}
    			cr = cl + cWidth;
    			cb = cHeight + ct;
    			childView.layout(cl, ct, cr, cb);
    		}
    
    	}

    代码比较容易懂:遍历所有的childView,根据childView的宽和高以及margin,然后分别将0,1,2,3位置的childView依次设置到左上、右上、左下、右下的位置。

    如果是第一个View(index=0) :则childView.layout(cl, ct, cr, cb); cl为childView的leftMargin , ct 为topMargin , cr 为cl+ cWidth , cb为 ct + cHeight

    如果是第二个View(index=1) :则childView.layout(cl, ct, cr, cb); 

    cl为getWidth() - cWidth - cParams.leftMargin- cParams.rightMargin;

    ct 为topMargin , cr 为cl+ cWidth , cb为 ct + cHeight

    剩下两个类似~

    这样就完成了,我们的ViewGroup代码的编写,下面我们进行测试,分别设置宽高为固定值,wrap_content,match_parent

    5、测试结果

    布局1:

    <com.example.zhy_custom_viewgroup.CustomImgContainer xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:background="#AA333333" >
    
        <TextView
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:background="#FF4444"
            android:gravity="center"
            android:text="0"
            android:textColor="#FFFFFF"
            android:textSize="22sp"
            android:textStyle="bold" />
    
        <TextView
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:background="#00ff00"
            android:gravity="center"
            android:text="1"
            android:textColor="#FFFFFF"
            android:textSize="22sp"
            android:textStyle="bold" />
    
        <TextView
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:background="#ff0000"
            android:gravity="center"
            android:text="2"
            android:textColor="#FFFFFF"
            android:textSize="22sp"
            android:textStyle="bold" />
    
        <TextView
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:background="#0000ff"
            android:gravity="center"
            android:text="3"
            android:textColor="#FFFFFF"
            android:textSize="22sp"
            android:textStyle="bold" />
    
    </com.example.zhy_custom_viewgroup.CustomImgContainer>

    ViewGroup宽和高设置为固定值

    效果图:



    布局2:

    <com.example.zhy_custom_viewgroup.CustomImgContainer xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#AA333333" >
    
        <TextView
            android:layout_width="150dp"
            android:layout_height="150dp"
            android:background="#E5ED05"
            android:gravity="center"
            android:text="0"
            android:textColor="#FFFFFF"
            android:textSize="22sp"
            android:textStyle="bold" />
    
        <TextView
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:background="#00ff00"
            android:gravity="center"
            android:text="1"
            android:textColor="#FFFFFF"
            android:textSize="22sp"
            android:textStyle="bold" />
    
        <TextView
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:background="#ff0000"
            android:gravity="center"
            android:text="2"
            android:textColor="#FFFFFF"
            android:textSize="22sp"
            android:textStyle="bold" />
    
        <TextView
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:background="#0000ff"
            android:gravity="center"
            android:text="3"
            android:textColor="#FFFFFF"
            android:textSize="22sp"
            android:textStyle="bold" />
    
    </com.example.zhy_custom_viewgroup.CustomImgContainer>
    ViewGroup的宽和高设置为wrap_content
    效果图:



    布局3:

    <com.example.zhy_custom_viewgroup.CustomImgContainer 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:background="#AA333333" >
    
        <TextView
            android:layout_width="150dp"
            android:layout_height="150dp"
            android:background="#E5ED05"
            android:gravity="center"
            android:text="0"
            android:textColor="#FFFFFF"
            android:textSize="22sp"
            android:textStyle="bold" />
    
        <TextView
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:background="#00ff00"
            android:gravity="center"
            android:text="1"
            android:textColor="#FFFFFF"
            android:textSize="22sp"
            android:textStyle="bold" />
    
        <TextView
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:background="#ff0000"
            android:gravity="center"
            android:text="2"
            android:textColor="#FFFFFF"
            android:textSize="22sp"
            android:textStyle="bold" />
    
        <TextView
            android:layout_width="150dp"
            android:layout_height="150dp"
            android:background="#0000ff"
            android:gravity="center"
            android:text="3"
            android:textColor="#FFFFFF"
            android:textSize="22sp"
            android:textStyle="bold" />
    
    </com.example.zhy_custom_viewgroup.CustomImgContainer>

    ViewGroup的宽和高设置为match_parent



    可以看到无论ViewGroup的宽和高的值如何定义,我们的需求都实现了预期的效果~~

    好了,通过这篇教程,希望大家已经能够初步掌握自定义ViewGroup的步骤,大家可以尝试自定义ViewGroup去实现LinearLayout的效果~~

    最后说明下,此为第一篇ViewGroup的教程,以后还会进一步的探讨如何更好的自定义ViewGroup~~~


    如果你觉得这篇文章对你有用,那么赞一个或者留个言吧~




    源码点击下载




    展开全文
  • Android控制文字水平间距android:letterSpacing 附录文章1实现的是Android的文字在水平方向的缩放,然而不是水平方向的文字间距。如果要想实现Android TextView之类的文字保持间距,那么需要设置android:...

    Android控制文字水平间距android:letterSpacing

    附录文章1实现的是Android的文字在水平方向的缩放,然而不是水平方向的文字间距。如果要想实现Android TextView之类的文字保持间距,那么需要设置android:letterSpacing,android:letterSpacing的值是一个浮点数,为标准字体的倍数作为间距。写一个小demo示例。
    Xml布局代码:

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
    
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Zhang Phil CSDN 默认" />
    
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:letterSpacing="0.1"
            android:text="Zhang Phil CSDN 0.1" />
    
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:letterSpacing="0.2"
            android:text="Zhang Phil CSDN 0.2" />
    
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:letterSpacing="0.3"
            android:text="Zhang Phil CSDN 0.3" />
    
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:letterSpacing="0.4"
            android:text="Zhang Phil CSDN 0.4" />
    
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:letterSpacing="0.5"
            android:text="Zhang Phil CSDN 0.5" />
    
    </LinearLayout>


    代码运行结果:


    附录文章:
    1,《Android控制水平方向字体缩放android:textScaleX》链接地址:http://blog.csdn.net/zhangphil/article/details/51306064

    展开全文
  • Android ViewGroup事件分发机制

    万次阅读 多人点赞 2014-09-09 09:38:03
    上一篇已经完整的解析了Android View的事件分发机制,今天给大家代码ViewGroup事件分发的源码解析~~凡是自定义ViewGroup实现各种滑动效果的,不可避免的会出现很多事件的冲突,对ViewGroup事件分发机制的了解,也...

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/39102591,本文出自【张鸿洋的博客】

    上一篇已经完整的解析了Android View的事件分发机制,今天给大家代码ViewGroup事件分发的源码解析~~凡是自定义ViewGroup实现各种滑动效果的,不可避免的会出现很多事件的冲突,对ViewGroup事件分发机制的了解,也有益于大家了解冲突产生的原因,以及对冲突进行处理~

    1、案例

    首先我们接着上一篇的代码,在代码中添加一个自定义的LinearLayout:

    package com.example.zhy_event03;
    
    import android.content.Context;
    import android.util.AttributeSet;
    import android.util.Log;
    import android.view.MotionEvent;
    import android.widget.LinearLayout;
    
    public class MyLinearLayout extends LinearLayout
    {
    	private static final String TAG = MyLinearLayout.class.getSimpleName();
    
    	public MyLinearLayout(Context context, AttributeSet attrs)
    	{
    		super(context, attrs);
    	}
    
    	@Override
    	public boolean dispatchTouchEvent(MotionEvent ev)
    	{
    		int action = ev.getAction();
    		switch (action)
    		{
    		case MotionEvent.ACTION_DOWN:
    			Log.e(TAG, "dispatchTouchEvent ACTION_DOWN");
    			break;
    		case MotionEvent.ACTION_MOVE:
    			Log.e(TAG, "dispatchTouchEvent ACTION_MOVE");
    			break;
    		case MotionEvent.ACTION_UP:
    			Log.e(TAG, "dispatchTouchEvent ACTION_UP");
    			break;
    
    		default:
    			break;
    		}
    		return super.dispatchTouchEvent(ev);
    	}
    
    	@Override
    	public boolean onTouchEvent(MotionEvent event)
    	{
    
    		int action = event.getAction();
    
    		switch (action)
    		{
    		case MotionEvent.ACTION_DOWN:
    			Log.e(TAG, "onTouchEvent ACTION_DOWN");
    			break;
    		case MotionEvent.ACTION_MOVE:
    			Log.e(TAG, "onTouchEvent ACTION_MOVE");
    			break;
    		case MotionEvent.ACTION_UP:
    			Log.e(TAG, "onTouchEvent ACTION_UP");
    			break;
    
    		default:
    			break;
    		}
    
    		return super.onTouchEvent(event);
    	}
    
    	@Override
    	public boolean onInterceptTouchEvent(MotionEvent ev)
    	{
    		
    		int action = ev.getAction();
    		switch (action)
    		{
    		case MotionEvent.ACTION_DOWN:
    			Log.e(TAG, "onInterceptTouchEvent ACTION_DOWN");
    			break;
    		case MotionEvent.ACTION_MOVE:
    			Log.e(TAG, "onInterceptTouchEvent ACTION_MOVE");
    			break;
    		case MotionEvent.ACTION_UP:
    			Log.e(TAG, "onInterceptTouchEvent ACTION_UP");
    			break;
    
    		default:
    			break;
    		}
    		
    		return super.onInterceptTouchEvent(ev);
    	}
    
    	@Override
    	public void requestDisallowInterceptTouchEvent(boolean disallowIntercept)
    	{
    		Log.e(TAG, "requestDisallowInterceptTouchEvent ");
    		super.requestDisallowInterceptTouchEvent(disallowIntercept);
    	}
    
    }
    

    继承LinearLayout,然后复写了与事件分发机制有关的代码,添加上了日志的打印~

    然后看我们的布局文件:

    <com.example.zhy_event03.MyLinearLayout 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=".MainActivity" >
    
        <com.example.zhy_event03.MyButton
            android:id="@+id/id_btn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="click me" />
    
    </com.example.zhy_event03.MyLinearLayout>
    

    MyLinearLayout中包含一个MyButton,MyButton都上篇博客中已经出现过,这里就不再贴代码了,不清楚可以去查看~

    然后MainActivity就是直接加载布局,没有任何代码~~~

    直接运行我们的代码,然后点击我们的Button,依然是有意的MOVE一下,不然不会触发MOVE事件,看一下日志的输出:

    09-06 09:57:27.287: E/MyLinearLayout(959): dispatchTouchEvent ACTION_DOWN
    09-06 09:57:27.287: E/MyLinearLayout(959): onInterceptTouchEvent ACTION_DOWN
    09-06 09:57:27.287: E/MyButton(959): dispatchTouchEvent ACTION_DOWN
    09-06 09:57:27.297: E/MyButton(959): onTouchEvent ACTION_DOWN
    09-06 09:57:27.297: E/MyButton(959): onTouchEvent ACTION_MOVE
    09-06 09:57:27.327: E/MyLinearLayout(959): dispatchTouchEvent ACTION_MOVE
    09-06 09:57:27.327: E/MyLinearLayout(959): onInterceptTouchEvent ACTION_MOVE
    09-06 09:57:27.337: E/MyButton(959): dispatchTouchEvent ACTION_MOVE
    09-06 09:57:27.337: E/MyButton(959): onTouchEvent ACTION_MOVE
    09-06 09:57:27.457: E/MyLinearLayout(959): dispatchTouchEvent ACTION_UP
    09-06 09:57:27.457: E/MyLinearLayout(959): onInterceptTouchEvent ACTION_UP
    09-06 09:57:27.457: E/MyButton(959): dispatchTouchEvent ACTION_UP
    09-06 09:57:27.457: E/MyButton(959): onTouchEvent ACTION_UP
    

    可以看到大体的事件流程为:

    MyLinearLayout的dispatchTouchEvent -> MyLinearLayout的onInterceptTouchEvent -> MyButton的dispatchTouchEvent ->Mybutton的onTouchEvent 

    可以看出,在View上触发事件,最先捕获到事件的为View所在的ViewGroup,然后才会到View自身~

    下面我们按照日志的输出,进入源码~

    2、源码分析

    ViewGroup - dispatchTouchEvent

    1、ViewGroup - dispatchTouchEvent - ACTION_DOWN

    首先是ViewGroup的dispatchTouchEvent方法:

     @Override
        public boolean dispatchTouchEvent(MotionEvent ev) {
            if (!onFilterTouchEventForSecurity(ev)) {
                return false;
            }
    
            final int action = ev.getAction();
            final float xf = ev.getX();
            final float yf = ev.getY();
            final float scrolledXFloat = xf + mScrollX;
            final float scrolledYFloat = yf + mScrollY;
            final Rect frame = mTempRect;
    
            boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
    
            if (action == MotionEvent.ACTION_DOWN) {
                if (mMotionTarget != null) {
                    // this is weird, we got a pen down, but we thought it was
                    // already down!
                    // XXX: We should probably send an ACTION_UP to the current
                    // target.
                    mMotionTarget = null;
                }
                // If we're disallowing intercept or if we're allowing and we didn't
                // intercept
                if (disallowIntercept || !onInterceptTouchEvent(ev)) {
                    // reset this event's action (just to protect ourselves)
                    ev.setAction(MotionEvent.ACTION_DOWN);
                    // We know we want to dispatch the event down, find a child
                    // who can handle it, start with the front-most child.
                    final int scrolledXInt = (int) scrolledXFloat;
                    final int scrolledYInt = (int) scrolledYFloat;
                    final View[] children = mChildren;
                    final int count = mChildrenCount;
    
                    for (int i = count - 1; i >= 0; i--) {
                        final View child = children[i];
                        if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE
                                || child.getAnimation() != null) {
                            child.getHitRect(frame);
                            if (frame.contains(scrolledXInt, scrolledYInt)) {
                                // offset the event to the view's coordinate system
                                final float xc = scrolledXFloat - child.mLeft;
                                final float yc = scrolledYFloat - child.mTop;
                                ev.setLocation(xc, yc);
                                child.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
                                if (child.dispatchTouchEvent(ev))  {
                                    // Event handled, we have a target now.
                                    mMotionTarget = child;
                                    return true;
                                }
                                // The event didn't get handled, try the next view.
                                // Don't reset the event's location, it's not
                                // necessary here.
                            }
                        }
                    }
                }
            }                                                                                                                                                  ....//other code omitted
    代码比较长,决定分段贴出,首先贴出的是ACTION_DOWN事件相关的代码。

    16行:进入ACTION_DOWN的处理

    17-23行:将mMotionTarget置为null

    26行:进行判断:if(disallowIntercept || !onInterceptTouchEvent(ev))

    两种可能会进入IF代码段

    1、当前不允许拦截,即disallowIntercept =true,

    2、当前允许拦截但是不拦截,即disallowIntercept =false,但是onInterceptTouchEvent(ev)返回false ;

    注:disallowIntercept 可以通过viewGroup.requestDisallowInterceptTouchEvent(boolean);进行设置,后面会详细说;而onInterceptTouchEvent(ev)可以进行复写。

    36-57行:开始遍历所有的子View

    41行:进行判断当前的x,y坐标是否落在子View身上,如果在,47行,执行child.dispatchTouchEvent(ev),就进入了View的dispatchTouchEvent代码中了,如果不了解请参考:Android View的事件分发机制,当child.dispatchTouchEvent(ev)返回true,则为mMotionTarget=child;然后return true;

    ViewGroup的ACTION_DOWN分析结束,总结一下:

    ViewGroup实现捕获到DOWN事件,如果代码中不做TOUCH事件拦截,则开始查找当前x,y是否在某个子View的区域内,如果在,则把事件分发下去。


    按照日志,接下来到达ACTION_MOVE

    2、ViewGroup - dispatchTouchEvent - ACTION_MOVE

    首先我们源码进行删减,只留下MOVE相关的代码:

     @Override
        public boolean dispatchTouchEvent(MotionEvent ev) {
            final int action = ev.getAction();
            final float xf = ev.getX();
            final float yf = ev.getY();
            final float scrolledXFloat = xf + mScrollX;
            final float scrolledYFloat = yf + mScrollY;
            final Rect frame = mTempRect;
    
            boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
    
           //...ACTION_DOWN
    
           //...ACTIN_UP or ACTION_CANCEL
    
            // The event wasn't an ACTION_DOWN, dispatch it to our target if
            // we have one.
    	final View target = mMotionTarget;
          
    
            // if have a target, see if we're allowed to and want to intercept its
            // events
            if (!disallowIntercept && onInterceptTouchEvent(ev)) {
                //....
            }
            // finally offset the event to the target's coordinate system and
            // dispatch the event.
            final float xc = scrolledXFloat - (float) target.mLeft;
            final float yc = scrolledYFloat - (float) target.mTop;
            ev.setLocation(xc, yc);
    
            return target.dispatchTouchEvent(ev);
        }

    18行:把ACTION_DOWN时赋值的mMotionTarget,付给target ; 

    23行:if (!disallowIntercept && onInterceptTouchEvent(ev)) 当前允许拦截且拦截了,才进入IF体,当然了默认是不会拦截的~这里执行了onInterceptTouchEvent(ev)

    28-30行:把坐标系统转化为子View的坐标系统

    32行:直接return target.dispatchTouchEvent(ev); 

    可以看到,正常流程下,ACTION_MOVE在检测完是否拦截以后,直接调用了子View.dispatchTouchEvent,事件分发下去;

    最后就是ACTION_UP了

    3、ViewGroup - dispatchTouchEvent - ACTION_UP

     public boolean dispatchTouchEvent(MotionEvent ev) {
            if (!onFilterTouchEventForSecurity(ev)) {
                return false;
            }
    
            final int action = ev.getAction();
            final float xf = ev.getX();
            final float yf = ev.getY();
            final float scrolledXFloat = xf + mScrollX;
            final float scrolledYFloat = yf + mScrollY;
            final Rect frame = mTempRect;
    
            boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
    
            if (action == MotionEvent.ACTION_DOWN) {...}
    	
    	boolean isUpOrCancel = (action == MotionEvent.ACTION_UP) ||
                    (action == MotionEvent.ACTION_CANCEL);
    
    	if (isUpOrCancel) {
                mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
            }
    	final View target = mMotionTarget;
    	if(target ==null ){...}
    	if (!disallowIntercept && onInterceptTouchEvent(ev)) {...}
    
            if (isUpOrCancel) {
                mMotionTarget = null;
            }
    
            // finally offset the event to the target's coordinate system and
            // dispatch the event.
            final float xc = scrolledXFloat - (float) target.mLeft;
            final float yc = scrolledYFloat - (float) target.mTop;
            ev.setLocation(xc, yc);
    
            return target.dispatchTouchEvent(ev);
        }

    17行:判断当前是否是ACTION_UP

    21,28行:分别重置拦截标志位以及将DOWN赋值的mMotionTarget置为null,都UP了,当然置为null,下一次DOWN还会再赋值的~

    最后,修改坐标系统,然后调用target.dispatchTouchEvent(ev);


    正常情况下,即我们上例整个代码的流程我们已经走完了:

    1、ACTION_DOWN中,ViewGroup捕获到事件,然后判断是否拦截,如果没有拦截,则找到包含当前x,y坐标的子View,赋值给mMotionTarget,然后调用 mMotionTarget.dispatchTouchEvent

    2、ACTION_MOVE中,ViewGroup捕获到事件,然后判断是否拦截,如果没有拦截,则直接调用mMotionTarget.dispatchTouchEvent(ev)

    3、ACTION_UP中,ViewGroup捕获到事件,然后判断是否拦截,如果没有拦截,则直接调用mMotionTarget.dispatchTouchEvent(ev)

    当然了在分发之前都会修改下坐标系统,把当前的x,y分别减去child.left 和 child.top ,然后传给child;


    3、关于拦截

    1、如何拦截

    上面的总结都是基于:如果没有拦截;那么如何拦截呢?

    复写ViewGroup的onInterceptTouchEvent方法:

    @Override
    	public boolean onInterceptTouchEvent(MotionEvent ev)
    	{
    		int action = ev.getAction();
    		switch (action)
    		{
    		case MotionEvent.ACTION_DOWN:
    			//如果你觉得需要拦截
    			return true ; 
    		case MotionEvent.ACTION_MOVE:
    			//如果你觉得需要拦截
    			return true ; 
    		case MotionEvent.ACTION_UP:
    			//如果你觉得需要拦截
    			return true ; 
    		}
    		
    		return false;
    	}

    默认是不拦截的,即返回false;如果你需要拦截,只要return true就行了,这要该事件就不会往子View传递了,并且如果你在DOWN retrun true ,则DOWN,MOVE,UP子View都不会捕获事件;如果你在MOVE return true , 则子View在MOVE和UP都不会捕获事件。

    原因很简单,当onInterceptTouchEvent(ev) return true的时候,会把mMotionTarget 置为null ; 

    2、如何不被拦截

    如果ViewGroup的onInterceptTouchEvent(ev) 当ACTION_MOVE时return true ,即拦截了子View的MOVE以及UP事件;

    此时子View希望依然能够响应MOVE和UP时该咋办呢?

    Android给我们提供了一个方法:requestDisallowInterceptTouchEvent(boolean) 用于设置是否允许拦截,我们在子View的dispatchTouchEvent中直接这么写:

    @Override
    	public boolean dispatchTouchEvent(MotionEvent event)
    	{
    		getParent().requestDisallowInterceptTouchEvent(true);  
    		int action = event.getAction();
    
    		switch (action)
    		{
    		case MotionEvent.ACTION_DOWN:
    			Log.e(TAG, "dispatchTouchEvent ACTION_DOWN");
    			break;
    		case MotionEvent.ACTION_MOVE:
    			Log.e(TAG, "dispatchTouchEvent ACTION_MOVE");
    			break;
    		case MotionEvent.ACTION_UP:
    			Log.e(TAG, "dispatchTouchEvent ACTION_UP");
    			break;
    
    		default:
    			break;
    		}
    		return super.dispatchTouchEvent(event);
    	}

    getParent().requestDisallowInterceptTouchEvent(true);  这样即使ViewGroup在MOVE的时候return true,子View依然可以捕获到MOVE以及UP事件。


    从源码也可以解释:

    ViewGroup MOVE和UP拦截的源码是这样的:

    if (!disallowIntercept && onInterceptTouchEvent(ev)) {
                final float xc = scrolledXFloat - (float) target.mLeft;
                final float yc = scrolledYFloat - (float) target.mTop;
                mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
                ev.setAction(MotionEvent.ACTION_CANCEL);
                ev.setLocation(xc, yc);
                if (!target.dispatchTouchEvent(ev)) {
                    // target didn't handle ACTION_CANCEL. not much we can do
                    // but they should have.
                }
                // clear the target
                mMotionTarget = null;
                // Don't dispatch this event to our own view, because we already
                // saw it when intercepting; we just want to give the following
                // event to the normal onTouchEvent().
                return true;
            }

    当我们把disallowIntercept设置为true时,!disallowIntercept直接为false,于是拦截的方法体就被跳过了~

    注:如果ViewGroup在onInterceptTouchEvent(ev)  ACTION_DOWN里面直接return true了,那么子View是木有办法的捕获事件的~~~


    4、如果没有找到合适的子View

    我们的实例,直接点击ViewGroup内的按钮,当然直接很顺利的走完整个流程;

    但是有两种特殊情况

    1、ACTION_DOWN的时候,子View.dispatchTouchEvent(ev)返回的为false ; 

    如果你仔细看了,你会注意到ViewGroup的dispatchTouchEvent(ev)的ACTION_DOWN代码是这样的

      if (child.dispatchTouchEvent(ev))  {
                                    // Event handled, we have a target now.
                                    mMotionTarget = child;
                                    return true;
                                }

    只有在child.dispatchTouchEvent(ev)返回true了,才会认为找到了能够处理当前事件的View,即mMotionTarget = child;

    但是如果返回false,那么mMotionTarget 依然是null

    mMotionTarget 为null会咋样呢?

    其实ViewGroup也是View的子类,如果没有找到能够处理该事件的子View,或者干脆就没有子View;

    那么,它作为一个View,就相当于View的事件转发了~~直接super.dispatchTouchEvent(ev);

    源码是这样的:

     final View target = mMotionTarget;
            if (target == null) {
                // We don't have a target, this means we're handling the
                // event as a regular view.
                ev.setLocation(xf, yf);
                if ((mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
                    ev.setAction(MotionEvent.ACTION_CANCEL);
                    mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
                }
                return super.dispatchTouchEvent(ev);
            }

    我们没有一个能够处理该事件的目标元素,意味着我们需要自己处理~~~就相当于传统的View~

    2、那么什么时候子View.dispatchTouchEvent(ev)返回的为true

    如果你仔细看了上篇博客,你会发现只要子View支持点击或者长按事件一定返回true~~

    源码是这样的:

     
    if (((viewFlags & CLICKABLE) == CLICKABLE ||
                    (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {   
                 return true ;                                                                                                                                                                                                                                                                                                   }
    
    

    5、总结

    关于代码流程上面已经总结过了~

    1、如果ViewGroup找到了能够处理该事件的View,则直接交给子View处理,自己的onTouchEvent不会被触发;

    2、可以通过复写onInterceptTouchEvent(ev)方法,拦截子View的事件(即return true),把事件交给自己处理,则会执行自己对应的onTouchEvent方法

    3、子View可以通过调用getParent().requestDisallowInterceptTouchEvent(true);  阻止ViewGroup对其MOVE或者UP事件进行拦截;


    好了,那么实际应用中能解决哪些问题呢?

    比如你需要写一个类似slidingmenu的左侧隐藏menu,主Activity上有个Button、ListView或者任何可以响应点击的View,你在当前View上死命的滑动,菜单栏也出不来;因为MOVE事件被子View处理了~ 你需要这么做:在ViewGroup的dispatchTouchEvent中判断用户是不是想显示菜单,如果是,则在onInterceptTouchEvent(ev)拦截子View的事件;自己进行处理,这样自己的onTouchEvent就可以顺利展现出菜单栏了~~





    展开全文
  • Android 自定义ViewGroup

    千次阅读 2014-04-09 22:04:41
    版本:1.0日期:2014.4.2版权:© 2014 kince 转载注明出处 之前介绍了关于自定义View类的一些知识,总结一下就是继承于View类,重写onDraw()方法。如果组件有自己的属性的话,在attrs.xml文件中加入即可。...
  • Android ViewGroup

    千次阅读 2010-08-31 23:32:00
     public class TestGroupView extends ViewGroup { public TestGroupView(Context context) { super(context); // TODO Auto-generated constructor stub } public TestGroupView(Context ...
  • Android移植基础

    万人学习 2015-05-21 23:53:34
    Android视频课程,该课程可以让学员了解Android系统架构、学习如何下载Android源码、编译及开发Android、学习如何追踪Android源码、了解Linux内核启动流程、了解Android启动流程、学习如何移植外部函式库至Android...
  • Android View系统分析之二View与ViewGroupandroidviewgroup 目录 在Android View系统分析之从setContentView说开来(一)一文中,我们从setContentView开始阐述了Android中的视图层次,从设置内容布局到整个...
  • Android 自定义ViewGroup 实战篇 -> 实现FlowLayout

    万次阅读 多人点赞 2014-08-04 09:13:44
    http://blog.csdn.net/lmj623565791/article/details/38352503 ,本文出自【张鸿洋的博客】1、概述上一篇已经基本给大家介绍了如何自定义ViewGroup,如果你还不了解,请查看:Android 手把手教您自定ViewGroup ,...
  • Android Studio配置本地SDK

    万次阅读 2019-02-21 11:03:23
    Android Studio配置本地SDK 问题一:Android Studio报错:SDK does not contain any platforms. 解决方法:本地SDK应放到名为platforms的外层文件夹内。 例子:D:\SDK\platforms… (选择路径时选取到SDK文件夹...
  • Android ViewDragHelper完全解析 自定义ViewGroup神器

    万次阅读 多人点赞 2015-07-13 10:16:25
    转载请标明出处: ...一、概述在自定义ViewGroup中,很多效果都包含用户手指去拖动其内部的某个View(eg:侧滑菜单等),针对具体的需要去写好onInterceptTouchEvent和onTouchEvent这两个方法是一件很不容易的事
  • Android Studio安装与SDK配置

    万次阅读 2016-04-26 09:11:26
    Android Studio安装和sdk配置!
  • Android ViewGroup原理

    千次阅读 2013-05-13 12:32:54
    Android系统的视图结构的设计也采用了组合模式,即View作为所有图形的基类,Viewgroup对View继承扩展为视图容器类,由此就得到了视图部分的基本结构--树形结构 View定义了绘图的基本操作 基本操作由三个函数...
  • Android ViewGroup拦截触摸事件详解

    万次阅读 2014-10-01 21:29:13
    在自定义ViewGroup中,有时候需要实现触摸事件拦截,比如ListView下拉刷新就是典型的触摸事件拦截的例子。触摸事件拦截就是在触摸事件被parent view拦截,而不会分发给其child,即使触摸发生在该child身上。被拦截的...
  • ViewGroupandroid:animateLayoutChanges属性

    千次阅读 2016-05-03 14:49:52
    * android:animateLayoutChanges属性,android:animateLayoutChanges=true * /false,所有派ViewGroup的子控件都具有此属性,能实现添加/删除其中控件时,带有默认动画了。 * * @description: * @author ldm ...
  • AndroidViewGroup

    2020-02-05 14:18:13
    ViewGroup的使用
  • Android自定义ViewGroup

    2015-09-20 19:30:57
    概述:ViewGroup作为一个放置View的容器,并且我们在写布局xml的时候,会告诉容器(凡是以layout为开头的属性,都是为用于告诉容器的),我们的宽度(layout_width)、高度(layout_height)、对齐方式(layout_...
  • android 自定义ViewGroup之浪漫求婚

    千次阅读 2016-11-18 11:07:46
    1、最终效果有木有发现还是很小清新的感觉 2、看整体效果这是一个scrollView,滑动时每个子view都有一个或多个动画效果,但是如果我们直接给每个子view加上动画去实现这个需求就太low了,而且也不利于扩展,所以这里...
  • Android自定义ViewGroup实现流式布局

    千次阅读 2015-08-28 18:02:44
    实现宽度不足自动换行的流式布局: FlowLayout.java ... import android.content.Context; import android.util.AttributeSet; import android.view.View;...import android.view.ViewGroup; /** * Cr
  • 像上图,宽度不足自动换行。 FlowLayout /** * author : stone * email : aa86799@163.com * time : 15/8/4 15 08 */ public class FlowLayout extends ViewGroup { public FlowLayout(Context context) {
  • Android 自定义ViewGroup手把手教你实现ArcMenu

    万次阅读 多人点赞 2014-07-08 17:41:52
    逛eoe发现这样的UI效果,感觉很不错,后来知道github上有这么个开源项目~~~~当然本篇不是教你如何使用这个开源项目,而是教你如何自己通过自定义ViewGroup写这样的效果,自定义ViewGroup也是我的痛楚,嘿嘿,希望...
  • android之View和ViewGroup介绍

    千次阅读 2013-07-29 09:16:09
    资源描述:  Activity(活动)中包含views(视图)和ViewGroups(视图组)。 “视图”(View)就是显示在屏幕上的一个组件(Widget)。View的例子:按钮(Button)、...“视图组”(ViewGroup)可以包含一个或多个
  • 前面我们分析了如何自定义View,下面我们继续来分析如何创建自定义ViewGroupViewGroup存在的目的就是为了对其子View进行管理,为其子View添加显示、响应的规则。因此,自定义ViewGroup通常需要重写onMeasure()方法...
  • android自定义ViewGroup(侧滑菜单)

    千次阅读 2016-11-27 22:14:13
    侧滑菜单的简单实现不少APP中都有这种侧滑菜单,例如QQ这类的,比较有名开源库如slidingmenu。 有兴趣的可以去研究研究这个开源库。这里我们将一种自己的实现方法,把学习的 东西做个记录,O(∩_∩)O!...

空空如也

1 2 3 4 5 ... 20
收藏数 1,946,086
精华内容 778,434
关键字:

viewgroup