引子

最近,在做产品的需求的时候,遇到 PM 要求在某个按钮上添加一个新手引导动画,引导用户去点击。作为 RD,我哗啦啦的就写好相关逻辑了。自测完成后,提测,PM Review 效果。

看完后,PM 提了个问题,这个动画效果范围能不能再大一点?PM 解释到按钮本身大小不是很大,会导致引导效果不够明显,也会导致用户的点击欲望不够。我想了想,似乎很有道理啊,但是这个能做到吗?

答案是当然可以呢。如果单纯从现在的布局上去将动画的尺寸去扩大,得改变原本的布局。这个引导只出现几次,为了引导,而去改动原有的布局,个人觉得改动还是蛮大的。不值得!

于是想用 clipChildren 属性来试着让 子 view 突破父布局,但是这样同样会影响其他子 view,也不好去与按钮的中心进行定位。

那还有没有其他尽可能不去改动原有布局就可以实现的方案呢?

有的!

准备知识

相信大家都对下面这段代码会很熟悉:

protected voidonCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

}

这段代码执行后,将 activity_main 这个布局添加到了 DecorView 。对于 activity 与 DecorView 之间的关系,大家可以看这篇文章:

DecorView 是一个应用窗口的根容器,它本质上是一个 FrameLayout。DecorView 有唯一一个子 View,它是一个垂直 LinearLayout,包含两个子元素,一个是 TitleView( ActionBar 的容器),另一个是 ContentView(窗口内容的容器)也是一个 FrameLayout(android.R.id.content),平常用的 setContentView 就是设置它的子 View 。后面我们就是在 ContentView 上做文章。

另外,对于 FrameLayout,他的子 view 如果没有指定 Gravity 的话,那么就会堆积再左上角,谁是后面添加的谁在上面。其实使用也可以下面两个方法来决定放置的位置:

public void setX(floatx) {

setTranslationX(x-mLeft);

}public void setY(floaty) {

setTranslationY(y-mTop);

}

可以发现这两个方法其实是都通过设置平移的偏移的量来实现的。这样我们就可以指定 View 所显示的位置的。

那如何去获取 PM 需求中所要求的位置呢?如果这个按钮是 wrap_content 的,按钮的宽度是无法确定的?那就只能拿到按钮对应的 View 实例,通过该实例就可以获取到按钮的宽高。

获取 view 的显示位置

按钮的宽高知道后,结合前面介绍的两个设置显示位置方法,有些人应该已经猜到要怎么做了。如果能够知道按钮的显示位置,这时候只要调用这两个方法,就可以将动画 view 显示位置确定下来。那我要怎么去获取按钮的显示位置呢。下面就得介绍另一个方法呢。

public final booleangetLocalVisibleRect(Rect r) {final Point offset = mAttachInfo != null ? mAttachInfo.mPoint : newPoint();if(getGlobalVisibleRect(r, offset)) {

r.offset(-offset.x, -offset.y); //make r local

return true;

}return false;

}

在来看看 getGlobalVisibleRect 的实现,

public booleangetGlobalVisibleRect(Rect r, Point globalOffset) {int width = mRight -mLeft;int height = mBottom -mTop;if (width > 0 && height > 0) {

r.set(0, 0, width, height);if (globalOffset != null) {

globalOffset.set(-mScrollX, -mScrollY);

}return mParent == null || mParent.getChildVisibleRect(this, r, globalOffset);

}return false;

}

简单来说,就是 rect 是 View 的宽高和 View 的偏移量综合的结果,具体计算过程咱就不纠结了,下面说下每个数字代表的含义:

其中对于 getLocalVisibleRect 来说:

rect.left 大于0,表示左边已经处于不可见,否则是等于0;

rect.top 大于0,表示上边已经处于不可见,否则是等于0;

rect.right 小于 View 的宽度,表是处于不可见,否则是等于 View 的宽度;

rect.bottom 小于 View 的高度,表是处于不可见,否则是等于 View 的高度;

View 的可见高度 = rect.bottom - rect.top;View 的可见宽度 = rect.right - rect.left;

对于 getGlobalVisibleRect 来说:就是其在屏幕当中的位置。具体可见下面的 gif 图

