Launcher3拖拽分析(一)——Android13版本

Launcher3拖拽分析系列文章

第一章 拖拽事件的发起过程分析



前言

Launcher3比较重要特性的就是支持图标拖拽,本文记录下我学习的Android13版本的Launcher拖拽的过程,由于个人理解能力有限,如有错误或者补充之处,请大家悉数指教。

本文主要介绍launcher拖拽功能中几次事件是怎么来的,后面的文章再继续分析核心的拖拽(onDragOver和onDrop)


一、拖拽怎么发起的?

接触过AOSP的Launcher3项目的肯定不陌生,拖拽就是长按图标,等到workspace画面微缩后,就可以拖动该图标。如果遇到格子已经被占领,则挤走当前格子的图标(或者与其生成文件夹);如果当前格子没有被占领则可以放入这个位置;如果拖动到画面右侧边缘,则自动进入下一页。

在这里插入图片描述

那么这一切是怎么发生的呢?

二、明确几个对象

1、Workspace:主屏幕对应的布局,是直接添加到Launcher.xml中的布局对象
2、CellLayout:主屏幕中的每一页,其父布局就是Workspace,左右滑动屏幕,就是每一个CellLayout的变化过程,这个类中有很多处理拖拽相关方法。
3、ShortcutAndWidgetContainer:装载图标的容器(布局),其父布局是CellLayout。
4、BubbleTextView:launcher中的图标对象(单击、长按图标的实际载体)

其view树结构如下:
在这里插入图片描述
其中ShortcutAndWidgetContainer会被划分为m×n的格子区域,具体划分数量依据res/xml/device_profiles.xml中定义,大家可自行查看。

每个BubbTextView怎么显示在ShortcutAndWidgetContainer对应的格子的?

这就要依据BubbTextView携带的ItemInfo对象了,这个信息类封装了图标的一切信息:行列号、屏幕ID、图标名称、图标Drawable、Intent信息等等,这个信息对象非常重要!!!。ShortcutAndWidgetContainer的onMeasuew() 方法会对每个装载BubbTextView的格子指定大小(也是根据配置xml来的),在onLayout()方法中根据BubbTextView的行列号计算其在当前那个格子中。

因此,拖拽就是不断更新BubbTextView的行列号以及屏幕ID的过程,然后实时刷新。

除了上述几个用于显示的View对象,还有和拖拽相关的专用对象

1、DragLayer:拖拽图层,最顶层的View对象,其主要功能就是处理滑动事件,以及拖拽对象的动画效果。
其子View包含Workspace(主页)、PageIndicatorDots(分页指示器)、AllApp(更多应用界面、上拉弹出的抽屉页)、HotSeat(画面底部常驻图标区)…。具体大家可以查看res/layout/launcher.xml里面的内容,以及DragLayer类方法。

2、DragController:核心拖拽控制器基类,定义很多拖拽相关的公共方法,处理滑动事件等等,其子类重点关注LauncherDragController。

2、DropTarget:拖拽事件接口,在Workspace中有实现这个接口。其包含主要的拖拽事件:onDrop(拖拽结束松手的瞬间触发)、onDragEnter(进入拖拽触发)、onDragOver(拖拽过程中触发)、onDragExit(退出拖拽)。重点需要理解的就是onDragOver以及onDrop。

3、DragObject:DropTarget的内部类,顾名思义这个对象就是“拖拽对象”,其最重要的功能就是封装拖拽过程中的信息(数据结构)

4、DragView:BubbTextView的平替(他们携带的信息是一样的),因为BubbTextView的父布局是ShortcutAndWidgetContainer,如果拖拽到另一个ShortcutAndWidgetContainer是不允许的。所以创造了一个DragView来代替BubbTextView,这样拖动过程其实是拽着DragView动(原始的BubbTextView会被隐藏)

