最新要闻
- 水溶性笔是什么意思?水溶性笔和油性笔的区别是什么?
- 白炽灯色温是多少?白炽灯色温4000-4600K是多少度?
- 八字有多少种排列组合?八字有食神代表什么?
- 荷月是几月的别称?十二个月的别称和雅称
- 酒店的英语怎么说?酒店前台的工作内容是什么?
- 当前快播:河北用上了无人驾驶农机:北斗加持 坐在屋里就把地给种了
- 今日要闻!奔驰、宝马、特斯拉相继取消收音机!广播公司担忧:有隐患
- 换机周期将创历史新高:长达43个月 你多久换一次?
- 波音、空客狂喜 印度将签史诗级飞机订单:一次买下500架
- 天天实时:创建全国首个ICU!我国重症医学奠基人陈德昌离世 享年90岁
- 【世界新要闻】2799元配酷睿i5!小米迷你主机将开启众筹 便宜900元条件公布
- 环球热门:小米13黑色版图赏:直屏直边手感秒杀iPhone 14!
- 真爱粉!有米粉花近万元抢了两台小米13
- 世界快资讯:好评率超99% 小米MIX Fold 2推出月光银配色 雷军:镜面效果令人惊叹
- 天天热点评!比RTX 4090便宜5000 AMD RX 7900系列显卡年底供货20万块:备货充足
- 小米13系列供不应求!线上已售罄 3999元起
手机
iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
- 警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- 男子被关545天申国赔:获赔18万多 驳回精神抚慰金
- 3天内26名本土感染者,辽宁确诊人数已超安徽
- 广西柳州一男子因纠纷杀害三人后自首
- 洱海坠机4名机组人员被批准为烈士 数千干部群众悼念
家电
世界微头条丨零基础开启元宇宙|如何快速创建虚拟形象
元宇宙(Metaverse),是人类运用数字技术构建的,由现实世界映射或超越现实世界,可与现实世界交互的虚拟世界,具备新型社会体系的数字生活空间。
(资料图片仅供参考)
可见元宇宙第一步是创建专属虚拟形象,但创建3D虚拟形象需要3D基础知识。对于大部分android
开发者(包括我本人)来说没有这方面的积累。难道因此我们就难以进入元宇宙的世界吗?不,今天我们借助即构平台提供的Avatar SDK,只要有Android
基础即可进入最火的元宇宙世界!先看效果:
上面gif被压缩的比较狠,这里放一张截图:
1 免费注册即构开发者
前往即构控制台网站:https://console.zego.im/注册开发者账户。注册成功后,创建项目:
控制台中可以得到AppID和AppSign两个数据,这两个数据是重要凭证,后面会用到。
由于我们用到了即构的Avatar
功能,但目前官方没有提供线上自动开启方式,需要主动找客服申请(当然,这是免费的),只需提供自己项目的包名,即可开通Avatar权限。打开https://doc-zh.zego.im/article/15206右下角有“联系我们”
,点击即可跟客服申请免费开通权限。
注意,如果不向客服申请Avatar
权限,调用AvatarSDK
会失败!
2 准备开发环境
前往即构官方元宇宙开发SDK网站https://doc-zh.zego.im/article/15302下载SDK,得到如下文件列表:
接下来过程如下:
- 打开
SDK
目录,将里面的ZegoAvatar.aar
拷贝至app/libs
目录下。 - 添加
SDK
引用。打开app/build.gradle
文件,在dependencies
节点引入libs
下所有的jar
和aar
:
dependencies { implementation fileTree(dir: "libs", include: ["*.jar", "*.aar"]) //通配引入 //其他略}
- 设置权限。根据实际应用需要,设置应用所需权限。进入
app/src/main/AndroidManifest.xml
文件,添加权限。
因为
Android 6.0
在一些比较重要的权限上要求必须申请动态权限,不能只通过AndroidMainfest.xml
文件申请静态权限。具体动态请求权限代码可看附件源码。
3 导入资源
上一小节下载的zip
文件中,有个assets
目录。里面包含了Avatar
形象相关资源,如:衣服、眉毛、鞋子等。这是即构官方免费提供的资源,可以满足一般性需求了。当然了,如果想要自己定制资源也是可以的。assets
文件内容如下:
资源名称 | 说明 |
---|---|
AIModel.bundle | Avatar 的 AI 模型资源。当使用表情随动、声音随动、AI 捏脸等能力时,必须先将该资源的绝对路径设置给 Avatar SDK。 |
base.bundle | 美术资源,包含基础 3D 人物模型资源、资源映射表、人物模型默认外形等。 |
Packages | 美妆、挂件、装饰等资源。每个资源 200 KB ~ 1 MB 不等,跟资源复杂度相关。 |
注意:由于资源文件很大,上面下载的美术资源只包含少量必须资源。如果需要全部资源,可以去官网找客服索要:https://doc-zh.zego.im/article/14882
上面的文件需要存放到Android
本地SDCard
上,这里有2个方案可供参考:
- 方案一: 将资源先放入到
app/src/assets
目录内,然后在app启动时,自动将assets的内容拷贝到SDcard
中。 - 方案二: 将资源放入到服务器端,运行时自动从服务器端下载。
为了简单起见,我们这里采用方案一。参考代码如下, 详细代码可以看附件:
AssetsFileTransfer.copyAssetsDir2Phone(this.getApplication(), "AIModel.bundle", "assets");AssetsFileTransfer.copyAssetsDir2Phone(this.getApplication(), "base.bundle", "assets");AssetsFileTransfer.copyAssetsDir2Phone(this.getApplication(), "Packages", "assets");
4 创建虚拟形象
创建虚拟形象本质上来说就是调用即构的Avatar SDK
,其大致流程如下:
接下来我们逐步实现上面流程。
需要注意的是,上面示意图中采用的是AvatarView,可以非常方便的直接展示Avatar形象,但是不方便后期将画面通过RTC实时传递, 因此,我们后面的具体实现视通过TextureView替代AvatarView。
4.1 申请权鉴
这里再次强调一下,一定要打开https://doc-zh.zego.im/article/15206点击右下角有“联系我们”
,向客服申请免费开通Avatar权限。否则无法使用Avatar SDK
。
申请权鉴代码如下:
public class KeyCenter { // 控制台地址: https://console.zego.im/dashboard public static long APP_ID = 这里值可以在控制台查询,参考第一节; //这里填写APPID public static String APP_SIGN = 这里值可以在控制台查询,参考第一节; // 鉴权服务器的地址 public final static String BACKEND_API_URL = "https://aieffects-api.zego.im?Action=DescribeAvatarLicense"; public static String avatarLicense = null; public static String getURL(String authInfo) { Uri.Builder builder = Uri.parse(BACKEND_API_URL).buildUpon(); builder.appendQueryParameter("AppId", String.valueOf(APP_ID)); builder.appendQueryParameter("AuthInfo", authInfo); return builder.build().toString(); } public interface IGetLicenseCallback { void onGetLicense(int code, String message, ZegoLicense license); } /** * 在线拉取 license * @param context * @param callback */ public static void getLicense(Context context, final IGetLicenseCallback callback) { requestLicense(ZegoAvatarService.getAuthInfo(APP_SIGN, context), callback); } /** * 获取license * */ public static void requestLicense(String authInfo, final IGetLicenseCallback callback) { String url = getURL(authInfo); HttpRequest.asyncGet(url, ZegoLicense.class, (code, message, responseJsonBean) -> { if (callback != null) { callback.onGetLicense(code, message, responseJsonBean); } }); } public class ZegoLicense { @SerializedName("License") private String license; public String getLicense() { return license; } public void setLicense(String license) { this.license = license; } }}
在获取Lincense时,只需调用getLicense
函数,例如在Activity
类中只需如下调用:
KeyCenter.getLicense(this, (code, message, response) -> { if (code == 0) { KeyCenter.avatarLicense = response.getLicense(); showLoading("正在初始化..."); avatarMngr = AvatarMngr.getInstance(getApplication()); avatarMngr.setLicense(KeyCenter.avatarLicense, this); } else { toast("License 获取失败, code: " + code); } });
4.2 初始化AvatarService
初始化AvatarService过程比较漫长(可能要几秒),通过开启worker线程后台加载以避免主线程阻塞。因此我们定义一个回调函数,待完成初始化后回调通知:
public interface OnAvatarServiceInitSucced { void onInitSucced();}public void setLicense(String license, OnAvatarServiceInitSucced listener) { this.listener = listener; ZegoAvatarService.addServiceObserver(this); String aiPath = FileUtils.getPhonePath(mApp, "AIModel.bundle", "assets"); // AI 模型的绝对路径 ZegoServiceConfig config = new ZegoServiceConfig(license, aiPath); ZegoAvatarService.init(mApp, config);}@Overridepublic void onStateChange(ZegoAvatarServiceState state) { if (state == ZegoAvatarServiceState.InitSucceed) { Log.i("ZegoAvatar", "Init success"); // 要记得及时移除通知 ZegoAvatarService.removeServiceObserver(this); if (listener != null) listener.onInitSucced(); }}
这里setLicense
函数内完成初始化AvatarService
,初始化完成后会回调onStateChange函数。但是要注意,在初始化之前必须把资源文件拷贝到本地SDCard,即完成资源导入:
private void initRes(Application app) { // 先把资源拷贝到SD卡,注意:线上使用时,需要做一下判断,避免多次拷贝。资源也可以做成从网络下载。 if (!FileUtils.checkFile(app, "AIModel.bundle", "assets")) FileUtils.copyAssetsDir2Phone(app, "AIModel.bundle", "assets"); if (!FileUtils.checkFile(app, "base.bundle", "assets")) FileUtils.copyAssetsDir2Phone(app, "base.bundle", "assets"); if (!FileUtils.checkFile(app, "human.bundle", "assets")) FileUtils.copyAssetsDir2Phone(app, "human.bundle", "assets"); if (!FileUtils.checkFile(app, "Packages", "assets")) FileUtils.copyAssetsDir2Phone(app, "Packages", "assets");}
4.3 创建虚拟形象
前面在https://doc-zh.zego.im/article/15302下载SDK
包含了helper
目录,这个目录里面有非常重要的两个文件:
其中ZegoCharacterHelper
文件是个接口定义类,即虽然是个类,但具体的实现全部在ZegoCharacterHelperImpl
中。我们先一睹为快,看看ZegoCharacterHelper
包含了哪些可处理的属性:
public class ZegoCharacterHelper { public static final String MODEL_ID_MALE = "male"; public static final String MODEL_ID_FEMALE = "female"; //****************************** 捏脸维度的 key 值 ******************************/ public static final String FACESHAPE_BROW_SIZE_Y = "faceshape_brow_size_y";// 眉毛厚度, 取值范围0.0-1.0,默认值0.5 public static final String FACESHAPE_BROW_SIZE_X = "faceshape_brow_size_x";// 眉毛长度, 取值范围0.0-1.0,默认值0.5 public static final String FACESHAPE_BROW_ALL_Y = "faceshape_brow_all_y";// 眉毛高度, 取值范围0.0-1.0,默认值0.5 public static final String FACESHAPE_BROW_ALL_ROLL_Z = "faceshape_brow_all_roll_z";// 眉毛旋转, 取值范围0.0-1.0,默认值0.5 public static final String FACESHAPE_EYE_SIZE = "faceshape_eye_size"; // 眼睛大小, 取值范围0.0-1.0,默认值0.5 public static final String FACESHAPE_EYE_SIZE_Y = "faceshape_eye_size_y"; public static final String FACESHAPE_EYE_ROLL_Y = "faceshape_eye_roll_y";// 眼睛高度, 取值范围0.0-1.0,默认值0.5 public static final String FACESHAPE_EYE_ROLL_Z = "faceshape_eye_roll_z";// 眼睛旋转, 取值范围0.0-1.0,默认值0.5 public static final String FACESHAPE_EYE_X = "faceshape_eye_x";// 双眼眼距, 取值范围0.0-1.0,默认值0.5 public static final String FACESHAPE_NOSE_ALL_X = "faceshape_nose_all_x";// 鼻子宽度, 取值范围0.0-1.0,默认值0.5 public static final String FACESHAPE_NOSE_ALL_Y = "faceshape_nose_all_y";// 鼻子高度, 取值范围0.0-1.0,默认值0.5 public static final String FACESHAPE_NOSE_SIZE_Z = "faceshape_nose_size_z"; public static final String FACESHAPE_NOSE_ALL_ROLL_Y = "faceshape_nose_all_roll_y";// 鼻头旋转, 取值范围0.0-1.0,默认值0.5 public static final String FACESHAPE_NOSTRIL_ROLL_Y = "faceshape_nostril_roll_y";// 鼻翼旋转, 取值范围0.0-1.0,默认值0.5 public static final String FACESHAPE_NOSTRIL_X = "faceshape_nostril_x"; public static final String FACESHAPE_MOUTH_ALL_Y = "faceshape_mouth_all_y";// 嘴巴上下, 取值范围0.0-1.0,默认值0.5 public static final String FACESHAPE_LIP_ALL_SIZE_Y = "faceshape_lip_all_size_y";// 嘴唇厚度, 取值范围0.0-1.0,默认值0.5 public static final String FACESHAPE_LIPCORNER_Y = "faceshape_lipcorner_y";// 嘴角旋转, 取值范围0.0-1.0,默认值0.5 public static final String FACESHAPE_LIP_UPPER_SIZE_X = "faceshape_lip_upper_size_x"; // 上唇宽度, 取值范围0.0-1.0,默认值0.5 public static final String FACESHAPE_LIP_LOWER_SIZE_X = "faceshape_lip_lower_size_x"; // 下唇宽度, 取值范围0.0-1.0,默认值0.5 public static final String FACESHAPE_JAW_ALL_SIZE_X = "faceshape_jaw_all_size_x";// 下巴宽度, 取值范围0.0-1.0,默认值0.5 public static final String FACESHAPE_JAW_Y = "faceshape_jaw_y";// 下巴高度, 取值范围0.0-1.0,默认值0.5 public static final String FACESHAPE_CHEEK_ALL_SIZE_X = "faceshape_cheek_all_size_x";// 脸颊宽度, 取值范围0.0-1.0,默认值0.5 //其他函数略}
可以看到,上面数据基本包含所有人脸属性了,基本具备了捏脸能力,篇幅原因,我们这里不具体去实现。有这方面需求的读者,可以通过在界面上调整上面相关属性来实现。
接下来我们开始创建虚拟形象,首先创建一个User实体类:
public class User { public String userName; //用户名 public String userId; //用户ID public boolean isMan; //性别 public int width; //预览宽度 public int height; //预览高度 public int bgColor; //背景颜色 public int shirtIdx = 0; // T-shirt资源id public int browIdx = 0; //眉毛资源id public User(String userName, String userId, int width, int height) { this.userName = userName; this.userId = userId; this.width = width; this.height = height; this.isMan = true; bgColor = Color.argb(255, 33, 66, 99); } }
示例作用,为了简单起见,我们这里只针对眉毛和衣服资源做选取。接下来创建一个Activity:
public class AvatarActivity extends BaseActivity { private int vWidth = 720; private int vHeight = 1080; private User user = new User("C_0001", "C_0001", vWidth, vHeight); private TextureView mTextureView; //用于显示Avatar形象 private AvatarMngr mAvatarMngr; // 用于维护管理Avatar private ColorPickerDialog colorPickerDialog; //用于背景色选取 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_avatar); // .... // 其他初始化界面相关代码略... // .... user.isMan = true; mTextureView = findViewById(R.id.avatar_view); mZegoMngr = ZegoMngr.getInstance(getApplication()); // 开启虚拟形象预览 mAvatarMngr.start(mTextureView, user); }
最后一行代码中开启了虚拟形象预览,那么这里开启虚拟形象预览具体做了哪些工作呢?show me the code:
public class AvatarMngr implements ZegoAvatarServiceDelegate, RTCMngr.CaptureListener { private static final String TAG = "AvatarMngr"; private static AvatarMngr mInstance; private boolean mIsStop = false; private User mUser = null; private TextureBgRender mBgRender = null; private OnAvatarServiceInitSucced listener; private ZegoCharacterHelper mCharacterHelper; private Application mApp; public interface OnAvatarServiceInitSucced { void onInitSucced(); } public void setLicense(String license, OnAvatarServiceInitSucced listener) { this.listener = listener; ZegoAvatarService.addServiceObserver(this); String aiPath = FileUtils.getPhonePath(mApp, "AIModel.bundle", "assets"); // AI 模型的绝对路径 ZegoServiceConfig config = new ZegoServiceConfig(license, aiPath); ZegoAvatarService.init(mApp, config); } public void stop() { mIsStop = true; mUser = null; stopExpression(); } public void updateUser(User user) { mUser = user; if (user.shirtIdx == 0) { mCharacterHelper.setPackage("m-shirt01"); } else { mCharacterHelper.setPackage("m-shirt02"); } if (user.browIdx == 0) { mCharacterHelper.setPackage("brows_1"); } else { mCharacterHelper.setPackage("brows_2"); } } /** * 启动Avatar,调用此函数之前,请确保已经调用过setLicense * * @param avatarView */ public void start(TextureView avatarView, User user) { mUser = user; mIsStop = false; initAvatar(avatarView, user); startExpression(); } private void initAvatar(TextureView avatarView, User user) { String sex = ZegoCharacterHelper.MODEL_ID_MALE; if (!user.isMan) sex = ZegoCharacterHelper.MODEL_ID_FEMALE; // 创建 helper 简化调用 // base.bundle 是头模, human.bundle 是全身人模 mCharacterHelper = new ZegoCharacterHelper(FileUtils.getPhonePath(mApp, "human.bundle", "assets")); mCharacterHelper.setExtendPackagePath(FileUtils.getPhonePath(mApp, "Packages", "assets")); // 设置形象配置 mCharacterHelper.setDefaultAvatar(sex); updateUser(user); // 获取当前妆容数据, 可以保存到用户资料中 String json = mCharacterHelper.getAvatarJson(); } // 启动表情检测 private void startExpression() { // 启动表情检测前要申请摄像头权限, 这里是在 MainActivity 已经申请过了 ZegoAvatarService.getInteractEngine().startDetectExpression(ZegoExpressionDetectMode.Camera, expression -> { // 表情直接塞给 avatar 驱动 mCharacterHelper.setExpression(expression); }); } // 停止表情检测 private void stopExpression() { // 不用的时候记得停止 ZegoAvatarService.getInteractEngine().stopDetectExpression(); } // 获取到 avatar 纹理后的处理 public void onCaptureAvatar(int textureId, int width, int height) { if (mIsStop || mUser == null) { // rtc 的 onStop 是异步的, 可能activity已经运行到onStop了, rtc还没 return; } boolean useFBO = true; if (mBgRender == null) { mBgRender = new TextureBgRender(textureId, useFBO, width, height, Texture2dProgram.ProgramType.TEXTURE_2D_BG); } mBgRender.setInputTexture(textureId); float r = Color.red(mUser.bgColor) / 255f; float g = Color.green(mUser.bgColor) / 255f; float b = Color.blue(mUser.bgColor) / 255f; float a = Color.alpha(mUser.bgColor) / 255f; mBgRender.setBgColor(r, g, b, a); mBgRender.draw(useFBO); // 画到 fbo 上需要反向的 ZegoExpressEngine.getEngine().sendCustomVideoCaptureTextureData(mBgRender.getOutputTextureID(), width, height, System.currentTimeMillis()); } @Override public void onStartCapture() { if (mUser == null) return;// // 收到回调后,开发者需要执行启动视频采集相关的业务逻辑,例如开启摄像头等 AvatarCaptureConfig config = new AvatarCaptureConfig(mUser.width, mUser.height);// // 开始捕获纹理 mCharacterHelper.startCaptureAvatar(config, this::onCaptureAvatar); } @Override public void onStopCapture() { mCharacterHelper.stopCaptureAvatar(); stopExpression(); } private void initRes(Application app) { // 先把资源拷贝到SD卡,注意:线上使用时,需要做一下判断,避免多次拷贝。资源也可以做成从网络下载。 if (!FileUtils.checkFile(app, "AIModel.bundle", "assets")) FileUtils.copyAssetsDir2Phone(app, "AIModel.bundle", "assets"); if (!FileUtils.checkFile(app, "base.bundle", "assets")) FileUtils.copyAssetsDir2Phone(app, "base.bundle", "assets"); if (!FileUtils.checkFile(app, "human.bundle", "assets")) FileUtils.copyAssetsDir2Phone(app, "human.bundle", "assets"); if (!FileUtils.checkFile(app, "Packages", "assets")) FileUtils.copyAssetsDir2Phone(app, "Packages", "assets"); } @Override public void onError(ZegoAvatarErrorCode code, String desc) { Log.e(TAG, "errorcode : " + code.getErrorCode() + ",desc : " + desc); } @Override public void onStateChange(ZegoAvatarServiceState state) { if (state == ZegoAvatarServiceState.InitSucceed) { Log.i("ZegoAvatar", "Init success"); // 要记得及时移除通知 ZegoAvatarService.removeServiceObserver(this); if (listener != null) listener.onInitSucced(); } } private AvatarMngr(Application app) { mApp = app; initRes(app); } public static AvatarMngr getInstance(Application app) { if (null == mInstance) { synchronized (AvatarMngr.class) { if (null == mInstance) { mInstance = new AvatarMngr(app); } } } return mInstance; }}
以上代码完成了整个虚拟形象的创建,关键代码全部展示,如果还需要具体全部代码,直接从附件中下载即可。
5 附件
- 源码:https://github.com/RTCWang/Meta-Avatar
- 即构元宇宙官网:https://doc-zh.zego.im/article/15302
-
今日视点:Python爬虫实战,requests+openpyxl模块,爬取手机商品信息数据(附源码)
前言今天给大家介绍的是Python爬取手机商品信息数据,在这里给需要的小伙伴们代码,并且给出一点小心得...
来源: -
k8s容器内部通过Prometheus Operator部署MySQL Exporter监控k8s集群外部的MySQL
写在前面在按照下面步骤操作之前,请先确保服务器已经部署k8s,prometheus,prometheusoperator,关于这...
来源: 世界微头条丨零基础开启元宇宙|如何快速创建虚拟形象
Git 的基本使用——笔记
今日视点:Python爬虫实战,requests+openpyxl模块,爬取手机商品信息数据(附源码)
k8s容器内部通过Prometheus Operator部署MySQL Exporter监控k8s集群外部的MySQL
网上打字员赚钱是真的吗?网上打字员是干什么的?
鸿蒙系统3.0支持哪些手机型号?鸿蒙系统3.0怎么升级?
微博借款利息高吗?微博借款利息和借呗哪个高?
余额宝消费红包是什么意思?余额宝消费红包怎么使用?
送男客户什么礼物合适?送男客户礼物前十件排名
电脑光驱是什么东西?电脑找不到光驱驱动器怎么办?
华硕笔记本如何下载驱动?华硕笔记本驱动怎么打开?
空的文件夹占内存吗?空的文件夹为什么删不掉?
DOTA是什么意思?dota和英雄联盟的区别有哪些?
如何提高电脑性能?提高电脑性能的方法有哪些?
环球热头条丨ADB批量截图,且用时间命名
每日信息:行为管理(锐捷智慧教室)
水溶性笔是什么意思?水溶性笔和油性笔的区别是什么?
白炽灯色温是多少?白炽灯色温4000-4600K是多少度?
八字有多少种排列组合?八字有食神代表什么?
荷月是几月的别称?十二个月的别称和雅称
酒店的英语怎么说?酒店前台的工作内容是什么?
全球关注:CAD.NET 批量打印、导出DPF
精选!为解决微信加群或拉人建群的痛点,我用时一个多月写了一个小程序
c++ 如何做出实现一组数据的实际索引
短信+邮箱验证码登录
今亮点!go-dongle 0.2.1 版本发布,一个轻量级、语义化的 golang 编码解码、加密解密库
当前快播:河北用上了无人驾驶农机:北斗加持 坐在屋里就把地给种了
今日要闻!奔驰、宝马、特斯拉相继取消收音机!广播公司担忧:有隐患
换机周期将创历史新高:长达43个月 你多久换一次?
波音、空客狂喜 印度将签史诗级飞机订单:一次买下500架
天天实时:创建全国首个ICU!我国重症医学奠基人陈德昌离世 享年90岁
【当前独家】公司产品太多了,怎么实现一次登录产品互通?
【世界新要闻】2799元配酷睿i5!小米迷你主机将开启众筹 便宜900元条件公布
环球热门:小米13黑色版图赏:直屏直边手感秒杀iPhone 14!
真爱粉!有米粉花近万元抢了两台小米13
世界快资讯:好评率超99% 小米MIX Fold 2推出月光银配色 雷军:镜面效果令人惊叹
天天热点评!比RTX 4090便宜5000 AMD RX 7900系列显卡年底供货20万块:备货充足
小米13系列供不应求!线上已售罄 3999元起
环球即时:差价1000元!一文了解小米13和13 Pro区别
世界观察:NASA猎户座登月飞船成功返回地球:美国完成重返月球第一步
环球观天下!手机“毒瘤”微信终于修复!转发文件多次保存问题解决:不占空间了
天天微头条丨算了吧!电脑RGB光效好看不好玩
59岁李连杰晒照引网友围观 “功夫皇帝”定居国外略显苍老
世界看热讯:Git从入门到实战
Python 基于xml.etree.ElementTree实现XML对比
天天微资讯!飞链云智能机器人-基于ChatGPT的有趣问答
穷人入侵TikTok直播间:躺着就能赚钱 真爽
焦点热讯:为了帮梅西赢球 我敲了一晚上的电子木鱼
环球速递!2022贺岁档总票房突破4亿!《阿凡达2》势不可挡 最快破亿
GPU CPU向量加法时间测试
快播:雷军运气爆棚:在小米手机官方活动 中奖一台小米13
世界资讯:小米音质最好智能音箱!小米Sound Pro开箱图赏
Windows OpenGL 图像透明度调节
焦点热文:异步批处理教程
小米首款万兆路由发布:1799元
天天速递!只要699元!小米旗舰降噪耳机Buds 4发布:HiFi音质
小米8款新品价格汇总:3999元的小米13爱不爱?
全球新资讯:雷军:只有两家做双尺寸双高端 一家是苹果 一家是小米
小米13正式亮相:金属直边 继续对标iPhone!
今亮点!3999元起!小米13正式发布:黄金尺寸小屏、满血徕卡镜头
不涨价 小米13价格良心了:顶配12+512GB还便宜200元
全球微动态丨小米13 Pro正式亮相:中框仅仅3.2毫米!科技纳米皮手感无敌
焦点消息!ELK+FileBeat日志分析系统
快看点丨差错控制
全球信息:Intel显卡驱动即将大更新!DX9、DX11老游戏有惊喜
环球快资讯:MIUI 14“剃刀计划”砍向应用臃肿:微信文件重复存储问题终于解决
全球快看点丨全新界面 系统大幅精简!MIUI 14正式发布
【世界聚看点】使webworker中支持使用import导入模块——threads.js
世界速看:说人话系列:DOM(Document Object Mode)究竟是怎么来的呢?
环球快报:俞敏洪称不敢进高档美发店:一顿好酒肉没有了 心疼
当前热点-达尔文错了?螃蟹竟在地球上出现了5次
全球新资讯:时间轮 (史上最全)
【天天聚看点】雷军晒小米13生产过程 由小米智能工厂全自动化装配
百事通!Intel独立显卡不做了?掌门人一句话 放心了!
长安深蓝SL03宣布涨价:预计上调2000元-8000元
环球快讯:沉船捞出165年前牛仔裤80万卖出:更有数不清的黄金
SpringMVC执行流程
全球快资讯丨任务调度系统-业务线资源隔离
天天微速讯:echarts设置暂无数据
老版本微博下线倒计时!新版已无法返回老版本
舒适降噪的天花板 索尼WH-1000XM5耳机跌破2000元
全球视点!苹果把果链撤出中国?难度可是地狱级的
天天滚动:SQLyog连接MYSQL时报错 Client does not support authentication protocol requested
世界快报:小米发布米家智能小厨宝7L S1:2000W速热、1年省电100度
播报:男子将手机改装成“偷拍神器”:摄像头改到耳机插孔 赚了30万
世界资讯:我迟早被这些AI绘画笑死
世界快报:买不到抗原检测试剂盒怎么办?不要慌
重点聚焦!Intel A750显卡新驱动性能实测:《CS:GO》帧数飙升79%
公众号附件怎么添加?还在用阅读原文的方式来添加吗,你已经落伍啦,现在大家都在用这个新方法,不仅方便还省事!
TabControl控件的简单使用-添加tab
【全球时快讯】Python爬虫实战,requests+xlwt模块,爬取螺蛳粉商品数据(附源码)
环球微速讯:Linux笔记03: Linux常用命令_3.3文件操作命令
新资讯:canvas分享
全球今头条!推特重推蓝V认证服务 苹果用户得多交3美元!马斯克也无能为力
每日焦点!三星S23 Ultra关键参数确定:首发高频版第二代骁龙8、祖传25W快充
童年记忆的黄桃罐头为啥突然火了?专家建议不要盲目囤
世界今头条!【2023最新B站评论爬虫】用python爬取上千条哔哩哔哩评论
【世界快播报】刘强东称新冠比感冒还轻微!王石也发声:只有鼻子不舒服 10天转阴
全球今亮点!再见C罗!C罗赛后痛哭:葡萄牙0-1摩洛哥 非洲球队首进4强
世界杯英法大战结束!法国2-1淘汰英格兰 晋级四强