相信大家在有了上述知识基础之后,就知道要怎么做了。下一步就是实战。

实践

目标:将一个 imageView 居中显示在一个 TextView 上面。

步骤:

获取锚点 TextView 实例对象;

根据实例对象获取 ContentView;

根据 ContentView 和 TextView 的显示位置确定 TextView 在 ContentView 中的位置;

将 imageView 添加到 ContentView 上,根据位置调整位置。

经过上面四步即可将一个 view 添加到任何一个位置呢。

最终实现效果:

源码

下面是具体实现代码,为了便于该逻辑的重复利用,我稍微进行了封装。采用的是 builder 模式,虽然我的变量比较少,但是真的当封装的功能足够强大的时候,需要用到属性就会很多,这时候就能体会到 builder 模式的强大呢。比如可以支持设置 Gravity,支持传入不同的 targetView。现在我是直接 imageView 写死的。

protected voidonCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

mText=findViewById(R.id.text);

mText.setClickable(true);

mText.setOnClickListener(newView.OnClickListener() {

@Overridepublic voidonClick(View v) {

showCenterView(mText);

}

});

}public voidshowCenterView(View view) {

FloatingManager.Builder builder=FloatingManager.getBuilder();

builder.setAnchorView(view);

FloatingManager manager=builder.build();

manager.showCenterView();

}

下面是 采用的是 builder 模式简单封装的一个管理类:

public classFloatingManager {privateView mAnchorView;privateString mTitle;privateViewGroup mRootView;public staticBuilder getBuilder() {return newBuilder();

}static classBuilder {privateFloatingManager mManager;publicFloatingManager build() {returnmManager;

}publicBuilder() {

mManager= newFloatingManager();

}publicBuilder setAnchorView(View view) {

mManager.setAnchorView(view);return this;

}publicBuilder setTitle(String title) {

mManager.setTitle(title);return this;

}

}public voidsetAnchorView(View view) {

mAnchorView=view;

}public voidsetTitle(String title) {this.mTitle =title;

}public voidshowCenterView() {if (mAnchorView == null) {return;

}

Activity activity=(Activity) mAnchorView.getContext();

mRootView=activity.findViewById(android.R.id.content);

Rect anchorRect= newRect();

Rect rootViewRect= newRect();

mAnchorView.getGlobalVisibleRect(anchorRect);

mRootView.getGlobalVisibleRect(rootViewRect);//创建 imageView

ImageView imageView = newImageView(activity);

imageView.setImageDrawable(activity.getResources().getDrawable(R.drawable.ic_launcher));

mRootView.addView(imageView);//调整显示区域大小

FrameLayout.LayoutParams params =(FrameLayout.LayoutParams) imageView.getLayoutParams();

params.width= 100;

params.height= 100;

imageView.setLayoutParams(params);//设置居中显示

imageView.setY(anchorRect.top - rootViewRect.top + (mAnchorView.getHeight() - 100) / 2);

imageView.setX(anchorRect.left+ (mAnchorView.getWidth() - 100) / 2);

}

}

其实添加以后,还得考虑事件的点击之类的,比如可以通过设置回调,当点击引导动画的时候,先隐藏动画,再去主动促发按钮的点击逻辑等。

还有就是上面写的管理类存在重复添加 imageView 的逻辑漏洞,应该在每次添加前都做一个检查,确保不会重复添加。

到这里,整个知识点就讲完了。