注意这里有个坑:如果在Launcher中替换了app的图标(BubbleTextView#applyIconAndLabel()方法中替换了FastBitmapDrawable),且图标资源是自适应的,但是拖拽开始时刻会创建DragView,DragView的图标还是原始的,从而发生拖拽时候被替换的图标被打回原型的情况。因此DragView的drawable资源也得跟着替换才行!!!

5、DraggableView:定义绘制预览、拖拽预览以及相关动画的接口,BubbleTextView中有相关的实现。

6、DragOptions:定义拖拽过程中的一些状态、行为信息(例如:是否正在拖拽,是否是键盘控制等等)。

二、拖拽触发的起点分析

1.BubbleTextView创建以及添加点击事件

代码如下(示例):

// Launcher.java中
public View createShortcut(ViewGroup parent, WorkspaceItemInfo info) {
		// 找到布局文件并创建对象
        BubbleTextView favorite = (BubbleTextView) LayoutInflater.from(parent.getContext())
                .inflate(R.layout.app_icon, parent, false);
        favorite.applyFromWorkspaceItem(info);
        // 添加单击事件,点击图标跳转对应的Activity
        favorite.setOnClickListener(getItemOnClickListener());
        // 添加焦点变更的回调
        favorite.setOnFocusChangeListener(mFocusHandler);
        return favorite;
}

上面只是BubbleTextView 创建过程,以及单击事件。那么长按事件在哪儿?

只要跟着Launcher#bindItems()步骤,就能找到WorkspaceLayoutManager#addInScreen()中

//WorkspaceLayoutManager.java
default void addInScreen(View child, int container, int screenId, int x, int y,
            int spanX, int spanY) {
            
		...
		...
		
		// 设置此视图是否应该为长按等事件提供触觉反馈。
		child.setHapticFeedbackEnabled(false);    

        // 这里也是一个关键点:设置当前child(BubbleTextView )的长按事件!!!
        child.setOnLongClickListener(getWorkspaceChildOnLongClickListener());
        if (child instanceof DropTarget) {
            onAddDropTarget((DropTarget) child);
        }

}

关键的来了,长按事件回调就写在getWorkspaceChildOnLongClickListener()方法中,接着跟代码,最终找到ItemLongClickListener#onWorkspaceItemLongClick():顾名思义就是桌面item的长按事件

// ItemLongClickListener.java
private static boolean onWorkspaceItemLongClick(View v) {
        if (v instanceof LauncherAppWidgetHostView) {
            TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "Widgets.onLongClick");
        } else {
            TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onWorkspaceItemLongClick");
        }
        // 拿到Launcher对象(activity)
        Launcher launcher = Launcher.getLauncher(v.getContext());
        // 一些条件过滤判断
        if (!canStartDrag(launcher)) return false;
        if (!launcher.isInState(NORMAL) && !launcher.isInState(OVERVIEW)) return false;
        if (!(v.getTag() instanceof ItemInfo)) return false;

        launcher.setWaitingForResult(null);
		
		// 核心来了:开始拖拽咯!!!
        beginDrag(v, launcher, (ItemInfo) v.getTag(), launcher.getDefaultWorkspaceDragOptions());
        return true;
}

接着看beginDrag()

// ItemLongClickListener.java
public static void beginDrag(View v, Launcher launcher, ItemInfo info,
            DragOptions dragOptions) {
		// 一些条件判断
        if (info.container >= 0) {
            Folder folder = Folder.getOpen(launcher);
            if (folder != null) {
                if (!folder.getIconsInReadingOrder().contains(v)) {
                    folder.close(true);
                } else {
                    folder.startDrag(v, dragOptions);
                    return;
                }
            }
        }

		// 封装了一个单元格信息的数据结构,类似于哈希表,key:BubbleTextView ,val:单元格信息(行列号...)
        CellLayout.CellInfo longClickCellInfo = new CellLayout.CellInfo(v, info);   
		
		// 开始调用Workspace中startDrag()方法 
        launcher.getWorkspace().startDrag(longClickCellInfo, dragOptions);
}

2.Workspace触发拖拽事件

接着看Workspace#startDrag()

// Workspace.java
public void startDrag(CellLayout.CellInfo cellInfo, DragOptions options) {
        // 拿出BubbleTextView
        View child = cellInfo.cell;

        mDragInfo = cellInfo;
        // 拖动第一瞬间,格子上原本的BubbleTextView设置为不可见,为DragView的出现做铺垫!!!
        child.setVisibility(INVISIBLE);   

        if (options.isAccessibleDrag) {
            mDragController.addDragListener(
                    new AccessibleDragListenerAdapter(this, WorkspaceAccessibilityHelper::new) {
                        @Override
                        protected void enableAccessibleDrag(boolean enable) {
                            super.enableAccessibleDrag(enable);
                            setEnableForLayout(mLauncher.getHotseat(), enable);
                        }
                    });
        }

        beginDragShared(child, this, options);
}

