0%

react是将数据渲染为HTML视图的开源JS工具。

react的优势:原生js或jq操作的是真实的DOM(Document Object Model,文档对象模型,可指html模型中的对象),操作频繁且无法复用。而react是操作虚拟DOM,且有着优秀的diffing(different)算法,易于比较出有差异的一部分、复用真实DOM、最小化页面重绘。

主要先分2步,创建虚拟DOM。然后把虚拟DOM渲染到页面(其实就是虚拟DOM被react最终转换为真实DOM显示上页面)

js

    <script type="text/babel">//说明用的语言不是js是jsx并且使用babel翻译
    //1.创建虚拟DOM
    const VDOM = (
    <h1>hello,react</h1>
    )
    //2.渲染虚拟DOM到页面上
    ReactDOM.render(VDOM,document.getElementById('test'))
    </script>

虚拟DOM创建2种书写方式。js或者jsx。但jsx其实就是js(创建DOM太繁琐)的语法糖,就是比js创建方式更加便捷的方式,底层还是用了js创建。

jsx的语法:

//不可出现双{所以以下用代码框

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
1.定义虚拟DOM时,不要写引号。

2.标签中混入js表达式时要用{}。

3.样式类名的指定不要用class,应该使用className

4.内联样式,要用style={{key:value,key:value}}形式,value可以是字符串或对象。

5.虚拟DOM只能有一个根标签,想写多个只能是用个div啥的包起来。

6.jsx中标签必须闭合。

7.jsx的标签首字母

​ (1).若小写字母开头,则将该标签转为html同名元素,若html中无该标签对应同名元素则报错。

​ (2).若大写字母开头,react就去渲染对应的组件,若组件没定义则报错。


实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<!DOCTYPE html>
<html>

<head>
<meta charset="UTF-8">
<title>hello_react</title>
<style>
.title {
background-color: orange;
}
</style>
</head>

<body>
<!--准备测试容器-->
<div id="test"></div>

<script type="text/javascript" src="./js/react.development.js"></script>
<script type="text/javascript" src="./js/react-dom.development.js"></script>
<script type="text/javascript" src="./js/babel.min.js"></script>
<script type="text/babel">//说明用的语言不是js是jsx并且使用babel翻译
//1.创建虚拟DOM
const val = "hello,react"
const VDOM = (
<div>
<h1 className="title" style={{color:"red"}}>{val}</h1>
<h1>hello,react</h1>
</div>
)

//2.渲染虚拟DOM到页面上
ReactDOM.render(VDOM,document.getElementById("test"))
</script>
</body>

</html>

注意:{}里面只能是js表达式,不能是js语句(代码)。

js表达式:(其实就是有返回值的就是表达式)

1.a 变量

2.a+b 变量运算结果

3.demo(0) 方法

4.arr.map() 数组的使用方法

5.function test(){} 直接定义的方法也是会有返回值的,就是方法本身

js语句(代码):

1.if(){}

2.for(){}

3.switch(){case:}

数组map()方法的定义:map() 方法返回一个由原数组中的每个元素调用一个指定方法后的返回值组成的新数组。

map使用实例:

1
2
3
4
5
6
7
8
9
10
const data=["a","b","c"]
const VDOM = (
<ul>
{
data.map((item,index)=>{
return <li key={index}>{item}</li>//需要为每个节点赋予一个唯一的key,用于diffing算法。
})
}
</ul>
)

react面向组件开发:

模块一般在前端仅指js模块,也就是个完成特定功能的js程序,一般就是个.js文件。

而组件则是比模块更深,是指实现某局部功能效果的代码和资源集合包括html/css/js/image等等。

模块和组件都是为了复用代码,简化编码,提高开发效率。

模块化:如果此应用的js都是以模块来编写,则此应用为模块化应用。

组件化:如果此应用是以多组件形式完成,则此应用为组件化应用。

var,const,let

const和let的作用域一样,是块{}作用域(即{}内定义的不可被外部访问到)。且const不允许修改其所指向的内存地址中的内容,也就是当const一个基本类型(数值、字符串、布尔值)时,基本类型的值就存在指向的内存空间中,所以值不可修改。但当const一个复合类型例如数组、对象时,const变量指向的仅是那个复合类型的指针,所以不然让其重新指向但可以修改指向的指针中的值(即可修改对象的变量)。const 必须初始化赋值。

var定义的变量可以修改,如果不初始化会输出undefined,不会报错。var的作用域为函数级作用域,其实就是和平时java里使用的变量一样,定义在哪就决定是全局变量或函数的局部变量,且会去找作用域最近的同名变量(即函数内部使用会优先去找函数内部是否有同名变量,没有才去找全局同名变量)。且var有变量提升机制(即使用var在函数或全局内任何地方声明变量相当于在其内部最顶上声明它,应注意提升的只是函数的声明语句,赋值还是在原来位置所以之前使用还是会undefined)

let为块级作用域,其余和var的使用相似。

react面向组件开发:

函数式组件

1
2
3
4
5
6
7
8
9
10
11
<script type="text/babel">//说明用的语言不是js是jsx并且使用babel翻译 
//1.用function方式创建组件
function MyComponent(){
//会显示undefined,详见下文 “需注意”的三
console.log(this)
//组件可以包含很多东西,但最少最少也得有个骨架即html
return <h2>Hello</h2>
}
//2.渲染虚拟DOM到页面上
ReactDOM.render(<MyComponent/>,document.getElementById("test"))
</script>

需注意:(适用于简单组件)

一.定义的函数一定得大写首字母,因为jsx的语法规定:

1.标签首字母小写则直接转换为html同名元素。

2.标签首字母大写react才会去作为组件渲染。

二.传给react render时得用标签形式去传,不能直接调用方法,这样react不会将其渲染为组件。

三.关于this的指向:在text/babel里的代码会由babel翻译,而babel本身会启动严格模式,会禁止自定义函数里的this指向windows。

执行render发生的事:

1.react解析标签,找到组件。

2.发现组件是函数定义则调用此函数,将返回的虚拟DOM转化为真实DOM显示在页面上。

类式组件

函数式组件的组件名就是函数名,类式组件的组件名就是类名。所以renact render时传入的标签为类名

