autojs段子视频化

简介: 牙叔教程 简单易懂

牙叔教程 简单易懂


效果展示

注意, 对话是一句一句出来的,

并且, 底部的新增对话, 向上滚动的时候, 是可以设置滚动速度的,

这是重写了RecyclerView的滚动方法.


段子视频化步骤

  1. 找个段子, 最好是对话形式的
  2. 把对话生成图片
  3. 把图片合成视频


备注: 所有操作均在手机上进行, ffmpeg使用的是autojs的ffmpeg插件.


环境

手机: Mi 11 Pro

Android版本: 11

Autojs版本: 9.0.11

ffmpeg版本: v4.4-dev-416


你将学到以下知识点

  • 使用ffmpeg把图片合成视频
  • RecyclerView的基本使用
  • 一个聊天界面模板
  • 沉浸式通知栏
  • recyclerView更新数据
  • recyclerView控制滚动速度
  • 重写layoutManager的某些方法
  • recyclerView条目的多种布局方法
  • GradientDrawable的使用方法


代码讲解

1. 停止其他脚本

调试必备, 因为我们会多次调试脚本, 那么就需要停止之前的脚本, 不然运行的脚本一多, 一会就autojs就挂了, 或者出现别的bug

engines.all().map((ScriptEngine) => {
  if (engines.myEngine().toString() !== ScriptEngine.toString()) {
    ScriptEngine.forceStop();
  }
});


2. 导入类

注意有的类开头是Packages.

importClass(android.graphics.drawable.GradientDrawable);
importClass(android.view.WindowManager);
importClass(android.view.View);
importClass(Packages.androidx.recyclerview.widget.LinearSmoothScroller);
importClass(android.graphics.Color);
importClass(Packages.androidx.recyclerview.widget.LinearLayoutManager);
importClass(Packages.androidx.recyclerview.widget.RecyclerView);


3. 计算通知栏高度
const resources = context.getResources();
const status_bar_height = resources.getDimensionPixelSize(
  resources.getIdentifier("status_bar_height", "dimen", "android")
);


4. UI界面

注意在xml中的注释格式

ui.layout(
  <vertical>
    {/*标题*/}
    <horizontal>
      <img></img>
      <text>段子视频化</text>
      <img></img>
    </horizontal>
    <View></View>
    {/*聊天内容*/}
    <androidx.recyclerview.widget.RecyclerView
    ></androidx.recyclerview.widget.RecyclerView>
    {/*底栏*/}
    <horizontal>
      <img></img>
      <View></View>
      <img></img>
      <img></img>
    </horizontal>
  </vertical>
);


5. 读取头像

注意在exit事件中, 增加图片回收, 避免内存泄露

let leftAvatarPath = files.path("./leftAvatar.jpg");
let rightAvatarPath = files.path("./rightAvatar.jpg");
let leftAvatar = images.read(leftAvatarPath);
let rightAvatar = images.read(rightAvatarPath);
let leftAvatarBitmap = leftAvatar.bitmap;
let rightAvatarBitmap = rightAvatar.bitmap;
events.on("exit", function () {
  leftAvatar.recycle();
  rightAvatar.recycle();
});


6. 格式化段子

格式为

right:我的意中人是个盖世英雄
right:有一天他会踩着七色云彩来娶我
right:我猜中了开头
right:可是我猜不到这结局
left:尘世间最痛苦的事莫过于此
left:我会跟那个女孩说我爱她
left:如果非要把这份爱加上一个期限
left:我希望是一万年
...


~line.indexOf("left:") 这句话的意思是left在line中,

!~line.indexOf("left:") 这句话的意思是left不在line中,


还挺好记的吧, 感叹号就表示否定的意思, 没有感叹号就表示肯定.

let jokeContent = files.read("/sdcard/joke.js");
jokeContent = jokeContent.trim();
let lines = jokeContent.split("\n");
var len = lines.length;
let dataList = [];
for (var i = 0; i < len; i++) {
  let line = lines[i];
  if (~line.indexOf("left:")) {
    dataList.push({
      avatar: leftAvatarBitmap,
      content: line.replace("left:", ""),
      direction: "left",
    });
  } else if (~line.indexOf("right:")) {
    dataList.push({
      avatar: rightAvatarBitmap,
      content: line.replace("right:", ""),
      direction: "right",
    });
  }
}


dataList的元素有三个属性, 头像, 对话, 方向,


7. 点击立即开始

这是因为截图的时候, 系统会有弹框, 所以要用一个多线程点击弹框

threads.start(function () {
  let view = text("立即开始").visibleToUser(true).boundsInside(0, 0, device.width, device.height).findOne();
  view.click();
});


8. 生成对话, 并截图

对话是一句一句出现的, 不可能一下子把所有对话都展现出来, 否则就叫聊天记录, 而不叫对话了;

对话一句一句出现以后, recyclerView要刷新数据,


如果对话到了UI界面的底部, 那么还需要滚动到视线之内,

最好是慢慢滚动到视线内, 而不是一瞬间就出现,

因此, 需要重写smoothScrollToPosition方法, 来控制滚动速度.

setInterval(() => {
  let position = newDataList.length;
  ui.run(function () {
    recyclerView.adapter.notifyItemInserted(position - 1);
    recyclerView.smoothScrollToPosition(position - 1);
  });
  if (imgNum < 10) {
    imgNum = "0" + imgNum;
  }
  log(imgNum); // 00-28
  images.captureScreen("/sdcard/" + imgNum + ".png");
  imgNum++;
}, 1000);


9. recyclerView设置adapter
layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
recyclerView.setLayoutManager(layoutManager);
let recycleAdapter = createRecyclerViewAdapter(newDataList);
recyclerView.setAdapter(recycleAdapter);