接着看beginDragShared(),头疼的来了~~

// Workspace.java
public DragView beginDragShared(View child, DraggableView draggableView, DragSource source,
            ItemInfo dragObject, DragPreviewProvider previewProvider, DragOptions dragOptions) {
		// 1 找到图标的缩放比
        float iconScale = 1f;
        if (child instanceof BubbleTextView) {
            Drawable icon = ((BubbleTextView) child).getIcon();
            if (icon instanceof FastBitmapDrawable) {
                iconScale = ((FastBitmapDrawable) icon).getAnimatedScale();
            }
        }

        // Clear the pressed state if necessary
        // 2、清除掉聚焦状态,以及按压背景信息
        child.clearFocus();
        child.setPressed(false);
        if (child instanceof BubbleTextView) {
            BubbleTextView icon = (BubbleTextView) child;
            icon.clearPressedBackground();
        }

		// 3、draggableView的创建
        if (draggableView == null && child instanceof DraggableView) {
            draggableView = (DraggableView) child;
        }

		// 4、构建拖拽相关的图标对象,原始BubbleTextView不是已经不可见了嘛(上面的代码中),
		// 因此这里给再造一个图标的Drawable
        final View contentView = previewProvider.getContentView();
        final float scale;
        // The draggable drawable follows the touch point around on the screen
        final Drawable drawable;
        if (contentView == null) {
            drawable = previewProvider.createDrawable();
            scale = previewProvider.getScaleAndPosition(drawable, mTempXY);
        } else {
            drawable = null;
            scale = previewProvider.getScaleAndPosition(contentView, mTempXY);
        }
		
		// 5、定义一些拖拽相关的bounds
        int halfPadding = previewProvider.previewPadding / 2;
        int dragLayerX = mTempXY[0];
        int dragLayerY = mTempXY[1];

        Point dragVisualizeOffset = null;
        Rect dragRect = new Rect();

        if (draggableView != null) {
            draggableView.getSourceVisualDragBounds(dragRect);
            dragLayerY += dragRect.top;
            dragVisualizeOffset = new Point(- halfPadding, halfPadding);
        }

		// 6、初始化mDragSourceInternal (ShortcutAndWidgetContainer对象)
        if (child.getParent() instanceof ShortcutAndWidgetContainer) {
            mDragSourceInternal = (ShortcutAndWidgetContainer) child.getParent();
        }

		// 7、初始化dragOptions相关的信息
        if (child instanceof BubbleTextView) {
            BubbleTextView btv = (BubbleTextView) child;
            if (!dragOptions.isAccessibleDrag) {
                dragOptions.preDragCondition = btv.startLongPressAction();
            }
            if (btv.isDisplaySearchResult()) {
                dragOptions.preDragEndScale = (float) mAllAppsIconSize / btv.getIconSize();
            }
        }

		// 8、关键的来了,上面的代码都是做拖拽前的准备工作,下面的mDragController.startDrag()才是关键
		// 创建DragView并开始拖拽!!!!!!
        final DragView dv;
        // 这两个判断就是看有么有drawable,反正最终都会调用startDrag()方法
        if (contentView instanceof View) {
            if (contentView instanceof LauncherAppWidgetHostView) {
                mDragController.addDragListener(new AppWidgetHostViewDragListener(mLauncher));
            }
            dv = mDragController.startDrag(
                    contentView,
                    draggableView,
                    dragLayerX,
                    dragLayerY,
                    source,
                    dragObject,
                    dragVisualizeOffset,
                    dragRect,
                    scale * iconScale,
                    scale,
                    dragOptions);
        } else {
            dv = mDragController.startDrag(
                    drawable,
                    draggableView,
                    dragLayerX,
                    dragLayerY,
                    source,
                    dragObject,
                    dragVisualizeOffset,
                    dragRect,
                    scale * iconScale,
                    scale,
                    dragOptions);
        }
        return dv;
    }

关键代码来到了dv = mDragController.startDrag() , 跟着代码跳到了: DragController#startDrag() --> LauncherDragController#startDrag。

