布局类型
-
FrameLayout 帧布局 最简单的布局
叠放 gravity
-
LinearLayout 线性布局 左右排列 或 上下排列
横向或者纵向 orientation horizontal vertical
-
RelativeLayout 相对布局 A B 之间有相对关系
-
ConstraintLayout 约束布局
-> VIewGroup -> View
LinearLayout
线性布局(横向或者纵向)
-
orientation:设置排列方式 默认横向
-
Horizontal 横向排列
-
Vertical 纵向排列
-
-
weight:设置子控件占据父容器剩余空间的位置
FrameLayout
帧布局 不存在层级嵌套
第一个在最内层,后添加的在外面一层
-
外间距:margin 控件和父容器之间的额间距
-
内间距: padding 控件和自己内容之间的间距
ViewGroup.LayoutParams
- width
- height
- MATCH_PARENT
- WARP_CONTENT
View.MarginLayoutPrarms : ViewGroup.LayoutParams
- leftMargin - startMargin : 统一表示各个国家的语言顺序
- topMargin
- rightMargin - endMargin
- bottomMargin
FrameLayout.LayoutParams : ViewGroup.MarginLayoutPrarms
- gravity
LinearLayout.LayoutParams
-
gravity : 父容器使用 设置容器内部子控件的对齐方式
left start top right end bottom center center_horizontal center_vertical
-
layout_gravity : 子控件使用 设置子控件在父控件中的相对位置,如果父容器使用了gravity 子控件的优先级最高
-
weight :权重 设置子控件占据父容器剩余空间的比例 1:1 平分
RelativeLayout 相对布局
RelativeLayout.LayoutParams -> ViewGroup>MarginLayoutParams -> View.LayoutParams
- 和父容器之间的位置关系
- ALIGN_PARENT_BOTTOM 和父容器的底部对齐
- ALIGN_PARENT_END 和父容器的右边对齐
- ALIGN_PARENT_LEFT 和父容器的左边对齐
- ALIGN_PARENT_RIGHT 和父容器的右边对齐
- ALIGN_PARENT_START 和父容器的左边对齐
- ALIGN_PARENT_TOP 和父容器的顶部对齐
- CENTER_IN_PARENT 在父容器的中心位置
- 和兄弟控件之间的位置关系
- ABOVE 在某个控件的上面
- BELOW 在某个控件的下面
- END_OF 在某个控件的右边
- START_OF 在某个控件的左边
- ALIGN_BASELINE 和某个控件的基准线对齐
- ALIGN_BOTTOM 和某个控件底部对齐
- ALIGN_END 和某个控件右边对齐
- ALIGN_START 和某个控件右边对齐
- ALIGN_TOP 和某个控件顶部对齐
ConstraintLayout 约束布局
- Start_toStartOf: 左边和某一个控件的左边对齐 父控件:parent 子控件:id
- End_toEndOf
- Match_constraint 匹 配约束,尺寸由约束来确定 = 0dp
- Top_toTopOf
- Bottom_toBottomOf
界面布局相关知识
-
什么是View?
- 视图 看得见的东西 单个View
-
什么是ViewGroup?
-
容器 可以容纳很多个View 包含多个视图
-
每种容器都有自己独有的摆放方式
-
XML标记语言
<LinearLayout 标签的属性> <!--子控件--> <Button></Button> -> <Button 控件的属性/> <!--如果没有子控件,可以简化为以下的形式--> <Button 控件的属性 /> </LinearLayout>
-
设置xml文件中控件的尺寸
android:layout_width=“match_parent” 设置控件的宽度
android:layout_heiht=“match_parent” 设置控件的高度
- match_parent : 匹配父容器 和父容器的宽度或者高度一致
- warp_content : 包裹内容 由内容来定
- 固定值 android:layout_heiht=“100dp”
- 容易出错的情况:
- 父容器使用warp_content, 子控件使用match_content, 导致无法确定控件的尺寸
- 容易出错的情况:
-
-
界面显示:
- 1.计算子控件和容器的尺寸
- 2.渲染
- 3.加载
-
-
如何找到xml中的控件
- 给控件添加 id android:id = “@+id/black_view” blackView
- 命名时如果有多个单词组成,只用_连接,小写
- 给控件添加 id android:id = “@+id/black_view” blackView
-
如何确定一个控件的约束是否完整
- 确定一个控件在界面中的位置由4个元素组成(x,y,width,height)
GuideLine 导航线
-
Vertical
-
Horizontal
-
固定值
-
百分比
-
Chains 横向和纵向链接
- chain style
- spread(默认) 均分
- spread inside 中间均分
- packed 中间挤满,两边均分
Ratio 比例 宽:高
设置宽高比
w:宽度不变
h:高度不变
如何更改控件的id
选中控件 -> 找到属性里面的id -> 改为新的id -> 弹出的提示框中 scope: current file -> refactor
图片资源保存到什么位置?
res -> drawable
图片资源名称只能使用小写字母,如果有多个单词组成使用_ eg:icon_cat
显示图片使用ImageView
图片拉伸:scaleType:fitXY centerCrop
如何引用系统的资源 -> 整数
每一个资源添加添加到项目中,会给它们分配一个整数,这个整数用来唯一表示这个资源
如何通过代码给图片设置资源
imageView.setImageResource(R.drawable.hema)
如何控制一个控件的显示和隐藏
visibility:
- View.VISIBLE 控件可见
- View.INVISIBLE 控件还在,但不可见
- View.GONE 控件消失
怎么获取容器中的所有控件
-
获取容器
val container = findViewById<ConstraintLayout>(R.id.container)
-
使用childCount获得子控件数,通过getChildAt 获得每个子控件,保存在数组中
//保存所有的图片控件 private val allViewList = mutableListOf<ImageView>() //获取所有的图片控件 for (i in 0 until container.childCount) { val imageView = container.getChildAt(i) as ImageView allViewList.add(imageView) }
怎么将数据关联起来或将多个数据封装为整体
将数据封装到一个类中
/**
* 用来封装每一个图标的信息
* 图片控件
* 图片资源id
*/
class Model(
val imageView: ImageView,
val resId: Int
)
打乱数组里的元素
使用shuffle方法
//打乱数组的顺序
allViewList.shuffle()
怎么延时操作
使用定时器进行延时
Timer().schedule(object : TimerTask() {
override fun run() {
TODO("Not yet implemented")
}
}, 500)
注意:1.安卓中主线程是用来显示界面的,子线程没有权限操作主界面
2.注意可能出现的空指针异常
使用runOnUiThread切换到主线程
子线程中不能操作UI相关的事情,需要切换到主线程才能进行操作
使用翻转动画
- 添加翻转动画
//折叠翻转动画
@SuppressLint("Recycle")
private fun addFilpAnimation(target: View) {
//绕y轴旋转180度
ObjectAnimator.ofFloat(target, "rotationY", 0f, 180f).apply {
duration = 500
start()
}
}
- 调用翻转动画
addFilpAnimation(lastSelectedModel!!.imageView)
- 使用定时器能让动画更加流畅
else {
//图片不相同
runOnUiThread {
Timer().schedule(object : TimerTask() { // 250毫秒以后再更改图片背景 ->
override fun run() {
runOnUiThread { //和UI相关的要放在主线程中运行
model.imageView.setImageResource(R.drawable.icbg)
lastSelectedModel!!.imageView.setImageResource(R.drawable.icbg)
lastSelectedModel = null //不要提前设置为null,会出现空指针异常
}
}
}, 250)
addFilpAnimation(model.imageView) //开启翻转动画
addFilpAnimation(lastSelectedModel!!.imageView)
}
}
timer = null //定时器任务完成后,再空
}
}, 500)
MVP设计模式
- M Model 模型层 封装数据
- V View -> Activity和其对应的layout布局文件 为了与layout布局文件交互 ,需要实现定义好的接口
- P Persenter 展示层 通过接口中的属性和方法来和View进行交互
数据类
只有属性的类
data class LinkModel(
val imageView: ImageView,
val resId: Int
)
MVP中接口里面定义什么方法?
数据改变了,需要通知Activity做什么,就定义什么方法
interface ILinkView {
//通知界面切换图片
fun changeImage(target: ImageView, resId: Int)
//通知界面更改显示的状态
fun changeVisibleState(target: ImageView, visible: Boolean)
}
实现围绕y轴翻转动画、
of后的类型是什么,看动画改变的是什么属性
fun startFilpAnimation(view: View) {
//of后的类型,看动画改变的是什么属性
ObjectAnimator.ofFloat(
view, //给谁添加动画
"rotationY", //改变什么属性
0f, //改变的值
180f
).also {
it.duration = 500L //动画持续的时间
it.start()
}
触摸事件
界面和布局文件是怎么关联的?
-
layout -> activity_main.xml 负责静态界面的布局
-
Activity -> MainActivity 负责界面的具体显示和生命周期
-
通过setContentView(布局文件的id) 将layout布局文件显示到Activity中
-
内容和界面的关系
-
老版本:一个Activity管理一个界面 (一台电视机,只播放一部电影)
-
新版本:全局只有一个Activity,使用Fragment管理每一个界面,切换界面只需要切换内容即可(一个电视机可以切换很多台)
-
界面的层次
- 界面的层级关系
window -> status bar -> decor view -> action bar + Container -> 自己的内容
- 窗口 Window
- 状态栏 Status bar
- 装饰层 Decor View
- 导航栏 Action bar
- 内容 Container
如何监听触摸事件
-
重写Activity提供的onTouchEvent方法
-
系统使用MotionEvent将触摸的事件封装起来
- getAction 获得具体的事件
- ACTION_DOWN 刚刚触摸到界面/按下去那刻
- ACTION_MOVE 手在屏幕中连续滑动
- ACTION_UP 抬手/离开屏幕那一刻
- ACTION_CANCEL 被其他应用打断 取消这个事件
- getX() getY() 获取触摸点的坐标
-
事件的传递方式
-
触摸事件的传递方式
- 触摸事件是由Windows来监听,并且负责传递
- decorView -> content -> 容器 -> 子容器 -> 子控件1 -> 子控件2 -> 子控件3
-
onTouchEvent方法的返回值
- true 表示自己把这个事件消费了/处理了 后面就不会继续传递
- false 表示自己没有消费 事件继续传递
-
这个方法会被多次重复调用
- 尽量不要在这个方法内部创建共享的对象或者代码块
-
查找关联界面中的控件时容易犯的错
-
界面还没有关联,就提前去获取界面里面的控件
-
必须在界面后访问
获取actionBar、statusBar、内容的高度
-
event.x event.y 是相对与window窗口的坐标位置
- 需要将坐标转化为相对于内容视图 event.y - 状态栏 - 导航栏
-
Resource:管理程序的资源 color string drawable theme style
- DisplayMetrics 管理屏幕显示的参数
- widthPixels 屏幕的宽度
- heightPixels 屏幕的高度(可用的宽度:导航栏 + 内容容器) (不包括状态栏)
- density 屏幕的密度 (每一个物理点显示多少个)
- 1x 一个点显示1个像素 160dpi 160px
- 2x 一个点显示2个像素 160dpi 320px
- 3x 一个点显示3个像素 160dpi 480px
- DisplayMetrics 管理屏幕显示的参数
-
Rect封装矩形区域 left top right bottom
-
Point 封装坐标 x y
-
-
获取可用屏幕的高度
//获取可用的屏幕高度 导航栏 + 内容容器 val availableHeight = resources.displayMetrics.heightPixels
- 获取状态栏的高度
//获取decorView的矩形区域 left right top bottom val rect = Rect() window.decorView.getWindowVisibleDisplayFrame(rect) //状态栏的高度 val statusbarHeight = rect.top val actionBarHeight = window.findViewById<ViewGroup>(Window.ID_ANDROID_CONTENT).top
- 获取内容的高度
//获取内容的高度 val actionBarHeight = window.findViewById<ViewGroup>(Window.ID_ANDROID_CONTENT).top
-
-
当给父容器添加setOnTouchEvent事件,得到的x,y坐标就是相对于父容器的了
-
onCreat方法里面不能直接获取空间的尺寸 (宽、高)
-
当系统需要启动界面前,先调用这个界面的onCreate方法,这个方法里面就是确定这个界面有哪些控件,当界面确定之后,再告诉系统去渲染(绘制)界面,渲染完毕,才展现在我们面前,所以在渲染之前是无法获取控件的尺寸的
-
为什么在onCreat方法里面可以获取尺寸?
- 当你去触摸界面时,界面已经展现在我们眼前了(绘制完毕了),尺寸位置已经确定
-
如果想在onCreate方法里面获取控件的尺寸,只能监听控件即将被绘制的事件
//监听控件即将被绘制的事件 window.decorView.viewTreeObserver.addOnPreDrawListener { mHeight = barHeight() true //继续绘制 false 停止绘制 }
-