10. 对话左右不同的布局
let leftBoxXml = (
  <horizontal margin="0 15 0 0">
    <img id="img" marginRight="8" w="45dp" h="45dp" circle="true"></img>
    <text id="content" textColor="#cc000000" textSize="17sp" padding="11" layout_height="wrap_content"></text>
  </horizontal>
);
let rightBoxXml = (
  <horizontal margin="0 15 0 0" layout_width="match_parent" layout_height="wrap_content" gravity="right">
    <text id="content" textColor="#cc000000" textSize="17sp" padding="11" marginRight="8"></text>
    <img id="img" w="45dp" h="45dp" circle="true"></img>
  </horizontal>
);


11. 创建RecyclerView.Adapter

对没了解过RecyclerView的人来说, 有点难,

对于经常使用rv的人来说, 最重要的一句是view.content.setMaxWidth(maxWidth);

这是控制view的最大宽度, 这样, 如果对话内容太长, 也可以避免覆盖头像;


viewType, 左右布局就是依据他来区分的;

onCreateViewHolder: 生成布局;

onBindViewHolder: 给布局绑定数据;

getItemCountr: 数据的数量;

getItemViewType: 布局类型, 当前是左右, 另外经常使用的是head, foot

return RecyclerView.Adapter({
  onCreateViewHolder: function (parent, viewType) {
    let view;
    let holder;
    if (viewType === 0) {
      view = ui.inflate(leftBoxXml, parent, false);
      view.content.setMaxWidth(maxWidth);
      setBackgroundRoundRounded(view.content);
      holder = JavaAdapter(RecyclerView.ViewHolder, {}, view);
    } else {
      view = ui.inflate(rightBoxXml, parent, false);
      view.content.setMaxWidth(maxWidth);
      setBackgroundRoundRounded(view.content, "#96ec69");
      holder = JavaAdapter(RecyclerView.ViewHolder, {}, view);
    }
    return holder;
  },
  onBindViewHolder: function (holder, position) {
    let data = dataList[position];
    holder.itemView.img.setImageBitmap(data.avatar);
    holder.itemView.content.setText(data.content);
  },
  getItemCount: function () {
    return dataList.length;
  },
  getItemViewType: function (position) {
    return dataList[position].direction === "left" ? 0 : 1;
  },
});


12. 多张图片合成视频
let cmd = " -f concat -safe 0 -i /sdcard/input.txt -vsync vfr -pix_fmt yuv420p /sdcard/output1.mp4";
log(ffmpeg.inProcess.exec(cmd));
app.viewFile("/sdcard/output1.mp4");


名人名言


思路是最重要的, 其他的百度, bing, stackoverflow, github, 安卓文档, autojs文档, 最后才是群里问问
--- 牙叔教程


声明


部分内容来自网络
本教程仅用于学习, 禁止用于其他用途




相关文章
|
JavaScript 前端开发 程序员
node.js这些常用命令,你都会了吗?
本文介绍了Node.js和npm的常用命令。在npm方面,包括安装、卸载、更新包,初始化项目,运行脚本等;在Node.js方面,涉及运行JavaScript文件、启用调试模式和查看版本信息等。掌握这些基础命令对于日常开发至关重要。
|
存储 SQL 分布式计算
Parquet与ORC高性能列式存储
Parquet与ORC高性能列式存储
1126 0
Parquet与ORC高性能列式存储
|
Ubuntu
使用dpkg在ubuntu上安装软件包遇到依赖包的问题
使用dpkg在ubuntu上安装软件包遇到依赖包的问题
|
9月前
|
API
钉钉宜搭--远程API,在其他人访问时无法生效
简介: 描述了一种远程API配置问题的场景。开发人员在本地可正常通过应用表单获取数据,但同组织的其他同事访问时无法获取数据,尽管已设置全部权限。问题是关于如何解决这种跨用户数据访问异常的情况,确保同事间能正常共享数据。
|
11月前
|
机器学习/深度学习 存储 算法
《匿名化技术:数据隐私与价值挖掘的平衡探索》
在数据驱动的时代,数据成为企业和组织的核心资产,匿名化技术作为保护数据隐私的重要手段备受关注。它通过去除或混淆个人身份信息,如数据脱敏、泛化和加密等方法,有效保护隐私。然而,匿名化可能影响数据的完整性和准确性,进而影响价值挖掘。为平衡隐私保护与数据利用,需明确使用目的、加强数据治理、创新技术应用,确保数据安全合规,推动数字经济健康发展。
625 30
|
供应链 数据可视化 数据挖掘
企业信息化管理要了解的十种系统
信息化不仅是技术的引入,更是企业战略的重要组成部分,贯穿于企业的每一个环节,确保企业在复杂多变的市场中灵活应对、持续发展。
820 6
企业信息化管理要了解的十种系统
|
运维 jenkins Java
Jenkins在持续集成与持续部署中的价值
Jenkins在持续集成与持续部署中的价值
|
前端开发 数据安全/隐私保护
利用 HBuilderX 设置CSS样式会员注册页面
利用 HBuilderX 设置CSS样式会员注册页面
314 1
|
Web App开发 数据采集 Linux
Python爬虫请求库安装#1
摘要:requests安装、selenium安装、aiohttp安装【2月更文挑战第2天】
405 3
Python爬虫请求库安装#1
|
数据采集 存储 机器学习/深度学习
解密网络爬虫与数据抓取技术的奇妙世界
在信息时代,网络爬虫与数据抓取技术扮演着重要角色,它们能够帮助我们从庞杂的网络数据中提取有用信息。本文将深入探讨网络爬虫的工作原理、应用场景以及技术挑战,并展望未来发展方向。