镇楼图
OpenAI API
You can interact with the API through HTTP。
OpenAI 供给了开放API、各渠道都能够经过HTTP与其建立连接进行调用。开发者能够尝试在不同渠道上对接。这篇文章尝试将开放API对接到ComposeUnit。
免费额度: API方式调用官方明确是需求收费的。当然了注册之后默许有 $18.00的免费运用额度。对于调试API这些应该足够了。
充值相关: 当然了咱们能够选择付费充值,对于国内可能在充值方面比较麻烦。当然了咱们能够经过 欧易渠道购买USDT->提现到Depay钱包->兑换成美刀Depay信用卡->充值或升级到ChatGPT Plus。(开发者根据实际情况,选择是否升级注册**ChatGPT Plus**)
一、官方UI
官方布局UI 想必咱们都看到过,如下所示。ComposeUnit OpenAI模块的UI款式也沿用官方进行,接下来对官网布局剖析,好进行规划咱们APP中的UI布局。
1、布局模块
官方布局,右侧UI大约包含,对话列表、输入框、重定向恳求这三大模块。如下所示:
-
音讯列表
-
输入框
-
重定向恳求
2、模块剖析
-
音讯列表
能够看到音讯部分,是常见的问答列表款式。文章由于时间问题,不做本地数据库文件缓存等,数据操作都根据viewModel中可调查调集进行。此事例运用compose+viewModel+rerofit+协程方式进行代码编写。列表部分,运用Compose库供给的LazyColumn进行布局。
在对话中能够调查到对应的文字有输入效果。应该不是印度小哥后台的一顿疯狂操作。开个玩笑,先看看官方输出文字效果。这个是需求安排的小动画。
-
输入框
输入框内不也有许多细节,未输入之前的提示Send a message和文字颜色,以及输入之后的文字颜色等都是不同。
1、文字颜色改变
2、输入框右侧发送按钮和加载动画由网络是否恳求而决定。
3、加载进程中右侧是三个小点加载动画。
4、加载进程中输入框是能够输入的,可是无法进行发送恳求。
-
重定向恳求
在输入框上部有个Regenerate response和Stop generating能够切换的按钮。当恳求加载数据进程能够停止,而数据停止之后,能够点击Regenerate response再次恳求。
二、规划页面UI
1、UI全体布局
同官方UI,进行移动端页面规划,全体UI色调只用灰色系深浅两种:
val openAiDark = Color(52, 54, 65, 255)
val openAiLight = Color(68, 70, 84, 255)
对比如下:
经过Scaffold构建根本的页面结构。结构代码如下:
@Composable
fun OpenAIPage(viewModel: OpenAiViewModel) {
Scaffold(
backgroundColor = openAiDark,
topBar = {//1、顶部TopBar
OpenAITopBar()
}) {
OpenAIUI(//2、列表和底部输入框
)
}
}
2、TopBar
TopBar布景色取主题色的亮色,整个UI布局只用用灰色的深亮两种色,我是比较喜爱简约风。文字部分加粗且增加暗影。
//topBar部分,标题文字加粗增暗影
@Composable
fun OpenAITopBar() {
Box(
Modifier
.fillMaxWidth()
.height(70.dp)
.background(
openAiLight
),
contentAlignment = Alignment.Center
) {
Text(
text = "OpenAI",
fontSize = 18.sp,
textAlign = TextAlign.Center,
color = Color.White,
fontWeight = FontWeight.Bold,
style = TextStyle(
shadow = Shadow(
Color(43, 43, 43, 255),
offset = Offset(2f, 6f),
blurRadius = 11f
)
)
)
}
}
效果如下:
3、列表
对话列表和输入框部分,咱们能够用Box进行层次布局。
@Composable
fun OpenAIUI(
modifier: Modifier,
textFieldAlignment: Alignment = Alignment.BottomCenter,
) {
Box(modifier = modifier,
contentAlignment = textFieldAlignmen) {
//列表布局
OpenAIListView()
//输入和重定向布局
Column(
Modifier
.fillMaxWidth()
horizontalAlignment = Alignment.CenterHorizontally,
) {
OpenAIReRequestUI()
Box(Modifier.height(5.dp))
OpenAIBottomInputUI()
}
}
}
列表OpenAIListView部分,假数据进行填充,增加布景配色。
@Composable
fun OpenAIListView1() {
LazyColumn(
Modifier
.fillMaxSize()
.padding(bottom = 80.dp)
) {
items(2, key = { index ->
index
}) { index ->
if (index % 2 == 0)
Box(
Modifier
.fillMaxWidth()
.background(Color(52, 54, 65, 255))
.padding(top = 20.dp)
) {
Text(
text = "我是假数据,用户数据",
color = Color.White,
modifier = Modifier
.padding(start = 20.dp, bottom = 20.dp, end = 20.dp)
)
}
else
Box(
Modifier
.fillMaxWidth()
.background(openAiLight)
.padding(top = 20.dp)
) {
Text(
text = "我是假数据,OpenAI返回数据,假如答复有问题,我期望你能够给我一些主张,我持续生成,文字不行两行。",
color = Color.White,
modifier = Modifier
.padding(start = 20.dp, bottom = 20.dp, end = 20.dp)
)
}
}
}
}
当然了,用户和AI头像咱们不难用Row之类的容器进行摆放。代码如下:
LazyColumn(
Modifier
.fillMaxSize()
.padding(bottom = 80.dp)
) {
items(2, key = { index ->
index
}) { index ->
if (index % 2 == 0)
Row(
Modifier
.fillMaxWidth()
.background(Color(52, 54, 65, 255))
.padding(top = 20.dp)
) {
Box(Modifier.width(20.dp))
Box(
Modifier
.size(30.dp)
.background(
color = Color(
3,
149,
135,
255
),
shape = RoundedCornerShape(5.dp)
),
contentAlignment = Alignment.Center
) {
Text(
text = "11",
color = Color.White,
)
}
Text(
text = "我是假数据,用户数据",
color = Color.White,
modifier = Modifier
.padding(start = 20.dp, bottom = 20.dp, end = 20.dp)
)
}
else
Row(
Modifier
.fillMaxWidth()
.background(openAiLight)
.padding(top = 20.dp)
) {
Box(Modifier.width(20.dp))
Image(
painter = painterResource(id = R.mipmap.open_ai_head),
contentDescription = "head",
contentScale = ContentScale.Fit,
modifier = Modifier.size(30.dp)
)
Text(
text = "我是假数据,OpenAI返回数据,假如答复有问题,我期望你能够给我一些主张,我持续生成,文字不行两行。",
color = Color.White,
modifier = Modifier
.padding(start = 20.dp, bottom = 20.dp, end = 20.dp)
)
}
}
}
当然了代码别学上面堆屎山。
@Composable
fun OpenAIListView() {
LazyColumn(
Modifier
.fillMaxSize()
.padding(bottom = 80.dp)
) {
items(2, key = { index ->
index
}) { index ->
if (index % 2 == 0)
UserMessagesUI()
else
OpenAIMessageUI()
}
}
当然了后期还会持续优化代码的,有网络恳求过错或许异常恳求之类的头像以及文字颜色显示效果等。
大约用约束布局进行设置一个网络恳求异常的状况布局。
ConstraintLayout {
val (imageView, errorView) = createRefs()
Image(
painter = painterResource(id = R.mipmap.open_ai_head),
contentDescription = "head",
contentScale = ContentScale.Fit,
modifier = Modifier
.constrainAs(imageView) {
top.linkTo(parent.top)
start.linkTo(parent.start)
}
.size(30.dp)
)
if (netErro)
Image(
painter = painterResource(id = R.mipmap.open_ai_error),
colorFilter = ColorFilter.tint(Color.Red),
contentDescription = "error",
contentScale = ContentScale.Fit,
modifier = Modifier
.constrainAs(errorView) {
bottom.linkTo(imageView.bottom)
end.linkTo(imageView.end)
}
.offset(x = 6.dp, y = 8.dp)
.size(16.dp)
)
}
4、输入框
官方效果如下:
输入框部分,运用TextField,经过Modifier设置background设置布景色和裁剪布景有四角弧度,border设置增加边框。TexField的placeholder特点进行设置Send a message… 提示语,确保在无输入内容以及无焦点是显示提示语。经过TextField的shape进行设置输入内容部分的形状保持和布景共同。trailingIcon可用于设置最后边部分的发送按钮和加载中动画部分。
OpenAIBottomInputUI(){
TextField(
textStyle = TextStyle(
color = (inputColor(isFocused, textFieldValue.value.text, loading))
), colors = TextFieldDefaults.textFieldColors(
unfocusedIndicatorColor = Color.Transparent,
focusedIndicatorColor = Color.Transparent,
backgroundColor = Color(64, 65, 79, 255)
),
interactionSource = interactionSource,
modifier = Modifier
.fillMaxWidth(0.8f)
.height(50.dp)
.focusRequester(focusRequester)
.background(
color = Color(64, 65, 79, 255), RoundedCornerShape(10)
)
.border(
0.5.dp, Color(47, 49, 56, 166), RoundedCornerShape(10)
),
placeholder = {
Text(
text = "Send a message...",
color = (inputColor(isFocused, textFieldValue.value.text))
)
},
shape = RoundedCornerShape(10),
value = textFieldValue.value,
onValueChange = {
if (!isFocused && it.text.isNotEmpty()) {
textFieldValue.value = TextFieldValue(
text = it.text, selection = TextRange(0, it.text.lastIndex + 1)
)
} else {
textFieldValue.value = TextFieldValue(
text = it.text,
selection = TextRange(it.text.lastIndex + 1)
)
}
}, trailingIcon = {
Icon(
modifier = Modifier.clickable {
if (textFieldValue.value.text.isNotEmpty()) {
viewModel.setLoadValue(true)
viewModel.getChatGTPMessage(textFieldValue.value.text)
viewModel.setRegenerateInfo(textFieldValue.value.text)
textFieldValue.value = TextFieldValue(
text = ""
)
focusManager.clearFocus()
}
},
painter = painterResource(R.mipmap.send_icon),
contentDescription = "sendIcon",
tint = submitColor(isFocused, textFieldValue.value.text)
)
})
}
重定向恳求部分比较简单,不在阐述
@Composable
private fun OpenAIReRequestUI(
pageList: ArrayList<ChatGTPModel>,
) {
Row(
Modifier
.background(color = Color(52, 54, 65, 255))
.border(
0.6.dp,
Color(85, 87, 104, 255),
RoundedCornerShape(5.dp),
)
.clickable {
//点击网络操作
},
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically
) {
Icon(
modifier = Modifier
.height(18.dp)
.width(18.dp)
.padding(start = 5.dp),
painter = painterResource(
id = if (loading)
R.mipmap.open_ai_stop else
R.mipmap.open_ai_reload
),
contentDescription = "reload",
tint = Color(216, 216, 226, 255)
)
Text(
text = "Regenerate response",
Modifier.padding(5.dp),
color = Color(216, 216, 226, 255)
)
}
}
至此根本布局完成,接下来咱们看看对应官方的API,逐步接入数据部分。
三、Chat API
四、Images API
五、Audio API
六、Audio API
七、Edits API
八、最终效果