android 在指定位置添加布局,Android 如何动态添加 View 并显示在指定位置。相关推荐

  1. Android 在布局容器中动态添加控件

    这里,通过一个小demo,就可以掌握在布局容器中动态添加控件,以动态添加Button控件为例,添加其他控件同样道理. 1.addView 添加控件到布局容器 2.removeView 在布局容器中删掉 ...

  2. android 文本分页显示,Android查看带有列表视图的分页器并动态添加文本

    我在一个分片活动中有一个Viewpager,它有一个带有编辑文本和发送按钮的botton框架. 在片段布局中,我有一个ListView,并在片段中附加了一个适配器.现在我正在实现从片段中的Parent ...

  3. android仿微信发布动态功能,Android GridView扩展仿微信微博发图动态添加删除图片功能.pdf...

    Android GridView扩扩展展仿仿微微信信微微博博发发图图动动态态添添加加删删除除图图片片功功能能 这篇文章主要为大家详细介绍了Android GridView扩展仿微信微博发图动态添加删除 ...

  4. android动态添加控件在指定位置,Android 如何动态添加 View 并显示在指定位置。

    引子 最近,在做产品的需求的时候,遇到 PM 要求在某个按钮上添加一个新手引导动画,引导用户去点击.作为 RD,我哗啦啦的就写好相关逻辑了.自测完成后,提测,PM Review 效果. 看完后,PM ...

  5. android布局添加布局,Android中添加布局和初始化布局总结

    在android中布局很重要,下面总结下布局的三种形式 ①.在Activity的onCreate()方法中进行添加比如:setContentView(R.layout.activity_main); ...

  6. Android UI设计 下拉菜单Spinner用法 动态添加删除Spinner菜单项

    Spinner是一种下接菜单,类似HTML中的select标签,点击后弹出一个对话框,显示几个供选择的选项,手机屏幕大小有限,如果都用RadioGroup单选按钮,会占用很大的空间.今天的例子最终效果 ...

  7. android仿微博发动态,Android GridView扩展仿微信微博发图动态添加删除图片功能

    在平时的开发中,我们会看到不管是微信发朋友圈照片还是微博发布新鲜事,添加图片的时候都是选完后面还有个+号再去选择图片,这样的话比较方便用户去添加图片,有的右上角还有个-号方便用户去删除图片,而一般用户 ...

  8. Android GridView扩展仿微信微博发图动态添加删除图片

    转载请注明出处:http://blog.csdn.net/linglongxin24/article/details/53034123 [DylanAndroid的csdn博客] 在平时的开发中,我们 ...

  9. js动态添加样式和jQuery动态添加样式

    0.style方式添加样式 DOM对象.style.color = 'red'DOM对象.style.display = 'block/none' //让元素显示/隐藏 1.js动态添加样式 1.添加 ...

最新文章

  1. python excel数据框_使用python pandas使用新数据框附加现有excel表
  2. 华为NPU卡ubuntu(无网络连接情况)驱动安装记录
  3. python递归_纯Python递归计算行列式
  4. 清华成立“脑与智能”和“未来”两大实验室,跨学科AI深度融合
  5. 深入理解CSS线性渐变linear-gradient
  6. MySQL python update 语句
  7. 个人信息保护呼吁“基本法”
  8. 嵌入式Linux入门7:kernel移植
  9. 【报告分享】2020快手短视频直播电商营销增长宝典.pdf(附下载链接)
  10. [unity3d]自定义鼠标指针
  11. js时间与毫秒互相转换
  12. 使用ipmitool命令检测电源模块状态
  13. 使用app loader上传iOS应用
  14. SQL Server 2008 存储结构之DCM、BCM
  15. Python实现:超分子化学的建模------如何操控客体分子穿过主体分子和计算该过程能量变化(高斯(Gauss)输入文件为例,一键批量处理)
  16. 研发项目wbs分解简单案例_wbs分解案例
  17. 论文笔记:Reciprocal Multi-Layer Subspace Learning for Multi-View Clustering
  18. 怎么判断两个多项式互素_多项式互素的等价条件
  19. android中高级面试题,Android高级工程师必看系列
  20. Tomcat服务器的安装使用

热门文章

  1. 学习使用c++编写opencv的一些记录
  2. kafka消费者脚本无法启动问题
  3. php3源码分析,ThinkPHP3.1.3源码分析(一) 入口文件分析
  4. 无人驾驶入门(雷达、定位和高精地图)
  5. 随想录(mac下c、c++的编译方法)
  6. python编程(multiprocessing库)
  7. 神奇的go语言(image网站开发)
  8. ati catalyst linux 视频解码,如何为ATI/APU用户编译编译安装XBMC,硬解播放视频
  9. linux scrapy 定时任务_2019Python学习教程(全套Python学习视频):Scrapy爬虫框架入门...
  10. 重启php软重启_php-fpm 的重启方法(php7.3)