原生 JS 实现扫雷 (分析+代码实现)
阅读这篇文章需要掌握的基础知识:Html5、CSS、JavaScript
在线Demo:查看
扫雷规则
在写扫雷之前,我们先了解下它的游戏规则
● 扫雷是一个矩阵,地雷随机分布在方格上。
● 方格上的数字代表着这个方格所在的九宫格内有多少个地雷。
● 方格上的旗帜为玩家所作标记。
● 踩到地雷,游戏失败。
● 打开所有非雷方格,游戏胜利。
功能实现思路分析
矩阵的生成
- 矩阵的生成有多种方式可以实现,我们这里使用<table>+<span>标签。
- 通过 js 给定行数与列数在<table>的 innerHtml 写入<span>标签来动态生成矩阵。
方格的打开与标记
- 通过 onmousedown 事件,传入点击的方格的坐标及event,判断event为左键还是右键。
- 左键打开方格,右键标记方格。
地雷的随机分布
- 由于第一次打开的方格不能为地雷所以我们把生成地雷的函数放在第一次点击方格时。
- 我们通过循环用 Math.random() 函数来随机生成地雷的二维坐标。
- 判断坐标是否不为第一次点击方格的坐标以及没有雷存在。
- 是则将方格设置为地雷,当前地雷数+1,并且将九宫格内的方格的计雷数+1。
- 否则跳过进入下个循环,直到地雷的数量达到设定的最大雷数,结束循环。
踩到地雷游戏结束
- 打开方格为地雷时,提示游戏结束。
- 通过遍历矩阵来打开所有地雷
连锁打开方格
- 当打开的方格为计雷数为0的方格,自动打开九宫格内的非雷方格。
- 如果打开的非雷方格九宫格内仍有非雷方格,继续打开九宫格内的非雷方格,直到没有为止。
游戏胜利条件
- 当所有非雷方格被打开即为游戏胜利。
- 在每次打开方格函数中都遍历一遍矩阵,当找到有未打开的非雷方格时则结束遍历。
- 当遍历完未找到未打开的非雷方格则提示游戏胜利。
剩余地雷数与计时器
- 地雷的总数减去玩家标记的方格数即为剩余地雷数
- 计时器可以用setInterval()函数实现
代码实现
生成矩阵
我们先在<body>里写一个<table>标签,设定个 id='grid'
<table id='grid'></table>
然后在<script>里 定义两个变量 row--行数 col--列数
通过两个for循环把 (方格)<span> 写入到 (矩阵)<table> 里,通过<td><tr>标签控制行列。
var row = 10; //行数
var col = 10; //列数 //生成矩阵html <tr>--行标签 <td>--列标签
let gridHtml = '';
for (let i = 0; i < row; i++) {
gridHtml += '<tr>'
for (let j = 0; j < col; j++) {
gridHtml += '<td><span class="blocks"></span></td>';
}
gridHtml += '<tr>'
}
//写入html
document.getElementById('grid').innerHTML = gridHtml;
写一下矩阵和方格的CSS样式。
#grid {
margin: auto; /* 让矩阵居中显示于页面 */
} .blocks {
width: 30px;
height: 30px;
line-height: 30px;
display: block; /* 让span以block方式显示 */
text-align: center;
border: solid 1px #000;
user-select: none; /* 设置不可拖拽选中 */
cursor: pointer; /* 设置鼠标停留样式 */
} .blocks:hover {
background: #0af; /* 鼠标停留时背景颜色变化 */
}
至此打开页面,矩阵就初步显示出来了。
把矩阵的方格放入二维数组中
我们先定义一个全局变量grid。
把刚才写的生成矩阵的代码写成一个函数 function init_grid()
document.getElementsByClassName('blocks') 返回的是一个一维数组,我们把它通过两个for循环转化为二维数组。
给每个方格定义一个属性 count 计雷数 --- blocks[i].count = 0;
然后把返回值赋值给grid --- grid = init_grid();
var row = 10; //行数
var col = 10; //列数
var grid = init_grid(); //初始化矩阵 (row-行数 col-列数)
function init_grid() { //生成矩阵html <tr>--行标签 <td>--列标签
let gridHtml = '';
for (let i = 0; i < row; i++) {
gridHtml += '<tr>'
for (let j = 0; j < col; j++) {
gridHtml +=
'<td><span class="blocks"></span></td>';
}
gridHtml += '<tr>'
}
//写入html
document.getElementById('grid').innerHTML = gridHtml; //返回矩阵二维数组
let blocks = document.getElementsByClassName('blocks');
let grid = new Array();
for (let i = 0; i < blocks.length; i++) {
if (i % col === 0) {
grid.push(new Array());
}
//初始化计雷数
blocks[i].count = 0;
grid[parseInt(i / col)].push(blocks[i]);
}
return grid;
}
写完了这段我们先写一段代码测试下grid有没有赋值成功,遍历grid把方格的值改为对应的坐标。
for (let i = 0; i < row; i++) {
for (let j = 0; j < col; j++) {
grid[i][j].innerHTML = i + ',' + j;
}
}
可以看到 grid 已经赋值成功!没成功的回去检查下代码。(Tip:测试完记得把测试代码删除)
方格的点击事件
定义一个函数 function block_click( _i, _j, e) 的大致框架
e为传入的鼠标事件,e.button ( 0为左键,2为右键 )。
isOpen属性为自定义属性,用来判断方格是否打开。
//方格点击事件 _i:坐标i _j:坐标j e:鼠标事件
function block_click(_i, _j, e) { //跳过已打开的方格
if (grid[_i][_j].isOpen) {
return;
} //鼠标左键打开方格
if (e.button === 0) { }
//鼠标右键标记方格
else if (e.button === 2) { }
}
然后修改下之前写在 init_grid 函数里的<span>的属性,绑定 onmousedown 事件,传入 i,j 坐标,和鼠标事件 event
gridHtml += '<td><span class="blocks" onmousedown="block_click(' + i + ',' + j + ',event)"></span></td>';
修改下body的属性 加入防拖拽生成新页面和屏蔽右键菜单。
<!-- ondragstart:防拖拽生成新页面 oncontextmenu:屏蔽右键菜单-->
<body ondragstart='return false' oncontextmenu='self.event.returnValue=false'>
我们在鼠标左键事件里面写下测试代码,当左键方格时显示它的坐标。
//鼠标左键打开方格
if (e.button === 0) {
grid[_i][_j].innerHTML = _i + ',' + _j;
}
效果如下,没成功的回去检查下代码。(Tip:测试完记得把测试代码删除)
方格的标记
在鼠标右键事件写标记代码,这里用 ▲ 来作为标记。
右击一次添加标记,再次右击删除标记。
//鼠标右键标记方格
else if (e.button === 2) { let block = grid[_i][_j];
if (block.innerHTML !== '▲') {
block.innerHTML = '▲';
} else {
block.innerHTML = '';
}
}
效果如下:
随机生成地雷
由于第一次打开的方格不能为地雷所以我们把生成地雷的函数放在第一次点击方格时。
先定义全局变量 maxCount --- 最大地雷数 isFirstOpen --- 是否第一次打开方格。
var row = 10; //行数
var col = 10; //列数
var grid = init_grid();
var maxCount = 10; //最大地雷数量
var isFirstOpen = true; //第一次打开方格
在鼠标左键事件里面写第一次打开方格生成地雷的代码的大致框架。
//鼠标左键打开方格
if (e.button === 0) { //第一次打开
if (isFirstOpen) { isFirstOpen = false;
let count = 0; //当前地雷数 //生成地雷
while (count < maxCount) {
//........
}
}
}
完善生成地雷代码:
生成随机坐标 ri,rj,判断该坐标不等于第一次点击方格的坐标以及该坐标表方格不为地雷。
条件成立,将坐标对应方格的 isMine 设置为true,当前地雷数+1,并使九宫格内非雷方格的计雷数 count +1
自定义属性isMine代表方格为地雷。
自定义属性count为计雷数。
当地雷数大于最大地雷数,结束循环。
//生成地雷
while (count < maxCount) { //生成随机坐标
let ri = Math.floor(Math.random() * row);
let rj = Math.floor(Math.random() * col); //坐标不等于第一次点击方格的坐标 && 非雷方格
if (!(ri === _i && rj === _j) && !grid[ri][rj].isMine) {
grid[ri][rj].isMine = true; //自定义属性isMine代表方格为地雷
count++; //当前地雷数+1 //更新九宫格内非雷方格的计雷数
for (let i = ri - 1; i < ri + 2; i++) {
for (let j = rj - 1; j < rj + 2; j++) {
//判断坐标防越界
if (i > -1 && j > -1 && i < row && j < col) {
//计雷数+1
grid[i][j].count++;
}
}
}
}
}
写个测试代码在生成地雷后显示所有方格的状态。(Tip:测试完记得把测试代码删除)
for (let i = 0; i < row; i++) {
for (let j = 0; j < col; j++) {
//判断方格是否为雷
if (grid[i][j].isMine) {
//显示为雷
grid[i][j].innerHTML = '雷';
} else {
//否则显示计雷数
grid[i][j].innerHTML = grid[i][j].count;
}
}
}
效果如下:可以看到已经随机生成了雷,计雷数也正确显示了。
方格的打开事件
在生成地雷的代码下,加入方格打开代码函数 block_open(_i,_j) 的大致框架。
定义 function op(block) 函数设定打开方格的状态与样式。
判定打开的方格的类型
block.isMine 为打开地雷方格 --> 游戏结束
block.count === 0 为打开计雷数为0的方格 --> 连锁打开非雷方格
else 为打开计雷数大于0的方格 --> 显示方格计雷数
//鼠标左键打开方格
if (e.button === 0) { //第一次打开
if (isFirstOpen) {
//.......
} //执行打开方格函数
block_open(_i, _j); //打开方格函数
function block_open(_i, _j) { let block = grid[_i][_j];
op(block); //设定打开方格的状态与样式
function op(block) {
block.isOpen = true; //isOpen为自定义属性,设置为true代表已打开
block.style.background = '#ccc'; //将背景设置为灰色
block.style.cursor = 'default'; //将鼠标停留样式设置为默认
} if (block.isMine) {
//踩雷
} else if (block.count === 0) {
//打开计雷数为0的方格
} else {
//打开计雷数不为0的方格
}
}
}
打开非雷方格显示计雷数
我们先把最简单的显示方格计雷数搞定。
else {
//打开计雷数不为0的方格
block.innerHTML = block.count; //显示计雷数
}
效果如下:
踩雷游戏结束
接下来写踩雷代码,当打开的方格为雷时,将其显示为'雷',并打开所有的地雷,提示游戏结束。
if (block.isMine) {
//踩雷
block.innerHTML = '雷'; //显示为 '雷'
//遍历矩阵打开所有的地雷方格
for (let i = 0; i < row; i++) {
for (let j = 0; j < col; j++) {
//找到地雷
block = grid[i][j];
if (!block.isOpen && block.isMine) {
op(block); //设置打开状态和样式
block.innerHTML = '雷'; //显示为 '雷'
}
}
}
//提示游戏结束
alert("游戏结束");
}
效果如下:
连锁打开方格
打开的方格为计雷数为0的方格,自动打开九宫格内的非雷方格,循环递归到没有为止。
计雷数为0就没必要让innerHtml显示0了,保持空白就行。
else if (block.count === 0) {
//打开计雷数为0的方格
//遍历九宫格内的方格
for (let i = _i - 1; i < _i + 2; i++) {
for (let j = _j - 1; j < _j + 2; j++) {
//判断是否越界&&跳过已打开的方格&&非雷
if (i > -1 && j > -1 && i < row && j < col && !grid[i][j].isOpen && !grid[i][j].ismine) {
//递归打开方格函数
block_open(i, j);
}
}
}
}
效果如下:
游戏胜利条件
扫雷大体框架已经出来了,我们现在做胜利条件的判定。
在方格点击函数最后写判断代码。
//方块点击事件 _i:坐标i _j:坐标j e:鼠标事件
function block_click(_i, _j, e) { //跳过已打开的方块
if (grid[_i][_j].isOpen) {
//...
}
//鼠标左键打开方块
if (e.button === 0) {
//...
}
//鼠标右键标记方块
else if (e.button === 2) {
//...
} //遍历矩阵
let isWin = true;
for (let i = 0; i < row; i++) {
for (let j = 0; j < col; j++) {
let block = grid[i][j];
//判断游戏胜利条件(所有的非雷方格已打开)
if (!block.isMine && !block.isOpen) {
//如果有未打开的非雷方块 条件不成立
isWin = false;
}
}
}
if (isWin) {
alert("游戏胜利");
}
}
效果如下:(还专门玩了一遍^ ^)
游戏部分到这里就完成了!
剩余地雷数与计时器
最后,我们做一下剩余地雷数和计时器的显示。
我们写个 <div> 在 <table> 的上面,放两个 <span> 来做显示框,<label> 用来给 js 计数。
<div id='bar'>
<span class='bar'>剩余雷数:<label id='count'>0</label></span>
<span class='bar'>计时:<label id='time'>0</label>s</span>
</div> <table id='grid'></table>
再写下CSS样式:
#bar {
text-align: center;
margin-bottom: 20px;
} .bar {
height: 25px;
width: 150px;
line-height: 25px;
display: inline-block;
border: solid 1px #000;
margin-left: 20px;
margin-right: 20px;
}
效果如下:
在 js 中定义两个全局变量拿到 <lable> count 和 time
然后让地雷数量等于最大地雷数,设置个100ms定时器,每次+0.1s,保留一位小数。
var count = document.getElementById('count'); //剩余地雷数
count.innerHTML = maxCount; //初始化剩余雷数 var time = document.getElementById('time'); //计时器
var timer = setInterval(function () {
let seconds = (parseFloat(time.innerHTML) + 0.1).toFixed(1); //保留一位小数
time.innerHTML = seconds;
}, 100) //定时器 100ms执行一次
我们修改下方格点击事件中遍历矩阵的代码,更新剩余地雷数,胜利时结束计时。
//遍历矩阵
let isWin = true;
count.innerHTML = maxCount; //重置剩余地雷数
for (let i = 0; i < row; i++) {
for (let j = 0; j < col; j++) {
let block = grid[i][j]; //找到标记
if (block.innerHTML === '▲') {
count.innerHTML = parseInt(count.innerHTML) - 1; //剩余地雷数-1
} //判断游戏胜利条件(所有的非雷方格已打开)
if (!block.isMine && !block.isOpen) {
//如果有未打开的非雷方块 条件不成立
isWin = false;
}
}
}
if (isWin) {
clearInterval(timer); //游戏胜利结束计时,清除定时器
alert("游戏胜利");
}
再修改踩雷的代码,结束计时。
if (block.isMine) {
//踩雷
block.innerHTML = '雷'; //显示为 '雷'
//遍历矩阵打开所有的地雷方格
for (let i = 0; i < row; i++) {
for (let j = 0; j < col; j++) {
//找到地雷
block = grid[i][j];
if (!block.isOpen && block.isMine) {
op(block); //设置打开状态和样式
block.innerHTML = '雷'; //显示为 '雷'
}
}
}
clearInterval(timer); //游戏结束停止计时,清除定时器
//提示游戏结束
alert("游戏结束");
}
OK,大功告成!!!后续还可以加入选择难度的功能,重新开始按钮,动画效果等等,这个就看你们发挥了!!
完整代码
<!DOCTYPE html>
<html> <head>
<title>扫雷</title>
<style>
#bar {
text-align: center;
margin-bottom:20px;
} .bar {
height: 25px;
width: 150px;
line-height: 25px;
display: inline-block;
border: solid 1px #000;
margin-left: 20px;
margin-right: 20px;
} #grid {
margin: auto;
} .blocks {
width: 30px;
height: 30px;
line-height: 30px;
display: block;
text-align: center;
border: solid 1px #000;
user-select: none;
cursor: pointer;
} .blocks:hover {
background: #0af;
}
</style>
</head> <!-- ondragstart:防拖拽生成新页面 oncontextmenu:屏蔽右键菜单--> <body ondragstart='return false' oncontextmenu='self.event.returnValue=false'> <div id='bar'>
<span class='bar'>剩余雷数:<label id='count'>0</label></span>
<span class='bar'>计时:<label id='time'>0</label>s</span>
</div>
<table id='grid'></table> <script>
var row = 10; //行数
var col = 10; //列数
var maxCount = 10; //最大地雷数量
var isFirstOpen = true; //第一次打开方格
var grid = init_grid(); //初始化
var count = document.getElementById('count'); //剩余雷数
var time = document.getElementById('time'); //计时 //初始化矩阵 (row-行数 col-列数)
function init_grid() { //生成矩阵html <tr>--行标签 <td>--列标签
let gridHtml = '';
for (let i = 0; i < row; i++) {
gridHtml += '<tr>'
for (let j = 0; j < col; j++) {
gridHtml +=
'<td><span class="blocks" onmousedown="block_click(' + i + ',' + j + ',event)"></span></td>';
}
gridHtml += '<tr>'
}
//写入html
document.getElementById('grid').innerHTML = gridHtml; //返回矩阵二维数组
let blocks = document.getElementsByClassName('blocks');
let grid = new Array();
for (let i = 0; i < blocks.length; i++) {
if (i % col === 0) {
grid.push(new Array());
}
//初始化计雷数
blocks[i].count = 0;
grid[parseInt(i / col)].push(blocks[i]);
}
return grid;
} //方格点击事件 _i:坐标i _j:坐标j e:鼠标事件
function block_click(_i, _j, e) { //跳过已打开的方格
if (grid[_i][_j].isOpen) {
return;
} //鼠标左键打开方格
if (e.button === 0) { //第一次打开
if (isFirstOpen) { isFirstOpen = false;
let count = 0; //当前地雷数 //生成地雷
while (count < maxCount) { //生成随机坐标
let ri = Math.floor(Math.random() * row);
let rj = Math.floor(Math.random() * col); //坐标不等于第一次点击方格的坐标 && 非雷方格
if (!(ri === _i && rj === _j) && !grid[ri][rj].isMine) {
grid[ri][rj].isMine = true; //自定义属性isMine代表方格为地雷
count++; //当前地雷数+1 //更新九宫格内非雷方格的计雷数
for (let i = ri - 1; i < ri + 2; i++) {
for (let j = rj - 1; j < rj + 2; j++) {
//判断坐标防越界
if (i > -1 && j > -1 && i < row && j < col) {
//计雷数+1
grid[i][j].count++;
}
}
}
}
}
} //执行打开方格函数
block_open(_i, _j); //打开方格函数
function block_open(_i, _j) { let block = grid[_i][_j];
op(block); //设定打开方格的状态与样式
function op(block) {
block.isOpen = true; //isOpen为自定义属性,设置为true代表已打开
block.style.background = '#ccc'; //将背景设置为灰色
block.style.cursor = 'default'; //将鼠标停留样式设置为默认
} if (block.isMine) {
//踩雷
block.innerHTML = '雷'; //显示为 '雷'
//遍历矩阵打开所有的地雷方格
for (let i = 0; i < row; i++) {
for (let j = 0; j < col; j++) {
//找到地雷
block = grid[i][j];
if (!block.isOpen && block.isMine) {
op(block); //设置打开状态和样式
block.innerHTML = '雷'; //显示为 '雷'
}
}
}
//提示游戏结束
alert("游戏结束");
} else if (block.count === 0) {
//打开计雷数为0的方格
//遍历九宫格内的方格
for (let i = _i - 1; i < _i + 2; i++) {
for (let j = _j - 1; j < _j + 2; j++) {
//判断是否越界&&跳过已打开的方格&&非雷
if (i > -1 && j > -1 && i < row && j < col && !grid[i][j].isOpen && !grid[i][j].ismine) {
//递归打开方格函数
block_open(i, j);
}
}
}
} else {
//打开计雷数不为0的方格
block.innerHTML = block.count; //显示计雷数
} }
}
//鼠标右键标记方格
else if (e.button === 2) { let block = grid[_i][_j];
if (block.innerHTML !== '▲') {
block.innerHTML = '▲';
} else {
block.innerHTML = '';
}
} //判断游戏是否结束(所有的非雷方格已打开)
for (let i = 0; i < row; i++) {
for (let j = 0; j < col; j++) {
if (!grid[i][j].isMine && !grid[i][j].isOpen) {
return;
}
}
}
alert("游戏胜利");
}
</script>
</body> </html>
原生 JS 实现扫雷 (分析+代码实现)的更多相关文章
- 原生js 当前时间 倒计时代码
源:https://www.oschina.net/code/snippet_2318153_54763 <!DOCTYPE html> <html> <head> ...
- [笔记]原生JS实现的DOM操作笔记
原生JS实现的DOM一系列操作参考: 原生JavaScript封装DOM库 siblings: 原生JS-查找相邻的元素-siblings方法的实现 addClass,removeClass,hasC ...
- 导航栏中各按钮在点击当前按钮变色其他按钮恢复为原有色的实现方法(vue、jq、原生js)
一.vue如何实现? 代码: <!DOCTYPE html> <html lang="en"> <head> <meta charset= ...
- Rails Guide--Working with JavaScript in Rails; 如何把jquery转化为原生js
1 An Introduction to Ajax 打开网页的的过程也叫:request response cycel. JavaScript也可以request然后parse the respons ...
- 原生js封装十字参考线插件(一)
需求来源: 拓扑图之机房平面图,显示机房长宽比例尺,房间内标注各种设备间距不易实现,特在机房平面图上层加一个十字参考线 横竖两条线垂直,在鼠标指针处交叉,显示鼠标指针坐标(相对机房平面图的坐标,不是相 ...
- 原生JS与JQ获取元素的区别
刚学JQ不久,有时候可能会把JS和JQ获取元素的方式搞错,接下来获取属性方法什么的就一发不可收拾了,现在把两者获取获取元素的代码整理下. 一.原生JS获取元素. 1.常用的三种方式获取元素对象(将指定 ...
- C#保留2位小数几种场景总结 游标遍历所有数据库循环执行修改数据库的sql命令 原生js轮盘抽奖实例分析(幸运大转盘抽奖) javascript中的typeof和类型判断
C#保留2位小数几种场景总结 场景1: C#保留2位小数,.ToString("f2")确实可以,但是如果这个数字本来就小数点后面三位比如1.253,那么转化之后就会变成1.2 ...
- 原生js复制粘贴上传图片前后台代码,兼容firebox,chrome, ie11,亲测有效
需求:粘贴上传图片,截图工具,右键粘贴,或者ctrl+v粘贴 方法1:可直接套用富文本框的图片上传功能,完成复制粘贴 缺点:麻烦,样式难控制 方法2:用原生js完成,以下案例基于此,样式请自己动手调整 ...
- 原生JS实现购物车结算功能代码+zepto版
html <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3 ...
随机推荐
- 【代码笔记】Java常识性基础补充(二)——数组、ArrayList集合、ASCII编码、 eclipse基础操作、eclipse调试、eclipse项目导入、eclipse快捷键
1.0 如何定义数组以及访问数组中的元素,如下所示.ArrayDemo01.java public class ArrayDemo01 { public static void main(String ...
- Django—XSS及CSRF
一.XSS 跨站脚本攻击(Cross Site Scripting),为不和层叠样式表(Cascading Style Sheets, CSS)的缩写混淆,故将跨站脚本攻击缩写为XSS.恶意攻击者往W ...
- 关于ES7里面的async和await
async / await是ES7的重要特性之一,也是目前社区里公认的优秀异步解决方案.目前,async / await这个特性已经是stage 3的建议,可以看看TC39的进度,本篇文章将分享asy ...
- Eclipse开发工具printf打印方法提示报错的解决方法
最近在学习java,在练习printf方法的使用时按照书上的语法配置却出现了报错.报错内容为:The method printf(String, Object[]) in the type Print ...
- char和varchar的区别
在建立数据库表结构的时候,为了给一个String类型的数据定义一个数据库的数据库类型,一般参考的都是char或者varchar,这两种选择有时候让人很纠结,今天想总结一下它们两者的区别,明确一下两者的 ...
- zookeeper 的监控工具
zookeeper 的监控工具 公司很多产品会使用zookeeper,比如Meta消息中间件,在测试的过程中,我们经常需要查询zookeeper里面的信息来精确定位问题.目前项目中有开 ...
- 在IE浏览器输入测试servlet程序报:HTTP Status 404(The requested resource is not available)错
一.HTTP Status 404(The requested resource is not available)异常主要是路径错误或拼写错误造成的,请按以下步骤逐一排查: 1.未部署Web应用 2 ...
- 判断ORACLE启动时使用spfile还是pfile
自Oracle 9i以后启动的时候默认使用的初始化文件是spfile,我们可以通过如下三种方式来判断是SPFILE还是PFILE方式启动数据库.1.show parameter spfile2.sho ...
- ListView实现下拉刷新(三)实现下拉刷新
该准备的东西都已经准备好了.在这篇文章里,我们就开始实现下拉刷新功能吧. 一.大体的逻辑分析 我们来简单分析一下需要做的逻辑吧.首先分析头布局有几种状态.不下拉时,为正常状态,此时头布局隐藏.下拉到一 ...
- Could not publish to the server. null argument:
启动tomcat或clean tomcat报错:Could not publish to the server. null argument: Could not publish to the ser ...