Java实训项目一一拼图游戏附源码
1 实训基本信息
1.1 实训项目名称
(1)拼图游戏
1.2 实训环境
本次实训内容主要针对专业学生,实训形式以实战讲解为主导。实训课程由实训老师主讲,实训老师教学严谨又平易近人,讲解的内容非常细致和认真,对于重要的知识点内容老师还特意的标注下来,方便同学们回顾学习。
2 实训内容简介
2.1 拼图游戏
拼图游戏内容由若干小图像块组成的,其中有一个空白的小图像块,该图像块是来与别的图像块交换的,以此来实现大图像的拼凑。在Java标准环境下运行,通过鼠标点击图像块上下左右移动,完成大图像的拼凑。
通过所创建的窗体类、菜单、中间面板和左右面板完成设计拼图的交互界面 ,实现游戏登录、开始游戏、退出游戏、选择图片、图片缩放、图片分割、调整难易度、计数计时、打乱图片、判断拼图成功等功能。
游戏可以通过对图片文件的读取和加载,从多张图片中选择来进行拼图游戏,通过设置图像块的个数来实现对游戏难易度的调整,最后将游戏完成后所用时间和步数记录到游戏记录中,方便用户查看游戏记录。
3 项目开发过程
3.1 拼图游戏
3.1.1开发步骤
1、了解拼图游戏基本功能:
拼图游戏内容由若干小图像块组成的,通过鼠标点击图像块上下左右移动,完成图像的拼凑。
2、拼图游戏交互界面设计与开发:
通过创建窗体类、菜单、中间面板和左右面板完成设计拼图的交互界面 ,实现拼图游戏的基本功能。
3、图片的加载与分割:
使用Image类实现图片的缩放,ImageIO类实现图片的读写加载,通过接口类Icon,BufferedImage类获取BufferedImage类的对象实现图片分割。
4、图片随机打乱和交换:
产生随机数 Random rand=new Random();
rand.nextInt(hs*ls)------[0,8]
具体操作:生成两个随机数表示数组下标,互换两个数组元素的位置,按钮的方法getX和getY可以获取按钮的坐标,利用按钮的单击事件的处理ActionListener可以使其图片交换。
5、判赢:
当用户移动按钮后进行判断,代码写在监听器的actionPerformed方法中,判断拼图是否成功,主要取决于每一个按钮通过索引下标获取的位置值,与当前按钮的位置值是否相同。
6、计时和计数功能的实现:
计时功能的实现主要是线程的设计,线程的定义方法:第一:继承Thread类,第二:实现Runnable接口,创建带实现接口的子类对象的Thread对象,MainJFrame实现Runnable接口,重写run方法;而计数则在主窗体中通过rp.times实现对变量的使用来计数。
7、游戏记录的保存:
当用户拼图成功后,记录当前信息到文件中,FileWriter追加写信息,FileReader完成读取数据。
4 游戏截图
5 程序源代码
登录界面部分(用户名:admin 密码:123)
-
import java.awt.Color;
-
import java.awt.Font;
-
import java.awt.GridLayout;
-
import java.awt.event.ActionEvent;
-
import java.awt.event.ActionListener;
-
-
import javax.swing.JButton;
-
import javax.swing.JFrame;
-
import javax.swing.JLabel;
-
import javax.swing.JOptionPane;
-
import javax.swing.JPasswordField;
-
import javax.swing.JTextField;
-
-
public class LoginPintu extends JFrame{
-
JLabel jl1,jl2,jl3,jl4;
-
JTextField jtf;//文本框
-
JPasswordField jpf;//密码
-
JButton jb1,jb2;
-
public LoginPintu() {
-
this.setTitle("拼图游戏");
-
setBounds(400,350,500,400);
-
//设置窗体为流式布局
-
setLayout(new GridLayout(20,1));
-
//空布局
-
setLayout(null);
-
init();
-
setVisible(true);
-
setDefaultCloseOperation(EXIT_ON_CLOSE);
-
jb1.addActionListener(new ActionListener(){
-
public void actionPerformed(ActionEvent e) {
-
if(jtf.getText().trim().equals("admin")&&
-
new String(jpf.getPassword()).trim().equals("123"))
-
{JOptionPane.showMessageDialog(null, "欢迎进入游戏!");
-
new MainJFrame();}
-
else if(jtf.getText().trim().length()==0||
-
new String(jpf.getPassword()).trim().length()==0)
-
{JOptionPane.showMessageDialog(null, "用户名或密码不能为空!");}
-
else {JOptionPane.showMessageDialog(null, "用户名或密码错误!");}
-
}
-
});
-
jb2.addActionListener(new ActionListener() {
-
public void actionPerformed(ActionEvent e) {
-
// System.exit(0);
-
//获取事件源对象
-
JButton jb=(JButton)e.getSource();
-
jtf.setText(jb.getText());
-
}
-
});
-
-
}
-
public void init() {
-
jl1=new JLabel("拼图游戏登录窗口");
-
jl2=new JLabel("用户名:");
-
jl3=new JLabel("密码:");
-
jtf=new JTextField(10);
-
jpf=new JPasswordField(10);
-
jb1=new JButton("登录");
-
jb2=new JButton("取消");
-
jl1.setBounds(150,30,200,60);
-
jl2.setBounds(100, 120, 180, 30);
-
jtf.setBounds(200, 120, 180, 30);
-
jl3.setBounds(100, 180, 180, 30);
-
jpf.setBounds(200, 180, 180, 30);
-
jb1.setBounds(100, 260, 100, 30);
-
jb2.setBounds(220, 260, 100, 30);
-
Font font = new Font("楷体",Font.PLAIN,25);
-
jl1.setFont(font);
-
jl1.setForeground(Color.red);
-
add(jl1);
-
add(jl2);
-
add(jtf);
-
add(jl3);
-
add(jpf);
-
add(jb1);
-
add(jb2);
-
}
-
public static void main(String[] args) {
-
new LoginPintu();
-
}
-
}
左面板部分
-
import java.awt.Image;
-
import java.net.MalformedURLException;
-
import java.net.URL;
-
-
import javax.swing.ImageIcon;
-
import javax.swing.JLabel;
-
import javax.swing.JPanel;
-
//左面板类
-
public class LeftJPanel extends JPanel {
-
JLabel jl;
-
int width=700;
-
int height=700;
-
-
//构造方法
-
//标签创建,指定图片,放置到面板中
-
public LeftJPanel(){
-
//左面板大小
-
setSize(width,height);
-
jl=new JLabel();
-
jl.setSize(width,height);
-
//把标签添加到面板中
-
this.add(jl);
-
}
-
public void init(URL url){
-
-
-
//绝对路径:访问文件是从盘符开始
-
// ImageIcon icon=new ImageIcon("D:\\1picture\\s4.jpg");
-
//相对路径:访问路径不是从盘符开始,可以是\,也可以是一个文件夹
-
// ImageIcon icon=new ImageIcon("s4.jpg");//参数是字符串的相对路径,相对于当前项目根目录
-
//相对路径下url的获取
-
-
// //绝对路径的url的获取
-
// URL url=null;
-
// try {
-
// url = new URL("file:\\D:\\1picture\\5.jpg");
-
// } catch (MalformedURLException e) {
-
// // TODO Auto-generated catch block
-
// e.printStackTrace();
-
// }
-
ImageIcon icon=new ImageIcon(url);
-
//方法一:图片缩放
-
// Image image = icon.getImage();
-
// Image image2 = image.getScaledInstance(700, 700, 1);
-
// ImageIcon icon2 = new ImageIcon(image2);
-
// jl.setIcon(icon2);
-
//链式编程方式实现图片缩放
-
jl.setIcon(new ImageIcon(icon.getImage().getScaledInstance(width, height, 1)));
-
//刷新界面
-
validate();
-
}
-
}
右面板部分
-
import java.awt.Image;
-
import java.awt.event.ActionEvent;
-
import java.awt.event.ActionListener;
-
import java.awt.image.BufferedImage;
-
import java.io.FileInputStream;
-
import java.io.FileWriter;
-
import java.io.IOException;
-
import java.net.URL;
-
import java.util.Random;
-
-
import javax.imageio.ImageIO;
-
import javax.swing.ImageIcon;
-
import javax.swing.JButton;
-
import javax.swing.JOptionPane;
-
import javax.swing.JPanel;
-
-
import jdk.jfr.events.FileWriteEvent;
-
//右面板实现ActionListener接口,右面板也就成为了监听器
-
public class RightJPanel extends JPanel implements ActionListener{
-
-
//面板的大小
-
int width=700;
-
int height=700;
-
//定义按钮数组
-
JButton[] jbs;
-
//设置分割的行列数
-
int hs=2,ls=2;
-
//按钮的宽度和高度,指定是小图图片缩放的尺寸
-
int widthbut,heightbut;
-
-
//图片原始高度宽度
-
int widthtp,heighttp;
-
-
//小图的原始宽度高度
-
int widthxt,heightxt;
-
-
//实现步数计算的变量
-
int times;
-
-
//空白按钮
-
JButton kb;
-
public RightJPanel(){
-
//面板布局是空布局
-
setLayout(null);
-
setSize(width,height);
-
//init();
-
-
}
-
//创建按钮,并放置到右面板
-
public void init(URL url) {
-
//面板组件初始化前,先清除所有已有的组件
-
this.removeAll();
-
//创建按钮数组
-
jbs=new JButton[hs*ls];
-
//为每一个按钮实现初始化
-
//计算按钮的宽度和高度
-
//面板是700*700,拆分成3*3的9个区域
-
//每一块区域的宽度 700/3
-
//每一块区域的高度 700/3
-
widthbut=width/ls;
-
heightbut=height/hs;
-
-
BufferedImage buf=null;
-
try {
-
buf = ImageIO.read(url);
-
//获取原图的宽度、高度
-
widthtp=buf.getWidth();
-
heighttp=buf.getHeight();
-
//获取小图的宽度和高度
-
widthxt=widthtp/ls;
-
heightxt=heighttp/hs;
-
//每一块按钮的坐标位置确定
-
for(int i=0;i<jbs.length;i ){
-
jbs[i]=new JButton();
-
jbs[i].setSize(widthbut,heightbut);
-
//jbs[i].setText(i "");
-
//添加按钮前要确定坐标位置
-
//横坐标 i=0 0 i=1 233 i=2 466
-
//i=3 0 i=4 233
-
//纵坐标 i=3
-
jbs[i].setLocation((i%ls)*widthbut, i/ls*heightbut);
-
//jbs[i].setIcon(null);
-
//小图的获取
-
BufferedImage subimage = buf.getSubimage(i%ls*widthxt, i/ls*heightxt, widthxt, heightxt);
-
//小图的缩放
-
Image image = subimage.getScaledInstance(widthbut, heightbut, 1);
-
//将小图图片放置到按钮上
-
jbs[i].setIcon(new ImageIcon(image));
-
//添加按钮到右面板
-
add(jbs[i]);
-
//设置按钮不可用
-
jbs[i].setEnabled(false);
-
//设置按钮的监听,当按钮被单击,会到右面板中找actionPerformed方法执行
-
jbs[i].addActionListener(this);
-
}
-
jbs[hs*ls-1].setIcon(null);
-
kb=jbs[hs*ls-1];
-
} catch (IOException e) {
-
// TODO Auto-generated catch block
-
e.printStackTrace();
-
}
-
-
-
}
-
-
//打乱按钮在面板中显示的顺序
-
public void randomOrder(){
-
//创建随机数对象
-
Random rand=new Random();
-
//打乱多次
-
for(int i=0;i<hs*ls;i ){
-
//随机索引
-
int index1=rand.nextInt(hs*ls);
-
int index2=rand.nextInt(hs*ls);
-
int x1=jbs[index1].getX();
-
int y1=jbs[index1].getY();
-
int x2=jbs[index2].getX();
-
int y2=jbs[index2].getY();
-
jbs[index1].setLocation(x2, y2);
-
jbs[index2].setLocation(x1, y1);
-
jbs[i].setEnabled(true);
-
}
-
}
-
-
//按钮的单击事件执行的代码
-
@Override
-
public void actionPerformed(ActionEvent e) {
-
// 判断单击按钮和空白按钮是否相邻,如果相邻,则位置互换
-
//获取用户单击的按钮 ,通过ActionEvent e的方法gerSource获取事件源
-
JButton jb=(JButton)(e.getSource());
-
//获取单击按钮和空白按钮的坐标
-
int x1=jb.getX();
-
int y1=jb.getY();
-
int x2=kb.getX();
-
int y2=kb.getY();
-
//判断是否可以移动
-
//Math.abs(x1-x2)/widthbut Math.abs(y1-y2)/heightbut==1
-
if (Math.abs(x1-x2)/widthbut Math.abs(y1-y2)/heightbut==1){
-
jb.setLocation(x2, y2);
-
kb.setLocation(x1, y1);
-
times ;
-
}
-
//判断是否拼图成功
-
if (isWin()){
-
JOptionPane.showMessageDialog(null, "恭喜你,拼图成功");
-
//使得按钮不可用
-
for(int i=0;i<jbs.length;i ){
-
jbs[i].setEnabled(false);
-
}
-
//提示用户输入名称
-
//使用输入对话框
-
String name = JOptionPane.showInputDialog("请输入你的姓名:");
-
String info = hs "*" ls "拼图记录:" name "的步数是:" times "\r\n";
-
JOptionPane.showMessageDialog(null, hs "*" ls "拼图记录:" name "的步数是:" times "\r\n");
-
try {
-
FileWriter fw = new FileWriter("D:\\游戏记录.dat",true);
-
fw.write(info);
-
fw.close();
-
}catch (IOException e1) {
-
e1.printStackTrace();
-
}
-
}
-
-
}
-
-
//判断是否拼图成功
-
public boolean isWin() {
-
-
//获取每一个按钮的坐标
-
for(int i=0;i<jbs.length;i ){
-
//jbs[i].setLocation((i%ls)*widthbut, i/ls*heightbut);由之前坐标设置给出下面的x,y
-
int x=jbs[i].getX()/widthbut;
-
int y=jbs[i].getY()/heightbut;
-
//判断,通过下标值,也可以获取按钮的坐标 横坐标 i%ls 纵坐标 i/ls
-
if (i%ls!=x || i/ls!=y ){
-
return false;
-
}
-
}
-
return true;
-
}
-
}
游戏功能部分
-
import java.awt.Color;
-
import java.awt.GridLayout;
-
import java.awt.event.ActionEvent;
-
import java.awt.event.ActionListener;
-
import java.io.File;
-
import java.io.FileReader;
-
import java.io.IOException;
-
import java.net.MalformedURLException;
-
import java.net.URL;
-
-
import javax.swing.ButtonGroup;
-
import javax.swing.JFileChooser;
-
import javax.swing.JFrame;
-
import javax.swing.JLabel;
-
import javax.swing.JMenu;
-
import javax.swing.JMenuBar;
-
import javax.swing.JMenuItem;
-
import javax.swing.JOptionPane;
-
import javax.swing.JPanel;
-
import javax.swing.JRadioButtonMenuItem;
-
import javax.swing.filechooser.FileNameExtensionFilter;
-
-
public class MainJFrame extends JFrame implements Runnable{
-
-
//菜单
-
//菜单栏
-
JMenuBar jmenubar;
-
//菜单 菜单、等级、帮助
-
JMenu menu,menuclass,menuhelp;
-
//菜单项 开始、退出、图片更换、关于游戏、游戏记录、清空记录
-
JMenuItem itembegin,itemend,itemchange,itemabout,itemrecord,itemclear;
-
//单选菜单项 简单、一般、困难
-
JRadioButtonMenuItem itemeasy,itemnormal,itemhard;
-
//中间面板
-
JPanel jp;
-
//左面板
-
LeftJPanel lp;
-
//右面板
-
RightJPanel rp;
-
//访问的图片
-
URL url;
-
//显示计时标签
-
JLabel total_time;
-
//起止时间
-
long startTime,endTime;
-
//创建线程对象,实现计时功能
-
Thread th;
-
//显示步数的标签
-
JLabel total_count;
-
//构造方法
-
public MainJFrame(){
-
//标题设置
-
setTitle("拼图游戏");
-
//窗体大小
-
setSize(1440, 780);
-
//窗体位置在容器/屏幕的正中间
-
setLocationRelativeTo(null);
-
//窗体大小不可变
-
setResizable(false);
-
//实现界面菜单初始化
-
//创建一个线程对象
-
th=new Thread(this);
-
//界面菜单初始化
-
menuinit();
-
//各面板的初始化
-
init();
-
setDefaultCloseOperation(EXIT_ON_CLOSE);
-
setVisible(true);
-
//开始菜单
-
itembegin.addActionListener(new ActionListener() {
-
-
@Override
-
public void actionPerformed(ActionEvent e) {
-
//启动线程
-
//如果线程没有启动,则调用start方法启动
-
if(!th.isAlive()) th.start();
-
startTime=System.currentTimeMillis();
-
rp.times=0;
-
rp.randomOrder();
-
}
-
});
-
//结束游戏
-
itemend.addActionListener(new ActionListener() {
-
-
@Override
-
public void actionPerformed(ActionEvent e) {
-
System.exit(1);
-
}
-
});
-
//选择难易度itemeasy,itemnormal,itemhard
-
itemeasy.addActionListener(new ActionListener() {
-
-
@Override
-
public void actionPerformed(ActionEvent e) {
-
//第一,传递2*2到右面板
-
rp.hs=2;
-
rp.ls=2;
-
//第二,调用右面板组件初始化的方法
-
rp.init(url);
-
}
-
});
-
itemnormal.addActionListener(new ActionListener() {
-
-
@Override
-
public void actionPerformed(ActionEvent e) {
-
//第一,传递3*3到右面板
-
rp.hs=3;
-
rp.ls=3;
-
//第二,调用右面板组件初始化的方法
-
rp.init(url);
-
}
-
});
-
itemhard.addActionListener(new ActionListener() {
-
-
@Override
-
public void actionPerformed(ActionEvent e) {
-
//第一,传递4*4到右面板
-
rp.hs=4;
-
rp.ls=4;
-
//第二,调用右面板组件初始化的方法
-
rp.init(url);
-
}
-
});
-
//游戏记录显示
-
itemrecord.addActionListener(new ActionListener() {
-
-
@Override
-
public void actionPerformed(ActionEvent e) {
-
//info存储要显示的内容
-
String info="";
-
try {
-
//判断文件是否存在
-
File f = new File("D:\\游戏记录.dat");
-
if(f.exists()) {
-
//创建指向***的文件字符输入流对象
-
FileReader fr = new FileReader("D:\\游戏记录.dat");
-
//读取数据
-
char[] chs = new char[1024];
-
int len;
-
while((len=fr.read(chs))!=-1) {
-
//读取的结果放在info中
-
info =new String(chs,0,len);
-
}
-
-
fr.close();
-
//通过消息框显示结果
-
JOptionPane.showMessageDialog(null, info);
-
}else {
-
JOptionPane.showMessageDialog(null, "游戏记录为空!");
-
}
-
}catch (IOException e1) {
-
e1.printStackTrace();
-
}
-
}
-
});
-
//关于游戏
-
itemabout.addActionListener(new ActionListener() {
-
-
@Override
-
public void actionPerformed(ActionEvent e) {
-
JOptionPane.showMessageDialog(null, "关于拼图游戏\r\n版本:v2.0\r\n作者:LWL\r\n欢迎进入游戏!");
-
}
-
});
-
//清空记录
-
itemclear.addActionListener(new ActionListener() {
-
-
@Override
-
public void actionPerformed(ActionEvent e) {
-
File f = new File("D:\\游戏记录.dat");
-
if(f.exists()) {
-
f.delete();
-
}
-
}
-
});
-
//实现图片的更换
-
itemchange.addActionListener(new ActionListener() {
-
-
@Override
-
public void actionPerformed(ActionEvent e) {
-
//显示一个打开对话框,选择一个图片文件,将文件转换成url对象,调用左右面板的相应方法
-
JFileChooser jfc=new JFileChooser();
-
//设置文件的扩展名
-
jfc.setFileFilter(new FileNameExtensionFilter("图片格式(jpg|png|gif|jpeg)", "jpg","png","gif","jpeg"));
-
//弹出打开对话框
-
int sd = jfc.showOpenDialog(MainJFrame.this);
-
if (sd==jfc.APPROVE_OPTION)//如果用户选择了打开按钮
-
{
-
//获取用户选择的文件完整名称
-
String file=jfc.getSelectedFile().getAbsolutePath();
-
try {
-
url=new URL("file:\\" file);
-
//更新两个面板的图片
-
lp.init(url);
-
rp.init(url);
-
} catch (MalformedURLException e1) {
-
// TODO Auto-generated catch block
-
e1.printStackTrace();
-
}
-
}
-
}
-
});
-
}
-
-
public void init() {
-
jp=new JPanel();
-
//设置中间面板的布局方式
-
jp.setLayout(new GridLayout(1,2));
-
//提供左右面板的图片
-
url=this.getClass().getResource("小狗.jpg");
-
//创建左面板
-
lp=new LeftJPanel();
-
//对标签初始化
-
lp.init(url);
-
//将左面板添加到中间面板
-
jp.add(lp);
-
//创建右面板
-
rp=new RightJPanel();
-
//右面板的按钮初始化
-
rp.init(url);
-
//将右面板添加到中间面板
-
jp.add(rp);
-
//将中间面板添加到窗体
-
add(jp);
-
}
-
-
public void menuinit() {
-
jmenubar=new JMenuBar();
-
menu=new JMenu("菜单");
-
menuclass=new JMenu("等级");
-
menuhelp=new JMenu("帮助");
-
itembegin=new JMenuItem("开始游戏");
-
itemend=new JMenuItem("结束游戏");
-
itemchange=new JMenuItem("更换图片");
-
itemabout=new JMenuItem("关于游戏");
-
itemrecord=new JMenuItem("游戏记录");
-
itemclear=new JMenuItem("清空记录");
-
itemeasy=new JRadioButtonMenuItem("简单");
-
itemnormal=new JRadioButtonMenuItem("一般");
-
itemhard=new JRadioButtonMenuItem("困难");
-
//为单选菜单分组,实现多选一
-
ButtonGroup bg=new ButtonGroup();
-
bg.add(itemeasy);
-
bg.add(itemnormal);
-
bg.add(itemhard);
-
//添加菜单
-
menu.add(itembegin);
-
menu.add(itemend);
-
menu.add(itemchange);
-
-
menuclass.add(itemeasy);
-
menuclass.add(itemnormal);
-
menuclass.add(itemhard);
-
-
menuhelp.add(itemabout);
-
menuhelp.add(itemrecord);
-
menuhelp.add(itemclear);
-
-
jmenubar.add(menu);
-
jmenubar.add(menuclass);
-
jmenubar.add(menuhelp);
-
-
//菜单栏添加到窗体
-
this.setJMenuBar(jmenubar);
-
itemeasy.setSelected(true);
-
//创建一个线程对象
-
th=new Thread(this);
-
total_time=new JLabel("用时:");
-
total_time.setForeground(Color.red);
-
jmenubar.add(new JLabel(" "));
-
jmenubar.add(total_time);
-
total_count=new JLabel("步数:");
-
total_count.setForeground(Color.red);
-
jmenubar.add(new JLabel(" "));
-
jmenubar.add(total_count);
-
}
-
-
public static void main(String[] args) {
-
new MainJFrame();
-
}
-
//实现计时并定时显示的run()方法
-
@Override
-
public void run() {
-
while(true) {
-
endTime=System.currentTimeMillis();
-
total_time.setText("用时:" (endTime-startTime)/1000 "秒");
-
total_count.setText("步数:第" rp.times "步");
-
try {
-
Thread.sleep(500);
-
}catch (InterruptedException e) {
-
e.printStackTrace();
-
}
-
}
-
}
-
}
这篇好文章是转载于:学新通技术网
- 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
- 本站站名: 学新通技术网
- 本文地址: /boutique/detail/tanhicbfef
-
深受java开发者喜爱的顶级Java IDE
-
STM32开发笔记107安装STM32CubeIDE
-
ecliphotoshope新建Maven报错Could not initialize class org.apache.maven.plugin.war.util.WebappStructureSerialize
-
ecliphotoshope项目导入idea流程 ecliphotoshope项目导入idea404解决办法
-
No core dump will be written. Minidumphotoshop are not enabled by default on client versions of Windows
-
用JAVA建服务器,TCP/IP实现服务器客户端互联
-
photoshop保存的图片太大微信发不了怎么办
PHP中文网 06-15 -
word里面弄一个表格后上面的标题会跑到下面怎么办
PHP中文网 06-20 -
photoshop扩展功能面板显示灰色怎么办
PHP中文网 06-14 -
《学习通》视频自动暂停处理方法
HelloWorld317 07-05 -
TikTok加速器哪个好免费的TK加速器推荐
TK小达人 10-01 -
Android 11 保存文件到外部存储,并分享文件
Luke 10-12 -
微信公众号没有声音提示怎么办
PHP中文网 03-31 -
excel下划线不显示怎么办
PHP中文网 06-23 -
微信运动停用后别人还能看到步数吗
PHP中文网 07-22 -
excel打印预览压线压字怎么办
PHP中文网 06-22