1
2
3
4
5
6
7
8
9
10
11
<script type="text/babel">//说明用的语言不是js是jsx并且使用babel翻译 
//1.用类方式创建组件
class MyComponent extends React.Component {
render(){
console.log(this)
return <h2>Hello lei</h2>
}
}
//2.渲染虚拟DOM到页面上
ReactDOM.render(<MyComponent/>,document.getElementById("test"))
</script>

需注意:(适用于简单组件)

一.类式组件一定要继承React.Component类

二.不一定要写构造方法,但一定要重写其render方法(此方法和渲染到页面上的render没啥关系就是同名而已),且此方法返回组件。

执行render发生的事:

1.react解析标签,找到组件。

2.发现组件是类定义则new出该类的实例对象(即为组件实例对象),通过该实例对象调用其原型上的render方法。

3.将render方法返回的虚拟DOM转化为真实DOM显示在页面上。

什么是简单组件什么是复杂组件?

有state状态就是复杂组件,没有即为简单组件。

state是什么?

组件实例的三大核心属性

state

状态里存有数据,可根据其中数据驱动页面的变化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<script type="text/babel">//说明用的语言不是js是jsx并且使用babel翻译 
//1.用构造方法给state赋值
class Weather extends React.Component {
constructor(props){
super(props)
this.state={isHot:false}
}

render(){
//读取state的值
return( <h1>天气很{this.state.isHot?"炎热":"凉爽"}</h1>)
}
}
//2.渲染虚拟DOM到页面上
ReactDOM.render(<Weather/>,document.getElementById("test"))
</script>

jsx里的点击事件:

原生点击事件有三种实现方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<body>
<button id="btn1">按钮1</button>
<button id="btn2">按钮2</button>
<button onclick="demo()">按钮3</button>

<script type="text/javascript" >
const btn1 = document.getElementById('btn1')
btn1.addEventListener('click',()=>{
alert('按钮1被点击了')
})

const btn2 = document.getElementById('btn2')
btn2.onclick = ()=>{
alert('按钮2被点击了')
}

function demo(){
alert('按钮3被点击了')
}

</script>
</body>