其中LauncherDragController是DragController的子类之一,调试代码的时候发现最终调用的是LauncherDragController#startDrag()方法。

因此接着看LauncherDragController#startDrag()方法:

 @Override
   protected DragView startDrag(
           @Nullable Drawable drawable,
           @Nullable View view,
           DraggableView originalView,
           int dragLayerX,
           int dragLayerY,
           DragSource source,
           ItemInfo dragInfo,
           Point dragOffset,
           Rect dragRegion,
           float initialDragViewScale,
           float dragViewScaleOnDrop,
           DragOptions options) {
       if (TestProtocol.sDebugTracing) {
           Log.d(TestProtocol.NO_DROP_TARGET, "5");
       }
       if (PROFILE_DRAWING_DURING_DRAG) {
           android.os.Debug.startMethodTracing("Launcher");
       }

       mActivity.hideKeyboard(); // 隐藏键盘
       AbstractFloatingView.closeOpenViews(mActivity, false, TYPE_DISCOVERY_BOUNCE);

       Log.d("TAG", "startDrag: LauncherDragController 1");
       // 下面就是创建一些对象、变量
       mOptions = options;
       if (mOptions.simulatedDndStartPoint != null) {
           mLastTouch.x = mMotionDown.x = mOptions.simulatedDndStartPoint.x;
           mLastTouch.y = mMotionDown.y = mOptions.simulatedDndStartPoint.y;
       }

       final int registrationX = mMotionDown.x - dragLayerX;
       final int registrationY = mMotionDown.y - dragLayerY;

       final int dragRegionLeft = dragRegion == null ? 0 : dragRegion.left;
       final int dragRegionTop = dragRegion == null ? 0 : dragRegion.top;

       mLastDropTarget = null;

       mDragObject = new DropTarget.DragObject(mActivity.getApplicationContext());
       mDragObject.originalView = originalView;

       mIsInPreDrag = mOptions.preDragCondition != null
               && !mOptions.preDragCondition.shouldStartDrag(0);

       final Resources res = mActivity.getResources();
       final float scaleDps = mIsInPreDrag
               ? res.getDimensionPixelSize(R.dimen.pre_drag_view_scale) : 0f;
       
       // 核心代码1:创建DragView 的过程
       final DragView dragView = mDragObject.dragView = drawable != null
               ?
               new LauncherDragView(
               mActivity, drawable, registrationX, registrationY, initialDragViewScale,
               dragViewScaleOnDrop, scaleDps)
               :
               new LauncherDragView(mActivity, view,
               view.getMeasuredWidth(), view.getMeasuredHeight(), registrationX, registrationY,
               initialDragViewScale, dragViewScaleOnDrop, scaleDps);
       // 给dragView赋予 itemInfo对象(itemInfo对象封装了图标的行列号、屏幕ID、图标的title、intent等等信息,后续拖拽过程中会拿这个对象出来用)
       dragView.setItemInfo(dragInfo);

       mDragObject.dragComplete = false;

       mDragObject.xOffset = mMotionDown.x - (dragLayerX + dragRegionLeft);
       mDragObject.yOffset = mMotionDown.y - (dragLayerY + dragRegionTop);

	   // 核心代码2: 创建DragDriver对象,其内部有EventListener接口
       mDragDriver = DragDriver.create(this, mOptions, mFlingToDeleteHelper::recordMotionEvent);
       if (!mOptions.isAccessibleDrag) {
           mDragObject.stateAnnouncer = DragViewStateAnnouncer.createFor(dragView);
       }

		// 赋予一些变量,方便后续取值用
       mDragObject.dragSource = source;
       mDragObject.dragInfo = dragInfo;
       mDragObject.originalDragInfo = mDragObject.dragInfo.makeShallowCopy();

       if (dragOffset != null) {
           dragView.setDragVisualizeOffset(new Point(dragOffset));
       }
       if (dragRegion != null) {
           dragView.setDragRegion(new Rect(dragRegion));
       }

       mActivity.getDragLayer().performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
       dragView.show(mLastTouch.x, mLastTouch.y);
       mDistanceSinceScroll = 0;

       if (!mIsInPreDrag) {
           callOnDragStart();
       } else if (mOptions.preDragCondition != null) {
           mOptions.preDragCondition.onPreDragStart(mDragObject);
       }

       Log.d("TAG", "startDrag: LauncherDragController 2");
       // 非核心 处理一次的拖拽的移动事件(因为这个方法只触发一次,所以这里只走一次)
       handleMoveEvent(mLastTouch.x, mLastTouch.y);

       if (!mActivity.isTouchInProgress() && options.simulatedDndStartPoint == null) {
           // If it is an internal drag and the touch is already complete, cancel immediately
           MAIN_EXECUTOR.submit(this::cancelDrag);
       }
       return dragView;
   }