箭头函数:ES6的新规范(其实就是为了简化代码的匿名函数,即箭头函数可以替代函数表达式但是不能替代函数声明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
1.无花括号和return的,只能包含一个表达式并且会将其作为返回值。当只有一个传入参数使,也可以再省略小括号()  
x => x + 6

相当于

function(x){
return x + 6;
}

//es5
var fn = function(a, b){return a+b}
//es6 直接被return时候可以省略函数体的括号
const fn=(a,b) => a+b;


2.有花括号和return的,可以包含多条语句
()=>{}
相当于
function(){}


//es5
var foo = function(){
var a=20;
var b= 30;
return a+b;
}
//es6
//也可以用于已声明的函数的赋值
const foo=()=>{
const a= 20;
const b=30;
return a+b
}

// 注意这里 箭头函数可以替代函数表达式但是不能替代函数声明

但在这三种方式中jsx推荐使用第三种。没那么繁琐。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<script type="text/babel">//说明用的语言不是js是jsx并且使用babel翻译 
//1.用构造方法给state赋值
class Weather extends React.Component {
constructor(props){
super(props)
this.state={isHot:false}
}
render(){
//读取state的值
return( <h1 onClick={changeWeather}>天气很{this.state.isHot?"炎热":"凉爽"}</h1>)
}
}
//2.渲染虚拟DOM到页面上
ReactDOM.render(<Weather/>,document.getElementById("test"))

function changeWeather (){

console.log("sbwy")
}

</script>

需要注意:jsx的方法名和原生不同,不是onclick是onClick。且对于onClick方法的赋值语句的花括号内得是方法名不可加括号,否则会直接运行changeWeather方法并将changeWeather方法的返回值给onClick了,应该给的是changeWeather这个方法在点击事件发生时被调才对。

点击事件的获取组件对象实例:

当要在changeWeather方法中取到实例组件对象又出现困难,首先是这个onClick绑定的方法应该和组件写在一起,分开写有点步骤不清晰。

但当将changeWeather方法写到类中时,还是找不到this对象,console.log显示undefined。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Weather extends React.Component {
constructor(props) {
super(props);
this.state = { isHot: false };
}

render() {
//读取state的值
return (
<h1 onClick={this.changeWeather}>
天气很{this.state.isHot ? "炎热" : "凉爽"}
</h1>
);
}
changeWeather() {
console.log(this)
}
}

原因/需知:

1.在类中调用自己实例的方法要用this.changeWeather。

2.但是因为是绑定onClick方法,此时是系统调用onClick方法的并没有使用实例对象(之前的构造函数一定是用实例对象调用,渲染时调用render方法也是规定用实例对象来调用,所以这两个方法内可以去到this,但onClick没有这种规定)。

3.因为类中方法会被默认开启局部严格模式,且babel也会开启严格模式,则此时this必然连window也获取不到。

bind方法:(其实此方法就是会创建一个指定this环境对象的相同函数,将其返回

当我们调用某些函数的时候是要在特定环境下才能调用到,所以我们就要把函数放在特定环境下,就是使用bind把函数绑定到特定的所需的环境下,具体来说就是改变此函数中的this指向。

此时应该在构造方法中创建一个绑定组件实例对象的changeWeather函数,并将次函数返给实例作为成员变量,则此时调用this.changeWeather函数时寻找原型链就会先找到实例对象的changeWeather函数,成功。

对于state的修改不可以直接修改(this.state.xx=xx)

必须使用组件原型链上的api setState来修改,且这种修改是一种更新,只会对同名state更改,其余不改变。只有使用组件提供的setState函数来修改state的值,react才会根据state的改变重新执行render方法生成对应虚拟DOM组件。(即每次setState则会重新再执行一次render)

小总结:

construct方法一般用于:1.初始化state。2.还可以修改this指向。

render方法一般用于:根据state值生成组件

最终:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Weather extends React.Component {
constructor(props) {
super(props);
this.state = { isHot: false };
this.changeWeather= this.changeWeather.bind(this)
}

render() {
//读取state的值
return (
<h1 onClick={this.changeWeather}>
天气很{this.state.isHot ? "炎热" : "凉爽"}
</h1>
);
}

changeWeather() {
const isHot = this.state.isHot
this.setState({isHot:!isHot})
}
}

事实上还可以对以上代码进行简化:

原理:

1.如果不需要接收参数,则可以在类中直接定义或初始化变量(这些变量属于实例化对象)不需要写construct函数。

2.大多数情况下类中的自定义函数的环境this都会是实例化对象,所以可以用赋值语句形式+箭头函数来定义(因为箭头函数为匿名函数,本身没有this,但使用this也不会报错,它将外层的this(即实例化对象)作为自己的this)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Weather extends React.Component {
state={isHot:true}
render() {
//读取state的值
return (
<h1 onClick={this.changeWeather}>
天气很{this.state.isHot ? "炎热" : "凉爽"}
</h1>
);
}
changeWeather= ()=>{
const isHot = this.state.isHot
this.setState({isHot:!isHot})
}
}

state总结:

state是组件对象中最重要的属性,state的值是对象,此对象可包含怼哥键值对。state={isHot:true,wind:3}

组件被称为状态机,即可以通过更新状态来重新渲染改变组件。

组件类中自定义的方法的this为undefined,2种方法绑定实例对象:

a.bind方法强制绑定后作为实例对象的函数

b.赋值语句+箭头函数

为更新组件,状态数据一定不能直接更改,得使用setState方法来更改。

props

可以直接从组件类的外部(标签处)获取信息。

例如:

ReactDOM.render(, document.getElementById(“test”));

则对象中的props就会存有对应键值对信息:image-20211121213632447

解构赋值:

对象的解构赋值其实就是找同名属性进行对应赋值:

1
2
3
let {bar,foo} = {foo:"aaa",bar:"bbb"}
foo//aaa
bar//bbb

批量传递props:

需要使用展开运算符…

预备知识:一般用于数组求和的reduce方法的介绍:

1
arr.reduce((prev,cur,index,arr)=>{},init)

arr为原数组

prev为上一次内部方法回调时的返回值,如果没有设置init则prev初值为0+arr[0],否则为init,所以有初值init会比没初值多加一次。

cur当前正在处理的数组元素。

index表示正在处理的数组元素索引,没初值则从1开始,有初值则从0开始。

init为prev的初值。

js展开运算符只能用于展开数组:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<script>
//1.展开数组/连接数组
let arr1 = [1,3,5,7,9]
let arr2 = [2,4,6,8,10]
console.log(...arr1)
let arr3 = [...arr1,...arr2]
//函数中接收位置数量参数
function sum(...numbers){
return numbers.reduce((prev,cur)=>{
return prev+cur
})
}
//构造对象时的展开语法,必须在{}内使用...,且仅用于复制一个对象
let person = {name:"tom",age:18}
let person2 = {...person}//可以
//console/log(...person)报错

//也可合并覆盖复制
let person3 = {...person,name:"jack",address:"地球"}
</script>

但必须认清,批量传入的props和以上的都不同,并不属于对象赋值用法。

jsx标签内的{}就只是表明这是个js表达式,只是在react和babel双重影响下,…person 允许被使用了但也仅能用于标签内的展开对象传入参数这一种用法。

1
ReactDOM.render(<Person {...person}/>, document.getElementById("test"));

对传入的props属性参数进行限制,例如哪个属性必须为什么类型,不可以为空,不传的默认值是什么。

因为被react认为没那么必要,所以被额外放在一个工具库prop-types.js包内,导入则全局多了个PropTypes对象,可用来限制props。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<script type="text/javascript" src="./js/prop-types.js"></script>

<script type="text/babel">
//说明用的语言不是js是jsx并且使用babel翻译
//1.用构造方法给state赋值
class Person extends React.Component {
state = { isHot: true };
render() {
console.log(this);
const { name, age, sex } = this.props;
return (
<ul>
<li>name:{name}</li>
<li>age:{age}</li>
<li>sex:{sex}</li>
</ul>
);
}
}
//对标签属性内容和必要性的限制
Person.propTypes = {
name: PropTypes.string.isRequired,
sex: PropTypes.string,
age: PropTypes.number,
speak:PropTypes.func//注意此时指定类型为function但function是关键字所以写func
};
//给标签属性设置默认值
Person.defaultProps = {
sex: "不男不女",
age:18
};
function speak(){
console.log("我说话了")
}
const person = { name: "jerry"};

//2.渲染虚拟DOM到页面上
ReactDOM.render(<Person {...person} speak={speak}/>, document.getElementById("test"));
</script>

props是只读的,不允许修改会报错。

props的简写:其实之前的Person.propTypes=xxx,就是在给Person类添加属性,所以可以把这两个写在外部的添加属性写到Person类的内部,当没有关键字直接在类中 a = 1 是给实例对象添加属性,只需在此前添加关键字static,static a = 1,即为给类添加属性,则此时react就可以根据添加的属性限制属性来帮忙限制传入的props。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Person extends React.Component {
state = { isHot: true };
//对标签属性内容和必要性的限制
static propTypes = {
name: PropTypes.string.isRequired,
sex: PropTypes.string,
age: PropTypes.number,
speak: PropTypes.func,
};
//给标签属性设置默认值
static defaultProps = {
sex: "不男不女",
age: 18,
};
render() {
console.log(this);
const { name, age, sex } = this.props;
return (
<ul>
<li>name:{name}</li>
<li>age:{age}</li>
<li>sex:{sex}</li>
</ul>
);
}
}

组件类的构造器函数,就是基本不用写。因为构造器主要的两种功能:初始化state和修改自定义方法的this指向,我们已经可以用更简便的方式实现。

函数式组件没有this肯定无法使用state和refs,但是因为函数可以接收参数,所以可以使用props,直接在function Person(props)接收即可,react都帮忙把标签里的属性打包好了,直接在function中正常解构赋值取来使用即可。

函数式组件的props属性限制则只能写在外部,使用最开始的方法,直接给这个Person组件添加上propTypes、defaultProps属性即可Person.propTypes。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<script type="text/javascript" src="./js/prop-types.js"></script>

<script type="text/babel">
//说明用的语言不是js是jsx并且使用babel翻译
function Person(props) {
const { name, age, sex } = props;
return (
<ul>
<li>name:{name}</li>
<li>age:{age}</li>
<li>sex:{sex}</li>
</ul>
);
}
Person.propTypes = {
name: PropTypes.string.isRequired,
sex: PropTypes.string,
age: PropTypes.number
};
//给标签属性设置默认值
Person.defaultProps = {
sex: "男",
age: 98,
};
const person = { name: "jerry" };

//2.渲染虚拟DOM到页面上
ReactDOM.render(<Person {...person} />, document.getElementById("test"));
</script>

总结:

state是组件内部的事。props是从组件外获取到的。

props是只读的,不可以被修改会报错。

props是根据标签属性来封装出来的,类式组件封装在实例对象的props属性中,函数式组件就直接封装在传入的参数props中。

pros属性值在标签中可以由{…对象}来批量传入。

对props的限制,不一定非要有,所以react把此功能拿出封装成另一个包,需要限制时引入。

限制就是给组件类/组件那个function 添加属性propTypes或defaultProps,这样react接收标签属性封装为props时就会自动依此做出限制判断。

refs

refs其实很类似与id(id也是标识一下,然后document.getElementById()),就是为了标识某一个标签,只要此标签被ref标识,则react就会将其收入到组件的refs属性里面,可以直接根据refs解构赋值获得那个标签本身,注意获得到的是真实DOM。

字符串形式的refs

注意标签里是ref,但存到组件是存到refs里的。(这种ref有点过时,编写简便但运行效率偏低但还是用得挺多)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<script type="text/babel">
//说明用的语言不是js是jsx并且使用babel翻译
class Person extends React.Component {

output1=()=>{
const {input1} = this.refs
alert(input1.value)
}
output2=()=>{
const {input2} = this.refs
alert(input2.value)
}

render() {
console.log(this);
// const { name, age, sex } = this.props;
return (
<div>
<input ref="input1" type="text"/> &nbsp;
<button onClick={this.output1}>输出</button> &nbsp;
<input ref="input2" onBlur={this.output2} type="text"/>

</div>
);
}
}
//2.渲染虚拟DOM到页面上
ReactDOM.render(<Person />, document.getElementById("test"));
</script>

回调refs

其实就是在对应要标记的标签上给ref属性添加个回调函数,因为ref属性绑定函数会给此函数传入参数(即为此标签节点)并帮你回调执行,所以直接在此回调函数中将接受到的当前标签节点并赋值到组件实例对象的一个属性即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<script type="text/babel">
//说明用的语言不是js是jsx并且使用babel翻译
class Person extends React.Component {

output1=()=>{
const {input1} = this
alert(input1.value)
}
output2=()=>{
const {input2} = this
alert(input2.value)
}
render() {
console.log(this);
// const { name, age, sex } = this.props;
return (
<div>
<input ref={currentNode=>this.input1=currentNode} type="text"/> &nbsp;
<button onClick={this.output1}>输出</button> &nbsp;
<input ref={currentNode=>this.input2=currentNode} onBlur={this.output2} type="text"/>

</div>
);
}
}
//2.渲染虚拟DOM到页面上
ReactDOM.render(<Person />, document.getElementById("test"));
</script>

createRef

就是在类中定义实例的属性来接收React.createRef()方法传来的标签节点容器,但一定要注意这个容器是专人专用,也就是一个容器只能存放一个标签节点否则会被覆盖。然后再标签的ref属性中传入此容器{this.myRefContainer},此时此标签节点就会存储到myRefContainer中,可用.current的形式取出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<script type="text/babel">
//说明用的语言不是js是jsx并且使用babel翻译
class Person extends React.Component {

myRef1=React.createRef()
myRef2=React.createRef()
output1=()=>{
const input1 = this.myRef1.current
alert(input1.value)
}
output2=()=>{
const input2 = this.myRef2.current
alert(input2.value)
}
render() {
console.log(this);
// const { name, age, sex } = this.props;
return (
<div>
<input ref={this.myRef1} type="text"/> &nbsp;
<button onClick={this.output1}>输出</button> &nbsp;
<input ref={this.myRef2} onBlur={this.output2} type="text"/>

</div>
);
}
}
//2.渲染虚拟DOM到页面上
ReactDOM.render(<Person />, document.getElementById("test"));
</script>

Activity简介:

通俗来讲是个用户界面,通常一个应用有多个activity,activity的使用会在manifest中申明,且android中是用任务栈(任务栈是一个Android应用中所有Activity的集合)来管理activity,启动时压栈退出时弹出。

当点击home键时,当前任务栈转到后台,用户可启动任意应用则此应用的任务栈处前台。

Service简介:

Service(不能理解为系统调用,service实际就是在后台默默运行的功能 例如百度云的上传下载)不直接与用户交互没有界面,但比activity有着更高的优先级可以长期存在在后台且内存紧张时不易被终止。**Service不仅可以实现后台功能还用于实现进程间通信。**使用时和activity一样需要在manifest中申明。

BroadCastReceiver简介:

猜:就是有广播发送者和接收者吧。常驻就是一直监听,非常驻则与应用程序生命周期相关。

ContentProvider简介:

内容提供器可支持在多个应用中存储和读取数据,这也是跨应用共享数据的唯一方式。在Android系统中,没有一个公共的内存区域来给各应用共享数据,而是提供了一些主要类型的contentprovider。

猜:就是各应用可通过创建Contentprovider存储自己的数据,别的应用也可以通过使用这个类来获取数据。

在安卓中所有组件都有自己的生命周期从创建到销毁。在这一过程中,组件会在活动、非活动以及可见或不可见等状态中不断因应用场景的改变而进行切换。

Activity:

Android针对Activity的管理使用的是栈机制,Activity栈保存了已经启动并且没有 终止的Activity,并遵循“先进后出”的原则。

任意时刻与用户交互的界面只有一个,而某一时刻在栈顶的也只有一个,当这个activity被销毁或有一个新的activity被创建才会改变,所以使用栈这个数据结构。

Android为我们定义了四种加载模式,分别是standard、singleTop、singleTask和singleInstance。

Activity的加载模式可以在配置文件AndroidManifest.xml中进行配置,配置项为 android:launchMode具体如下图所示:

image-20211119205418964

standard 默认模式,可以不用写配置。在这个模式下,都会默认创建一个新的实例。因此,在这种模式下,可以有多个 相同的实例,也允许多个相同Activity叠加。 例如:若我有一个Activity名为A1, 上面有一个按钮可跳转到A1。那么如果我点击按钮,便会新创一个Activity A1叠在刚才的A1上(可在同一个任务栈中存在多个相同activity),再点击,又会再新创一个在它上……点back键会依照栈顸序依次退出。

singleTop可以有多个实例,但是不允许多个相同Activity叠加。如果在任务的栈顶正好存在该Activity的实例, 就重用该实例,否者就会创建新的实例并放入栈顶(即使栈中已经存在该Activity实例,只要不在栈顶,都会创建实例)。

singleTask(一个任务栈中只允许存在一个自己不会有重复)不允许有多个相同实例。(猜的本身应用程序)中启动的时候如果在栈中已经有该Activity的实例,就重用该实例(会调用实例的onNewIntent())。重用时,会让该实例回到栈顶,因此在它上面的实例将会被移出栈。如果栈中不存在该实例,将会创建新的实例放入栈中。

别的应用程序打开C2(singleTask的activity)时会新启动一个task。如别的应用Other中有一个activity,taskId为200,从它打开 C2,则C2的taskIdI不会为200,例如C2的taskId为201,那么再从C2打开C1、C3,则C2、C3的taskId仍为 201。

singleInstance(一个任务栈中只允许存在一个activity就是自己)一个实例activity一个task,这个task只有这个实例,不允许有别的Activity存在。

(关于在一个应用打开另一个应用的activity遵循的基本原则就是,另一个应用的activity 应该放在另一个任务栈中,遵循此原则的条件下再去遵守singleInstance还是singleTask。)

Activity生命周期

是指Activity从创建到销毁的过程,在这一过程中,Activity一般处于4种状态,即: Active/Running、Paused、Stop、Killed

(1)Active/Running 此时Activity一定处于屏幕的最前端,用户完全可以看得到,并且可以与用户进行交互。对于Activity栈来说,它处于栈顶;

(2)Paused 此时Activity在屏幕上仍然可见,但是它已经失去了焦点(一个活动被另一个非全屏的活动所覆盖(比如一个Dialog)),用户不能与之进行交互。暂停状态的Activity是存活的,它仍然维持着其内部状态信息,但是系统可能会在手机内存极低的情况下杀掉该Activity;

(3)Stop (放到后台)此时Activity在屏幕上完全不能被用户看见,也就是说这个Activity已经完全被其他Activity 所遮住。处于停止状态的Activity,系统仍然保留有其内部状态和成员信息,但是它经常会由于手机系统 内存被征用而被系统杀死回收;

(4)Killed Activity被系统杀死回收或者未启动。

回调函数:

使用者自己定义一个函数,函数的程序内容自己实现,然后把这个函数的入口地址传给别的(系统的)函数。由别的(系统的)函数运行时来调用。

Activity生命周期的回调函数(所有的Activity生命周期方法的实现都必项先调用其父类的方法。例如:public void onPause()

{ super.onPause();

. . .

}

):

void onCreate(Bundle savedInstanceState) //所有activity必须实现,用于做一下初始化工作。在Activity第一次被创建的时候调用。可在此处做初始化设置──创建视 图、绑定数据至列表等。如果曾经有状态记录,则调用此方法时会传入一个 表示Activity以前状态的包对象做为参数(就是浏览器可选择恢复原页面)。

void onStart()

void onRestart()

void onResume()

void onPause() //当系统将要启动另一个Activity活着弹出对话框时调用。此方法主要用于将所有持久性数据写入存储中,这一切动作应该在短时间内完成,因为 下一个Activity必须等到此方法返回后才会继续。onstop则可以等下一个activity resume后在来完成。

void onStop() //当Activity不再为用户可见时调用此方法。这可能发生在它被销毁戒者 另一个Activity(可能是现存的或者是新的)转到运行状态并覆盖它时。

void onDestroy() //在Activity销毁前调用,这可能发生在Activity结束(调用了它的 finish() 方法)或者因为系统需要临时空间而销毁杀死该Activity实例时。可以用 isFinishing()方法来区分这两种情况。

Android生命周期可分为全生命周期(从creat到destroy 活着)、可视生命周期(从start到stop 可被看见)、前台生命周期 (从resume到pause 可与用户交互)

img注意pause可以直接resume但stop得先restart再start。

还有两个方法不属于生命周期方法不总是会被调用。

onSaveInstanceState()//Android系统在资源不足而终止Activity前被调用(有时认为按home键stop前也会使用,或者有时也直接用在pause和stop两个回调函数之间),用以保存Activity的状态信息,供onCreate()和 onRestoreInstanceState()方法恢复使用。

onRestoreInstanceState()//恢复onSaveInstanceState()保存的Activity状态信息,在onStart()和onResume()方法间使用。

还需注意:activity之间切换时无论就是暂停还是停止或者销毁,都是上一个activity先执行到onPause()就直接先让下一个activity开始create到resume显示到前端,然后才在后端接着处理之前pause的activity是否需要stop还是要destroy。(onSaveInstanceState()方法有时也会直接在pause和stop方法之间被使用)

Fragment:

Fragemnt的出现就是因为屏幕变大(例如平板出现

image-202111200922186932个不同活动界面手机需要2个activity显示但平板要显示在一个activity上),设计者想尽可能利用当前页面显示更多信息所以将一个activity界面又分为多个有生命周期回调函数的fragment。Fragment像是个小activity子activity,也有生命周期需要依附于activity存在所以其生命周期会受所依附的activity生命周期的影响。一个activity里可以有多个fragment,一个fragment也可以用于多个activity。

Fragment生命周期回调函数:

imgcreate前先绑定,destroy后再解绑

onAttach(Activity) //当Fragment与Activity发生关联时调用。从该方法开始,就可以通过 Fragment.getActivity方法获取与Fragment关联的窗口对象了,但在该方法中仍然无法操作Fragment中的控 件。

onCreateView(LayoutInflater, ViewGroup, Bundle)//创建该Fragment的视图。

onActivityCreated(Bundle)//当Activity的onCreate方法返回时调用。

onDestoryView()//与onCreateView相对应,当该Fragment的视图被移除时调用。

onDetach()//与onAttach相对应,当Fragment与Activity关联被取消时调用

静态使用fragment:

直接新建fragment文件(继承fragment必须至少重写有oncreate和onCreateView),调整对应layout。然后在activity的layoutxml文件内添加fragment组件并在其android:name项指定哪个fragment即可。

动态使用fragment:

这个才是fragment的强大之处,先把mainactivity的layoutxml文件中的fragment引用去掉,只留下最外层linearlayout层,并对此layout添加id。然后可以在activity.java文件内用java代码直接对此layout动态添加或替换成Fragment.

Mainactivity.java:

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Display display = getWindowManager().getDefaultDisplay();//获得屏幕显示
if(display.getWidth()>display.getHeight())
{

​ getFragmentManager().beginTransaction().replace(R.id.main_layout,new Fragment1()).commit();
}else {

​ getFragmentManager().beginTransaction().replace(R.id.main_layout,new Fragment2()).commit();
}
}

报错Fragment1无法转换为Fragment:明明已经extends了,这种一般就是版本错误,方法内所需的fragment的版本和你继承的不同(看报错信息更改Fragment1所继承的版本即可)。

Fragment之间通信:

Fragment和Fragment之间的 通讯的桥梁就是这个FragmentManager这个类,这个类是用来管理所有的这个Fragment的,所以可以 找到任何一个所需要的Fragment类。

实际操作来说的话应该是在一个fragment中使用getActivity获得当前运行的上下文环境再.findViewById获得同一个Activity下不同Fragment内的组件信息。

例如:在fragment2内使用:

TextView textView = (TextView)getActivity().findViewById(R.id.f1textView);获取fragment1的textview。

Intent:

Intent是一个动作的完整描述,包含了产生组件、接收组件和传递数据信息。Android中提供了Intent机制来协助应用间的交互与通讯。Intent不仅可用于应用程序之间,也可用于应用程序内部 的 Activity / Service之间的交互。因此,意义:专门提供组件互相调用的相关信息,实现调用者与被调用者之间的解耦。

Intent由动作、数据、分类、类型、组件和扩展信息等内容组成

Action属性用于描述Intent要完成的动作:

image-20211120092301203

Data属性是执行动作的URI和MIME类型(执行的目标对象)

image-20211120092314711

Action和data一般匹配使用(这两个属性是最重要的):

image-20211120092327507

Category属性指明一个执行Action的分类(。例如 LAUNCHER_CATEGORY 表示Intent 的接受者应该在Launcher中作为顶级应用出现;而ALTERNATIVE_CATEGORY表示当前的Intent是一系列的可选动作中的一个,这些动作可以在同一块数据上执行。):

image-20211120092338853

Component属性用于指明Intent目标组件的类名称

通常(隐式)intent是根据Intent中包含的其他属性的信息,比如Action、Data/Type、Category进行查找,最终找到一个与之匹配的目标组件。但是,如果指定了Component这个属性(显式),Intent则会 直接根据组件名查找到相应的组件,而不再执行上述查找过程。指定Component属性后, Intent的其他属性都是可选的。

Extra属性用于添加一些附加信息:例如发送一个邮件,就可以通过Extra属性来添加主题 (subject)和内容(body)。这边putExtra()一些信息就可以被在那边用getExtra()取出。

根据Intent寻找目标组件时所采用的方式不同,可以将Intent分为两类:直接Intent和间接 Intent。又称为显式Intent与隐式Intent 。

显式intent:

一般就是已知组件名img直接传递到构造函数或者用set方法设置。

隐式intent(一般选择此方式,其实就有点想打开一个视频文件时你想用哪个应用打开,这些应用就像是一堆匹配隐式intent要求的activity组件):

隐式Intent,不指定具体的组件,但是它会声明将要执行的操作,从而匹配到相应的组件。这种方式通过Intent Filter过滤实现,过滤时通常根据Action、Data和Category属性进行匹配查找。 Android提供了两种生成Intent Filter的方式:一种是通过IntentFilter类生成;另一种通过在配置文件 AndroidManifest.xml中定义元素生成。

匹配原则:

标签中常用这些子元素(其实就是表明所处的activity的属性或是能力,如果和程序中所需intent目标组件匹配则可以将其使用)

其中属性不能为空,否则所有的Intent都会因匹配失败而被阻塞。

当发送一个隐式Intent后,系统会将它与设备中的每一个组件的过滤器进行匹配,匹配属性有Action、 Category、Data三个需要这三个属性都匹配成功过滤器中有多个则需大于等于才能唤起相应的组件。

过滤器中有多个action项时,隐式Intent中的Action属性,与组件中的某一个过滤器的Action能够匹配(如果一个过滤器声明了多个 Action属性,只需要匹配其中一个就行,就已经说明能满足要求了),那么就算是匹配成功.

过滤器中有多个category项时,过滤器的Category属性内容必须是大于或者等于隐式Intent的Category属性时候,隐 式Intent才能匹配成功。如果一个隐式Intent没有设置Category属性,那么它可以通过任何一个过滤器 的Category匹配。

过滤器中有多个data项时,用于指定组件可以执行的数据。每个Data属性都可以指定数据的URI结构(数据地址)和数据MIME类型(数据类型)。

Data匹配时候,MIME类型和URI两者都会进行匹配,匹配规则如下:

如果过滤器未声明URI和MIME类型,则只有不含URI和MIME类型的隐形Intent才能 匹配成功

如果过滤器中声明URI但是未声明MIME类型(也不能从URI中分析出MIME类型), 则只有URI与过滤器URI相同且不包含MIME类型的隐式Intent才能匹配成功

如果过滤器声明MIME类型但是未声明URI,只有包含相同MIME类型但是不包含URI 的隐式Intent才能匹配成功

如果过滤器声明了URI和MIME类型(既可以是直接设置,也可以是从URI分析出来) ,只有包含相同的URI和MIME类型的隐式Intent才能匹配成功

总结一下就是:基本隐式intent和intentfilter的这URI和MIME两个属性无论可知未知哪个都必须一模一样。

URI匹配原则:

Uri基本结构:

[scheme:][//authority][path][?query][#fragment] authority还可以细分为host和port

[scheme:][//host:port][path][?query][#fragment]

在URI中,每个组成部分都是可选的,但是有线性的依赖关系 ①如果没有scheme部分,那么host部分会被忽略 ②如果没有host部分,那么port部分会被忽略 ③如果host部分和port部分都没有,那么path部分会被忽略 当进行URI匹配时候,并不是比较全部,而是局部对比,以下是URI匹配规则。 ①如果一个URI仅声明了scheme部分,那么所有拥有与其相同的scheme的URI都会通过 匹配,其他部分不做匹配 ②如果一个URI声明了scheme部分和authority部分,那么拥有与其相同scheme和 authority的URI才能匹配成功,path部分不做匹配 ③如果一个URI所有的部分都声明了,那么只有所有部分都相同的URI才能匹配成功

实例:image-20211120092401413

在Android世界的四大组件Activity、BroadcastReceiver、Service、Content Provider中,前三个都是 通过Intent来解析进行跳转的,Intent可以说是连接这四大组件的重要桥梁。

先学习intent实现三种activity跳转:

不带参跳转:

Intent intent = new Intent(xxx.this(当前环境activity),xxx.class(目标activity));

startActivity(intent);

带参数跳转:

其实就是以键值对形式往在startActivity前往intet里面putExtra(键,值)

目标activity中直接getIntent().getExtra(键)。

img

带返回值的跳转:(简单来说就是跳转时的使用startActivityForResult跳转,这样在目标activity就可以使用setResult方法来返回数据,再在本activity使用onActivityResult方法接收传回的数据即可。)

在跳转的Activity端,调用 startActivityForResult(intent, 1),跳转到 下一个Activity,其中第一个参数为传入的意图对象(里面封装了目的activity类和其余信息),第二个为设置的请求码;

img

跳转到第二个Activity后,调用 setResult(100, intent)方法可返回上一个 Activity,其中第一个参数为结果码,第二 个为传入的意图对象;

img

在第一个Activity通过onActivityResult()方法获得返回的数据 得先判断是否是自己的那个请求以及请求的结果是否同意:

image-20211120092447578

View类:

image-20211120092950878

Android系统给开发者提供了三种设计UI的方式:使用XML文件布局(常用);使用传统的 代码布局;前两者结合使用。在介绍Android系统提供的5大布局容器和常用控件之前需 弄清两个概念:

(1)控件:继承于View类型的,可方便完成一些特殊功能的View类型。

(2)容器:继承于ViewGroup,是一种比较特殊的View类型或者控件(ViewGroup继 承于View),它存在的作用就是可以以一定的规则展示控件,下文所说的父控件指的就 是容器。只有viewGroup控件才能包含view 例如LinearLayout 里面可以有各种控件。

几种设计模式(设计模式其实就是大家在做安卓开发中发现的一些套路,根据这些套路才能开发的快和好)

组合模式,组合模式对单个对象(叶子对象)和组合对象(组合对象)具有一致性,它将对象组织到树结构中, 可以用来描述整体与部分的关系。同时它也模糊了简单元素(叶子对象)和复杂元素(容器对象)的 概念,使得客户能够像处理简单元素一样来处理复杂元素,从而使客户程序能够与复杂元素的 内部结构解耦。

image-20211120093007150

适配器模式(还是有点不太懂 主要解决两个软件实体间接口不兼容的问题 应该就是用一个适配器类将接口进行转换)

意图:将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的 那些类可以一起工作。

主要解决:主要解决在软件系统中,常常要将一些”现存的对象”放到新的环境中,而新环境要求的接口是现对象 不能满足的。 何时使用: 1、系统需要使用现有的类,而此类的接口不符合系统的需要。 2、想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类包括一些可能在将来引进的 类一起工作,这些源类不一定有一致的接口。 3、通过接口转换,将一个类插入另一个类系中。

该模式的主要优点: ① 客户端通过适配器可以透 明地调用目标接口。 ② 复用了现存的类,程序员 不需要修改原有代码而重 用现有的适配者类。 ③ 将目标类和适配者类解耦, 解决了目标类和适配者类 接口不一致的问题。 缺点:过多地使用适配器,会 让系统非常零乱,不易整体进行把握。

TextView常用的属性:

• android:id=”@+id/textView1”表示该控件的id,在布局文件中或者代码中被引用

• android:textStyle=”bold”表示TextView里面的字加粗显示

• android:layout_height=”wrap_content”表示该控件的高度为其包含内容的高度

• android:layout_width=”wrap_content”表示该控件的宽度为其包含内容的宽度

• android:text=”@string/signin” 显示的内容,这里表示存放在string.xml文件中name=signin 的文本 • android:layout_height=”40dip”设置具体的高度 • android:textColor=”#7089c0”设置文本的颜色

• android:textSize=”18sp”设置文本的大小 • android:gravity=”center_vertical”设置文本纵向居中

• android:paddingLeft=”5dip”设置内边距 • android:layout_marginTop=”5dip”设置外边距

EditText属性的大部分设置与TextView是一样的,这里仅介绍EditText与TextView不同的属性:

• android:hint=”@string/name”表示在为输入之前的提示,当EditText获得输入焦点,并输入文字 时,该文本自动消失,起提示的作用;android:singleLine=”true”表示该文本输入框不可换行输入, 只能在一行内输入文本;

• android:password=”true”表示该文本输入框是用来输入密码的,输入的文本会自动装换为“·”, 起到隐藏用户密码的作用

Android中的组件需要用一个int类型的值来表示,即为id

id属 性只能接受资源类型的值,也就是必须以@开头的值,例如,@id/abc、@+id/xyz等。 p

*@+id(一般用于添加id)就是在R.java文件里新增一个id名称,如果之前已经存在相同的id名称,那么会覆盖**之前的名称。*

*@id(一般用于指明那个部件)则是直接引用R.java文件的存在的id资源,如果不存在,会编译报错*

Button与TextView设置一样,区别在于Button可以有按键的效果和事件的监听

Button的响应方式:

匿名内部类:

匿名类是一个表达式,匿名类的语法就类似于调用一个类的构建函数 (new OnClickListener()), 除此之外,还包含了一个代 码块,在代码块中完成类的定义。

img先获取,再set监听器,监听器类直接匿名定义并重写其onclick方法即为点击时希望的响应事件。

自定义监听类:

基本没差就是额外定义了一个实现了OnClickListener接口的类,把他传入setOnClickListener方法中。

image-20211120093041191

由 Activity 实现 OnClickListener 接口:(这个在实际应用中使用的较多,当点击事件较多时可以减少代码量)

实际上就是Activity实现OnClickListener 接口并重写其onClick方法,在此方法内依据传入的view参数的id判断要执行哪一种点击事件。

image-20211120093053403

在布局文件中添加 onClick 属性(了解即可不好用容易出错):

Xml文件中添加onclick属性对应值为方法名 并在activity中实现此方法

img

ImageView和textview一样,区别在于内容是个图片

多了个src属性:android:src=”@drawable/icon_refresh”

5种layout:

LinearLayout线性布局(有个小关键点就是linearLayout水平还是垂直都不会自动换行/换列,放满了还接着往屏幕外放无法显示了就。)

orientation属性:

VERTICAL 垂直 组件之间只存在上下关系

HORIZONTAL 水平 组件之间只存在左右关系

image-20211120093202206

属性:

android:orientation 设置控件或者容器存放的方式

android:id 设置控件id,方便在使用时找到其引用

android:layout_width 容器的宽度,该值必须设置

android:layout_height 容器的高度,该值必须设置

android:layout_weight 该属性针对其内的子控件,存放在LinearLayout中的控件都有这 个属性,用来设置该控件或者容器占父控件或者容器的比例。

android:background 设置背景

android:clickable 是否可以响应点击事件

android:orientation 设置子控件放置方向

xmlns:android= “http://schemas.android.com/apk/res/android" 设置该值表示可以当做一个布局文件,被Android系统解释使用,每个布局 的根布局必须包含该属性,否则,系统将找不到该布局。必须放在最外层开始的的标记中

android:paddingLeft 设置该layout的左内边距,该值设置后,位于该layout中的View或者 ViewGroup均在padding的距离内放置,边距内不能放置控件。

android:paddingRight 设置该layout的右内边距,同上

android:paddingTop 设置该layout的上内边距,同android:paddingLeft

android:paddingBottom 设置该layout的下内边距,同上

android:padding 设置该layout的四个方向内边距,同上

android:layout_margin 表示外边距,还有与内边距相类似的属性,分别为上,下,左,右外 边距 android:minHeight 表示该Layout的最小高度,layout_height=“wrap_content”

android:minWidth 表示该Layout的最小宽度,Layout_width=“wrap_content”

RelativeLayout相对布局

相对布局,是指利用控件之间的相对位置关系来对布局进行放置。换句话说, 在该容器中的控件与其他任何一个控件或者容器(包括父控件)有相对关系。

属性:

android:layout_alignParentTop=”true|false”是否与父控件顶部齐平

android:layout_alignParentBottom=”true|false” 是否与父控件底部齐平

android:layout_alignParentLeft=”true|false” 是否与父控件左部齐平

android:layout_alignParentRight=”true|false” 是否与父控件右部齐平

android:layout_centerInParent=”true|false” 是否在父控件中间

android:layout_centerInHorizontal=”true|false” 是否水平方向在父控件的中间

android:layout_centerInVertical=”true|false” 是否垂直方向在父控件的中间

android:layout_alignTop=”@id/xxx” 与xxx的顶部平齐xxx(代表控件或者容器的ID,可以 是父控件的ID)

android:layout_alignBottom=”@id/xxx” 与xxx的底部平齐

android:layout_alignLeft=”@id/xxx” 与xxx的左边平齐

android:layout_alignRight=”@id/xxx” 与xxx的右边平齐

android:layout_above=”@id/xxx” 在xxx的上面,该控件的底部与xxx顶部平齐

android:layout_below=”@id/xxx” 在xxx的下面,该控件的顶部与xxx顶部平齐 android:layout_toRightOf=”@id/xxx” 在xxx的右边,该控件的左边与xxx的右边平齐 android:layout_toLeftOf=”@id/xxx” 在xxx的左边,该控件的右边与xxx的左边平齐

FrameLayout(了解即可其实是层叠布局)

帧布局,是指该容器内放置的控件或者容器没有上下左右的关系,只有层叠前后的关系。对于放置前后的关系,在没有设置其他属性之前,默认Android系统采用的是叠放的原则,即后加入节点的层叠在上面。设置属性android:bringToFront=“true|false”将前面放置的控件提到最前面可见。

TableLayout(感觉就有点奇怪应该不常用 继承自linearlayout):

表格布局 直接往tablelayout里面放部件会直接占满一整行,此时就需要使用tableRow属性将其包裹,则一个tablerow包裹的算是一行。

一个tablerow里包含太多部件img一行放不下会直接不显示。

属性:

以下属性都是针对所有行的对应列共同的设置,编号从0开始。

android:stretchColumns=”” 设置可伸展的列(前提是得有剩余空间)(此时列的宽度由所有行中最宽的单元格决定)。该列能够向行方向伸展。最多可占领一整行。 android:stretchColumns=”0” 第0列可伸展 。

android:shrinkColumns=”” 设置可收缩的列(前提是有组件未显示完全)。android:shrinkColumns=”1,2” 第1,2列皆可 收缩

android:collapseColumns=”” 隐藏指定的列,可设置多个值,用逗号隔开“0,2”表示隐藏第1和第 3列。android:collapseColumns=”*” 隐藏全部行

AbsoluteLayout(很少使用)

绝对布局,是指以屏幕左上角为坐标原点(0,0),控件在容器中的位置以坐标的形式存在, 可以随意指定控件的坐标位置,非常灵活。在开发过程中很少使用,原因是屏幕兼容性不好,不便控制两个控件之间的位置。其中控件或者容器放置的位置通过android:layout_x和 android:layout_y这两个属性进行设置。

Res文件夹下如何存放资源文件:

image-20211120093218942

喂喂喂