重点就在注释“核心代码2”这里,mDragDriver 被创建了,这个类包含一个EventListener接口,其内容如下所示:
在这里插入图片描述

并且DragController(及其子类)实现了这个接口:

在这里插入图片描述
根据我的理解:这个EventListener接口会在DragDriver#onDragEvent(DragEvent event)中被触发
在这里插入图片描述

至于这个event事件从哪儿来的,我就没继续往上层找了(主要是再上层的调用我也有点蒙蔽,工作时间有限也没继续深究,如果有朋友知道也请不吝赐教~~)。

既然这里触发了EventListener接口,而DragController实现了这个接口从而得到事件的响应,从而触发一系列拖拽事件:
onDragStart() --> onDragEnter() --> onDragOver() --> onDragExit() --> onDrop() --> onDragEnd()
在这里插入图片描述
至此拖拽的起点已经找到了


总结

本文主要介绍Android13的拖拽事件的起点分析,包括介绍了几个拖拽过程中用到的对象,以及长按事件触发的拖拽过程。

BubbtextView首先设置了长按监听事件,最终workspace触发了startDrag()方法(在这一过程中,把原始BubbtextView隐藏,创建了一个DragView来代替,同时杂七杂八的创建了很多变量、对象)。

关键的是创建一个DragDriver 对象,这个对象包含onDragEvent(),以及EventListener接口,并在onDragEvent中调用EventListener接口,换句话说调用了DragController的接口(DragController implements EventListener)从而引发了:

onDragStart() --> onDragEnter() --> onDragOver() --> onDragExit() --> onDrop() --> onDragEnd() 过程。

下一章,继续分析onDragOver() 事件发生了啥

最后由于本人能力有限,有些理解可能存在问题,如果有误欢迎大家指正~~

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
由于源文件大小为99.3MB,所以采用分卷压缩的方式进行上传   《Google Android SDK开发范例大全(第3版)》在上一版的基础上,以Android手机应用程序开发(采用Android SDK 2.3.3)为主题,超过200多个范例全面且深度地整合了手机、网络及服务等多个开发领域,为读者提高程序设计能力提供了很大的帮助。   全书共分11章,主要以范例集的方式来讲述Android的知识点,详细介绍了开发Android的人机交互界面、Android常用的开发控件、Android手机收发短信等通信服务、开发Android手机的自动服务功能和娱乐多媒体功能以及整合Android与Google强大的网络服务等内容。随书光盘中包括了所有范例的程序代码。   《Google Android SDK开发范例大全(第3版)》继承前两版由浅入深的方式,范例总数由原先的160多个增加到了200多个,在用户交互界面、手机控件、交互式通信服务、手机自助服务、娱乐多媒体等方面均增加了相应的范例来介绍新的开发技术,特别是新增加了第11章来专门介绍HTML5技术在Android移动设备里的应用,相信当下两个热门技术的交汇会碰撞出不一样的火花。   《Google Android SDK开发范例大全(第3版)》内容由Android的基础知识到实际开发应用,结构清晰、语言简洁,非常适合Android的初学者和Android的进阶程序开发者阅读参考。 目录   第1章 了解、深入、动手做   1.1 红透半边天的Android   1.2 本书目的及范例涵盖范围   1.3 如何阅读本书   1.4 使用本书范例   1.5 参考网站   第2章 Android初体验   2.1 安装Android SDK与ADT/DDMS   2.2 创建第一个Android项目(Hello Android!)   2.3 Android应用程序架构——从此开始   2.4 可视化的界面开发工具   2.5 部署应用程序到Android手机   第3章 用户人机界面   3.1 更改与显示文字标签   3.2 更改手机窗口画面底色   3.3 更改TextView文字颜色   3.4 置换TextView文字   3.5 取得手机屏幕大小   3.6 样式化的定型对象   3.7 简易的按钮事件   3.8 手机页面的转换   3.9 调用另一个Activity   3.10 不同Activity之间的数据传递   3.11 返回数据到前一个Activity   3.12 具有交互功能的对话框   3.13 置换文字颜色的机关   3.14 控制不同的文字字体   3.15 如iPhone拖动相片特效   3.16 自制计算器   3.17 关于(About)程序信息   3.18 程序加载中,请稍候   3.19 全屏幕以按钮重写   3.20 今晚到哪儿打牙祭   3.21 Android变脸   3.22 打勾显示输入的密码   3.23 Android多语系支持   3.24 判断手机操作系统版本是否允许运行程序   3.25 两个不同的程序彼此调用   3.26 指定安装应用程序迁移至SD卡   3.27 手机动态Layout主题随手势物换迁移   第4章 史上超豪华的手机控件   4.1 EditText与TextView共舞   4.2 设计具有背景图的按钮   4.3 给圣诞老人的信息   4.4 我同意条款   4.5 消费券采购列表   4.6 向左或向右   4.7 专业相框设计   4.8 自定义下拉菜单模式   4.9 动态添加/删除的Spinner菜单   4.10 心爱小宝贝相片集   4.11 快速地搜索手机文件引擎   4.12 按钮也能随单击变换   4.13 具自动提示功能的菜单   4.14 数字及模拟小时钟设计   4.15 动态输入日期与时间   4.16 猜猜红桃A在哪儿   4.17 后台程序运行进度提示   4.18 动态文字排版   4.19 在Activity里显示列表   4.20 以动态列表配置选项   4.21 查找程序根目录下所有文件   4.22 加载手机磁盘里的图片文件   4.23 动态放大缩小ImageView里的图片   4.24 动态旋转图片   4.25 猜猜我在想什么   4.26 离开与关闭程序的弹出窗口   4.27 隐藏式抽屉   4.28 手机桌面上的小玩意   4.29 手机图片搜索管理器   4.30 实时配置桌面上的AppWidget UI Layout   4.31 识别输入装置ID与InputDevice装置   4.32 选取文字的聪明文字联想   4.33 设计与Launcher相同的三页滑动Layout   第5章 交互式通信服务与手机控制   5.1 具有正则表达式的TextView   5.2 ACTION!CALL!拨打电话   5.3 自制发送短信程序   5.4 自制发送E-mail程序   5.5 自制日历手机数据库   5.6 手机振动的节奏   5.7 图文可视化提醒   5.8 状态栏的图标与文字提醒   5.9 搜索手机通讯录自动完成   5.10 取得联系人资料   5.11 制作有图标的文件资源管理器   5.12 还原手机默认桌面   5.13 置换手机后台图   5.14 获取手机现存桌面   5.15 文件资源管理器再进化   5.16 取得目前File与Cache的路径   5.17 打开/关闭WiFi服务   5.18 取得SIM卡内的信息   5.19 调用拨号按钮   5.20 DPAD按键处理   5.21 任务管理器正在运行的程序   5.22 动态更改屏幕方向   5.23 系统设置更改事件   5.24 取得电信网络与手机的相关信息   5.25 建立自己的手写笔画图案   5.26 判断手持设备的WiFi与GPS状态   5.27 手机安全设置管理器   5.28 Shell Script运行Cmmand   5.29 模拟文件下载Notification进度变化与关闭   5.30 取得已安装应用程序列表与安装日期信息   5.31 造假的Toast画面   5.32 剪贴簿管理器   第6章 手机自动服务纪实   6.1 您有一条短信pop up提醒   6.2 手机电池计量还剩多少   6.3 群发拜年短信给联系人   6.4 开始与停止系统服务   6.5 通过短信发送E-mail通知   6.6 手机拨接状态   6.7 有来电,发送邮件通知   6.8 存储卡剩余多少容量   6.9 访问本机内存与存储卡   6.10 实现可定时响起的闹钟   6.11 黑名单来电自动静音   6.12 手机翻背面即静音振动   6.13 指定时间置换桌面背景   6.14 判断发送短信后的状态   6.15 后台服务送出广播信息   6.16 开机程序设计   6.17 双向短信常驻服务   6.18 测量电池温度   6.19 电池电量显示Widget小工具   6.20 取出通讯录数据及多组电话号码   6.21 电子Sensor精确罗盘   6.22 屏幕手写事件判断   6.23 画一颗心,打电话给HONEY   6.24 开启内置闹钟与已安装的应用程序设置   6.25 在SDK开发环境中访问JNI接口   第7章 娱乐多媒体   7.1 访问Drawable资源的宽和高   7.2 绘制几何图形   7.3 手机屏幕保护程序   7.4 用手指移动画面里的照片   7.5 加载存储卡的Gallery相簿   7.6 取得手机内置媒体中的图片文件   7.7 相片导航向导与设置背景桌面   7.8 调整音量大小   7.9 播放mp3资源文件   7.10 播放存储卡里的mp3音乐   7.11 自制录音/播放录音程序   7.12 通过收到短信开始秘密录音   7.13 内置影片播放器载入3gp电影   7.14 自制3gp影片播放器   7.15 相机预览及拍照临时文件   7.16 拍照瞬间启动相机自动对焦   7.17 制作开场动画   7.18 我的手机会说话   7.19 手势触控屏幕控制   7.20 多点触控屏幕事件捕捉   7.21 电流急急棒   7.22 自定义动画按钮事件   7.23 设置录音取样频率与显示录音时间   7.24 EXIF照片信息编辑及读取   7.25 声音音效均衡器、重低音与音场控制   7.26 在自定义的SurfaceView上绘制动画   第8章 当Android与Internet接轨   8.1 HTTP GET/POST传递参数   8.2 在程序里浏览网页   8.3 嵌入HTML标记的程序   8.4 设计前往打开网页功能   8.5 将网络图像网址放入Gallery中显示   8.6 即时访问网络图片文件展示   8.7 手机气象局,实时卫星云图   8.8 通过网络播放mp3   8.9 设置远程下载音乐为手机铃声   8.10 远程下载桌面背景图案   8.11 将手机文件上传至网站服务器   8.12 移动博客发布器   8.13 移动RSS阅读器   8.14 远程下载安装Android程序   8.15 手机下载看3gp影片   8.16 访问网站Login API   8.17 地震速报   8.18 通过Web API动态更改手机Spinner控件   8.19 网络语音搜索   8.20 网络下载过程中显示加载中   8.21 快照WebView加载的网页画面   8.22 网页的放大与缩小   8.23 WebView网页载入中、载入完成样版   8.24 通过内嵌Flash Player播放FLV电影   8.25 WebView网站Session Cookie判断与访问   8.26 文件下载管理器   8.27 手机Socket联机拨号服务器   第9章 Google服务与Android完美整合   9.1 Google账号验证Token   9.2 Google搜索   9.3 前端产生QR Code二维条形码   9.4 以经纬度查找目的地位置   9.5 GPS Google地图   9.6 移动版Google Map   9.7 规划导航路径   9.8 移动设备上的Picasa相册   9.9 随身翻译机   9.10 通过YouTube ID调用YouTube播放影片   9.11 Google Analytics分析手机用户行为   第10章 创意Android程序设计   10.1 手机手电筒   10.2 GPS轨迹记录器   10.3 女性贴身看护   10.4 手机QR Code二维条形码生成器   10.5 Android QR Code二维条形码扫描仪   10.6 上班族今天中午要吃什么   10.7 掷杯筊   10.8 电蚊香   10.9 转转轮盘——Lucky!   10.10 任务管理器   10.11 免费的云端发音字典   10.12 动起来!手机桌面上会动的动态桌布   10.13 正妹墙相簿浏览器   10.14 YouTube影片FLV文件下载器   第11章 HTML5 Mobile应用程序开发   11.1 HTML5 INPUT输入框控制手机输入键盘   11.2 HTML5利用CSS3排版的墙贴相册   11.3 HTML5网页离线数据库   11.4 可拨打电话的HTML连接   11.5 HTML5取回所在经纬度坐标并反查地址   附录A 如何发布程序到Android Market   A.1 支付Google Android Market注册费   A.2 生成签署凭证.keystore金钥档案   A.3 上传*.apk程序到Android Market上   附录B 升级项目至新版SDK 与Honeycomb平板程序部署   B.1 Android SDK版本与项目最低运行版本设置   B.2 升级程序至Honeycomb平板运行
内容简介   《google android sdk开发范例大全(第3版)》在上一版的基础上,以android手机应用程序开发(采用android sdk 2.3.3)为主题,超过200多个范例全面且深度地整合了手机、网络及服务等多个开发领域,为读者提高程序设计能力提供了很大的帮助。    全书共分11章,主要以范例集的方式来讲述android的知识点,详细介绍了开发android的人机交互界面、android常用的开发控件、android手机收发短信等通信服务、开发android手机的自动服务功能和娱乐多媒体功能以及整合android与google强大的网络服务等内容。随书光盘中包括了所有范例的程序代码。    《google android sdk开发范例大全(第3版)》继承前两版由浅入深的方式,范例总数由原先的160多个增加到了200多个,在用户交互界面、手机控件、交互式通信服务、手机自助服务、娱乐多媒体等方面均增加了相应的范例来介绍新的开发技术,特别是新增加了第11章来专门介绍html5技术在android移动设备里的应用,相信当下两个热门技术的交汇会碰撞出不一样的火花。    《google android sdk开发范例大全(第3版)》内容由android的基础知识到实际开发应用,结构清晰、语言简洁,非常适合android的初学者和android的进阶程序开发者阅读参考。 目录 《google android sdk开发范例大全(第3版)》 第1章 了解、深入、动手做 1 1.1 红透半边天的android 2 1.2 本书目的及范例涵盖范围 3 1.3 如何阅读本书 4 1.4 使用本书范例 5 1.5 参考网站 6 第2章 android初体验 7 2.1 安装android sdk与adt/ddms 8 2.2 创建第一个android项目(hello android!) 11 2.3 android应用程序架构——从此开始 15 2.4 可视化的界面开发工具 18 2.5 部署应用程序到android手机 19 第3章 用户人机界面 22 3.1 更改与显示文字标签 23 3.2 更改手机窗口画面底色 24 3.3 更改textview文字颜色 26 3.4 置换textview文字 28 3.5 取得手机屏幕大小 29 3.6 样式化的定型对象 30 .3.7 简易的按钮事件 32 3.8 手机页面的转换 34 3.9 调用另一个activity 37 3.10 不同activity之间的数据传递 41 3.11 返回数据到前一个activity 44 3.12 具有交互功能的对话框 49 3.13 置换文字颜色的机关 51 3.14 控制不同的文字字体 52 3.15 如iphone拖动相片特效 54 3.16 自制计算器 56 3.17 关于(about)程序信息 58 3.18 程序加载中,请稍候 61 3.19 全屏幕以按钮重写 63 3.20 今晚到哪儿打牙祭 64 3.21 android变脸 67 3.22 打勾显示输入的密码 69 3.23 android多语系支持 71 3.24 判断手机操作系统版本是否允许运行程序 72 3.25 两个不同的程序彼此调用 75 3.26 指定安装应用程序迁移至sd卡 78 3.27 手机动态layout主题随手势物换迁移 79 第4章 史上超豪华的手机控件 84 4.1 edittext与textview共舞 85 4.2 设计具有背景图的按钮 86 4.3 给圣诞老人的信息 88 4.4 我同意条款 90 4.5 消费券采购列表 92 4.6 向左或向右 94 4.7 专业相框设计 96 4.8 自定义下拉菜单模式 99 4.9 动态添加/删除的spinner菜单 102 4.10 心爱小宝贝相片集 104 4.11 快速地搜索手机文件引擎 107 4.12 按钮也能随单击变换 109 4.13 具自动提示功能的菜单 110 4.14 数字及模拟小时钟设计 112 4.15 动态输入日期与时间 114 4.16 猜猜红桃a在哪儿 117 4.17 后台程序运行进度提示 121 4.18 动态文字排版 124 4.19 在activity里显示列表 128 4.20 以动态列表配置选项 130 4.21 查找程序根目录下所有文件 134 4.22 加载手机磁盘里的图片文件 137 4.23 动态放大缩小imageview里的图片 139 4.24 动态旋转图片 142 4.25 猜猜我在想什么 145 4

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值