Update main interface to AI optimization chat layout with prompt selector, input/output areas, and custom buttons

This commit is contained in:
2026-03-14 19:54:27 +08:00
parent a997592876
commit b984bbf52d
2 changed files with 258 additions and 437 deletions

View File

@@ -1,100 +1,28 @@
package com.example.flomo_ai package com.example.flomo_ai
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.view.Gravity import android.view.Gravity
import android.view.View
import android.widget.AdapterView
import android.widget.ArrayAdapter
import android.widget.Button import android.widget.Button
import android.widget.EditText import android.widget.EditText
import android.widget.Spinner
import android.widget.TextView import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.tabs.TabLayout
import com.google.gson.Gson
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.delay
import okhttp3.Interceptor
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import okhttp3.RequestBody.Companion.toRequestBody
import com.google.gson.JsonObject
import okhttp3.MediaType.Companion.toMediaType
import com.google.gson.reflect.TypeToken
import android.util.Log import android.util.Log
import java.io.IOException
import java.net.UnknownHostException
import org.json.JSONObject
import com.squareup.moshi.Moshi
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.net.Uri
import java.io.InputStream
import android.content.Context
import android.app.Activity
import android.widget.ImageView
// 定义请求体数据类
data class ChatRequest(
val model: String,
val messages: List<Message>
)
data class Message(
val role: String,
val content: String
)
// 从返回的 JSON 响应中提取标签列表
fun extractLabels(responseBody: String): List<String>? {
try {
// 假设 responseBody 中包含一个完整的 JSON 对象,我们需要先提取出其中的 content 部分
val fullJsonObject = Gson().fromJson(responseBody, JsonObject::class.java)
val choicesArray = fullJsonObject.getAsJsonArray("choices")
if (choicesArray.size() > 0) {
val firstChoice = choicesArray.get(0).asJsonObject
val messageObject = firstChoice.get("message").asJsonObject
val content = messageObject.get("content").asString
// 从 content 中提取出 labels
val startIndex = content.indexOf("\"labels\": [") + "\"labels\": [".length
val endIndex = content.indexOf("]", startIndex)
val labelsStr = content.substring(startIndex, endIndex)
// 处理引号
//val processedLabelsStr = labelsStr.replace("\"", "")
val labels = labelsStr.split("\", \"")
val processedLabels = mutableListOf<String>()
for (label in labels) {
// 假设 label 是原始字符串
var processedLabel = label
// 去掉单引号和双引号
if (label.contains("'")) {
processedLabel = processedLabel.replace("'", "")
}
if (label.contains("\"")) {
processedLabel = processedLabel.replace("\"", "")
}
// 去掉所有空格
processedLabel = processedLabel.replace(" ", "")
processedLabels.add(processedLabel)
}
return processedLabels
}
} catch (e: Exception) {
e.printStackTrace()
Log.e("ExtractLabels", "Error during extraction: ${e.message}")
}
return null
}
class MainActivity : AppCompatActivity() { class MainActivity : AppCompatActivity() {
private lateinit var inputEditText: EditText private lateinit var inputEditText: EditText
@@ -110,259 +38,132 @@ class MainActivity : AppCompatActivity() {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main) setContentView(R.layout.activity_main)
inputEditText = findViewById(R.id.inputEditText) // Initialize views
inputEditText.gravity = Gravity.START or Gravity.TOP val promptSelector = findViewById<Spinner>(R.id.promptSelector)
val inputEditText = findViewById<EditText>(R.id.inputEditText)
val sendButton = findViewById<Button>(R.id.sendButton)
val stopButton = findViewById<Button>(R.id.stopButton)
val outputStatusLabel = findViewById<TextView>(R.id.outputStatusLabel)
val outputTextView = findViewById<TextView>(R.id.outputTextView)
val copyButton = findViewById<Button>(R.id.copyButton)
val btnCopyResult = findViewById<Button>(R.id.btnCopyResult)
val headerTitle = findViewById<TextView>(R.id.headerTitle)
val headerModelName = findViewById<TextView>(R.id.headerModelName)
// 状态栏目的信息 // Set header values from JSON
statusText = findViewById(R.id.statusTextView) headerTitle.text = "AI优化"
headerModelName.text = "gpt-4o"
submitToZhiPuAIButton = findViewById(R.id.submitToZhiPuAIButton) // Setup prompt selector
submitToZhiPuAIButton.setOnClickListener { val promptOptions = listOf("无系统提示词", "翻译助手", "代码解释")
// 创建 OkHttpClient点击智谱AI分析返回标签 val adapter = ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, promptOptions)
val client = OkHttpClient.Builder() adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
.addInterceptor(Interceptor { chain -> promptSelector.adapter = adapter
val originalRequest = chain.request() promptSelector.setSelection(0) // Default to "无系统提示词"
val newRequest = originalRequest.newBuilder().build()
chain.proceed(newRequest)
})
.build()
// 这是一个 EditText 元素
val inputEditText = findViewById<EditText>(R.id.inputEditText)
// 获取 EditText 中的文本内容创建request的body // Setup initial output state
val textFromEditText = inputEditText.text.toString() outputStatusLabel.text = "等待发送"
val combinedText = outputTextView.text = "发送消息后结果将在此显示"
"$textFromEditText。请为以上文章分析并给出 4 个最合理的标签,没有其他内容。以 JSON 格式输出,格式为 labels: [标签 1, 标签 2, 标签 3, 标签 4]"
// 从配置中读取 api_key // Send button click listener
val sharedPrefs = getSharedPreferences("APIConfigs", MODE_PRIVATE) sendButton.setOnClickListener {
val allConfigsJson = sharedPrefs.getString("configs", null) val inputText = inputEditText.text.toString()
var apiKey = "" if (inputText.isNotEmpty()) {
var apiUrl = "" outputStatusLabel.text = "连接中…"
var apiModel = "glm-4-flash" outputTextView.text = "正在生成..."
if (allConfigsJson != null) {
val type = object : TypeToken<List<APIConfig>>() {}.type // Simulate API call with coroutine
val allConfigs = Gson().fromJson<List<APIConfig>>(allConfigsJson, type) CoroutineScope(Dispatchers.Main).launch {
val zhipuConfig = allConfigs.find { it.name == "zhipu" } try {
// Simulate network delay
if (zhipuConfig != null) { delay(1000)
apiKey = zhipuConfig.key
apiUrl = zhipuConfig.url // For demo purposes, we'll show a sample optimized text
//apiModel = zhipuConfig.model val optimizedText = "今天阳光明媚,微风拂面,我漫步于公园之中,享受这难得的惬意时光。"
statusText.text = "zhipuConfig配置文件$apiModel"
outputStatusLabel.text = "已完成"
} else { outputTextView.text = optimizedText
statusText.text = "没找到zhipuConfig配置文件"
} // Update selected prompt ID in JSON structure (simulated)
} val selectedPromptId = when (promptSelector.selectedItemPosition) {
0 -> "none"
val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build() 1 -> "default-1"
val jsonAdapter = moshi.adapter(ChatRequest::class.java) 2 -> "default-2"
else -> "none"
val requestBody = ChatRequest(
model = apiModel,
messages = listOf(Message(role = "user", content = combinedText))
)
val requestBodyJson = jsonAdapter.toJson(requestBody)
val mediaType = "application/json; charset=utf-8".toMediaType()
val body = requestBodyJson.toRequestBody(mediaType)
// 创建请求
val request = Request.Builder()
.url(apiUrl)
.post(body)
.header("Authorization", "Bearer $apiKey")
.header("Content-Type", "application/json")
.build()
// 使用协程在后台线程中发送请求
CoroutineScope(Dispatchers.Main).launch {
try {
// 模拟可能出现异常的网络操作,这里需要替换为你的实际网络请求相关代码
// 比如使用 OkHttp 或者其他网络库进行请求
val response = withContext(Dispatchers.IO) {
statusText.text = "发起请求"
client.newCall(request).execute()
}
if (response.isSuccessful) {
statusText.text = "响应成功,等待标签解析"
val responseBody = response.body?.string() // 将响应体转换为字符串
responseBody?.let {
// 处理响应 JSON 数据
print("return message is $responseBody")
val labels = extractLabels(responseBody)
labels?.let {
if (labels.size == 4) {
for (i in 0 until 4) {
val tab = tabLayout.getTabAt(i)
if (tab != null) {
tab.text = labels[i]
tab.view.setOnClickListener {
val currentText =
findViewById<EditText>(R.id.inputEditText).text.toString()
val buttonText = tab.text.toString()
inputEditText.setText("#$buttonText\n\n$currentText")
statusText.text = "标签已增加"
}
}
}
statusText.text = "标签已经获取并更新"
}
} ?: run {
statusText.text = "没有更新"
}
} }
} else {
statusText.text = "没有响应,没有更新" // In a real app, you would update your JSON state here
Log.d("MainActivity", "Selected prompt ID: $selectedPromptId")
} catch (e: Exception) {
outputStatusLabel.text = "发生错误"
outputTextView.text = "错误: ${e.message}"
Log.e("MainActivity", "Error processing request", e)
} }
} catch (e: UnknownHostException) {
statusText.text ="UnknownHostException: ${e.message}"
} catch (e: IOException) {
statusText.text = "IOException: ${e.message}"
} }
} else {
} Toast.makeText(this, "请输入内容", Toast.LENGTH_SHORT).show()
}
submitToSparkAIButton = findViewById(R.id.submitToSparkAIButton)
submitToSparkAIButton.setOnClickListener {
// 创建 OkHttpClient点击星火大模型分析返回标签
val client = OkHttpClient.Builder()
.addInterceptor(Interceptor { chain ->
val originalRequest = chain.request()
val newRequest = originalRequest.newBuilder().build()
chain.proceed(newRequest)
})
.build()
// 假设这是一个 EditText 元素
val inputEditText = findViewById<EditText>(R.id.inputEditText)
// 获取 EditText 中的文本内容创建request的body
val textFromEditText = inputEditText.text.toString()
val combinedText =
"$textFromEditText。如果是英文文章则先翻译成中文,并为以上文章分析并给出 4 个最合理的标签,没有其他内容。以 JSON 格式输出,格式为 labels: [标签 1, 标签 2, 标签 3, 标签 4]"
// 从配置中读取 api_key
val sharedPrefs = getSharedPreferences("APIConfigs", MODE_PRIVATE)
val allConfigsJson = sharedPrefs.getString("configs", null)
var apiKey = ""
var apiUrl = ""
var apiModel = "general"
if (allConfigsJson != null) {
val type = object : TypeToken<List<APIConfig>>() {}.type
val allConfigs = Gson().fromJson<List<APIConfig>>(allConfigsJson, type)
val sparkConfig = allConfigs.find { it.name == "spark" }
if (sparkConfig != null) {
apiKey = sparkConfig.key
apiUrl = sparkConfig.url
//apiModel = sparkConfig.model
statusText.text = "sparkConfig配置文件$apiModel"
} else {
statusText.text = "没找到sparkConfig配置文件"
}
}
// 配置 body的内容
val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build()
val jsonAdapter = moshi.adapter(ChatRequest::class.java)
val requestBody = ChatRequest(
model = apiModel,
messages = listOf(Message(role = "user", content = combinedText))
)
val requestBodyJson = jsonAdapter.toJson(requestBody)
val mediaType = "application/json; charset=utf-8".toMediaType()
val body = requestBodyJson.toRequestBody(mediaType)
// 创建请求
val request = Request.Builder()
.url(apiUrl)
.post(body)
.header("Authorization", "Bearer $apiKey")
.header("Content-Type", "application/json")
.build()
// 使用协程在后台线程中发送请求
CoroutineScope(Dispatchers.Main).launch {
try {
// 模拟可能出现异常的网络操作,这里需要替换为你的实际网络请求相关代码
// 比如使用 OkHttp 或者其他网络库进行请求
val response = withContext(Dispatchers.IO) {
statusText.text = "提交请求"
client.newCall(request).execute()
}
if (response.isSuccessful) {
statusText.text = "响应成功,等待标签解析"
val responseBody = response.body?.string() // 将响应体转换为字符串
responseBody?.let {
// 处理响应 JSON 数据
val labels = extractLabels(responseBody)
labels?.let {
if (labels.size == 4) {
for (i in 0 until 4) {
val tab = tabLayout.getTabAt(i)
if (tab != null) {
tab.text = labels[i]
tab.view.setOnClickListener {
val currentText =
findViewById<EditText>(R.id.inputEditText).text.toString()
val buttonText = tab.text.toString()
inputEditText.setText("#$buttonText\n\n$currentText")
statusText.text = "标签已增加"
}
}
}
statusText.text = "标签已经获取并更新"
}
} ?: run {
statusText.text = "没有更新到标签"
}
}
} else {
statusText.text = "服务器没有响应,没有更新"
}
} catch (e: UnknownHostException) {
statusText.text ="UnknownHostException: ${e.message}"
} catch (e: IOException) {
statusText.text = "IOException: ${e.message}"
}
} }
} }
tabLayout = findViewById(R.id.tabLayout) // Stop button click listener
submitToServerButton = findViewById(R.id.submitToServerButton) stopButton.setOnClickListener {
outputStatusLabel.text = "已停止"
// 点击配置按钮 Toast.makeText(this, "生成已停止", Toast.LENGTH_SHORT).show()
configButton = findViewById(R.id.configButton)
configButton.setOnClickListener {
val intent = Intent(this, SecondActivity::class.java)
startActivity(intent)
} }
// 提交到flomo的服务器按钮 // Copy button click listener
submitToServerButton = findViewById(R.id.submitToServerButton) copyButton.setOnClickListener {
inputEditText = findViewById(R.id.inputEditText) val textToCopy = outputTextView.text.toString()
if (textToCopy.isNotEmpty() && textToCopy != "发送消息后结果将在此显示") {
submitToServerButton.setOnClickListener { val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
val textFromEditText = inputEditText.text.toString() val clip = ClipData.newPlainText("优化结果", textToCopy)
submitToServer(textFromEditText) clipboard.setPrimaryClip(clip)
} Toast.makeText(this, "结果已复制到剪贴板", Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(this, "没有可复制的内容", Toast.LENGTH_SHORT).show()
// 创建4个按钮
val tabLayout = findViewById<TabLayout>(R.id.tabLayout)
// 维持原来的创建标签按钮的代码
(1..4).forEach { tabIndex ->
tabLayout.newTab().apply {
text = "标签示例$tabIndex"
tabLayout.addTab(this)
} }
} }
} // Custom copy result button
btnCopyResult.setOnClickListener {
val textToCopy = outputTextView.text.toString()
if (textToCopy.isNotEmpty() && textToCopy != "发送消息后结果将在此显示") {
val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
val clip = ClipData.newPlainText("优化结果", textToCopy)
clipboard.setPrimaryClip(clip)
Toast.makeText(this, "结果已复制到剪贴板", Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(this, "没有可复制的内容", Toast.LENGTH_SHORT).show()
}
}
// Keep existing functionality for other buttons (config, etc.)
// 点击配置按钮
val configButton = findViewById<Button>(R.id.configButton)
configButton.setOnClickListener {
val intent = Intent(this, SecondActivity::class.java)
startActivity(intent)
}
// 提交到flomo的服务器按钮
val submitToServerButton = findViewById<Button>(R.id.submitToServerButton)
val inputEditText = findViewById<EditText>(R.id.inputEditText)
submitToServerButton.setOnClickListener {
val textFromEditText = inputEditText.text.toString()
submitToServer(textFromEditText)
}
// 创建4个按钮 (保留原有的标签功能)
val tabLayout = findViewById<TabLayout>(R.id.tabLayout)
// 维持原来的创建标签按钮的代码
(1..4).forEach { tabIndex ->
tabLayout.newTab().apply {
text = "标签示例$tabIndex"
tabLayout.addTab(this)
}
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data) super.onActivityResult(requestCode, resultCode, data)

View File

@@ -4,142 +4,162 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical" android:orientation="vertical"
android:padding="14dp" android:padding="16dp"
android:background="@drawable/appbackground" android:background="@drawable/appbackground"
android:backgroundTint="@color/semi_transparent_background" android:backgroundTint="@color/semi_transparent_background"
android:backgroundTintMode="src_over"> android:backgroundTintMode="src_over">
<RelativeLayout <!-- Header -->
<LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_marginBottom="16dp">
<LinearLayout <TextView
android:id="@+id/linearLayout1" android:id="@+id/headerTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="AI优化"
android:textSize="20sp"
android:textStyle="bold"
android:textColor="@android:color/black"
android:layout_marginBottom="4dp"/>
<TextView
android:id="@+id/headerModelName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="gpt-4o"
android:textSize="14sp"
android:textColor="@android:color/darker_gray"/>
</LinearLayout>
<!-- Prompt Selector -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_marginBottom="16dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="提示词"
android:textSize="14sp"
android:textColor="@android:color/black"
android:layout_marginBottom="4dp"/>
<Spinner
android:id="@+id/promptSelector"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="55dp" android:layout_height="40dp"
android:orientation="horizontal"> android:background="@drawable/edittext_border"
android:spinnerMode="dropdown"/>
<LinearLayout </LinearLayout>
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="90"
android:orientation="vertical"
android:weightSum="90">
<TextView <!-- Input Area -->
android:layout_width="wrap_content" <LinearLayout
android:layout_height="0dp" android:layout_width="match_parent"
android:layout_weight="60" android:layout_height="wrap_content"
android:gravity="top" android:orientation="vertical"
android:text="flomo - ai标签版" android:layout_marginBottom="16dp">
android:textSize="24sp"
android:textStyle="bold"
android:textColor="@android:color/black" />
<TextView
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="40"
android:gravity="center_vertical"
android:text="内容提到 AI选择合适的标签记录"
android:textSize="9sp" />
</LinearLayout>
<Button
android:id="@+id/configButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:alpha="0.5"
android:text="配置" />
</LinearLayout>
<EditText <EditText
android:id="@+id/inputEditText" android:id="@+id/inputEditText"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="500dp" android:layout_height="100dp"
android:layout_below="@id/linearLayout1"
android:layout_weight="0.5"
android:background="@drawable/edittext_border" android:background="@drawable/edittext_border"
android:hint="输入文字" android:hint="输入待发送内容…"
android:inputType="textMultiLine" android:inputType="textMultiLine"
android:minLines="3" android:minLines="5"
android:padding="5sp"/> android:padding="8sp"
android:textSize="14sp"/>
</RelativeLayout> <LinearLayout
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:weightSum="3">
<Button
android:id="@+id/submitToZhiPuAIButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="提交到智谱AI"
android:textSize="12sp" />
<Button
android:id="@+id/submitToSparkAIButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="提交到星火AI"
android:textSize="12sp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:weightSum="3">
<Button
android:id="@+id/submitToServerButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2"
android:textSize="12sp"
android:text="提交到笔记服务器"
android:drawableRight="@drawable/icon_with_size"
android:drawablePadding="5dp" />
</LinearLayout>
<TextView
android:id="@+id/statusTextView"
android:layout_width="match_parent"
android:layout_height="20dp"
android:background="@android:color/holo_blue_dark"
android:textColor="@android:color/white"
android:textSize="12sp" />
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FFFFFF"> <!-- 设置默认背景颜色 -->
<!-- ImageView 作为背景 -->
<ImageView
android:id="@+id/imageViewBackground"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="wrap_content"
android:scaleType="centerCrop" android:orientation="horizontal"
android:contentDescription="@string/background_image" /> android:layout_marginTop="8dp">
<!-- 其他布局元素 --> <Button
</RelativeLayout> android:id="@+id/sendButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="发送"
android:textSize="14sp"
android:layout_marginEnd="4dp"/>
<Button
android:id="@+id/stopButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="停止生成"
android:textSize="14sp"
android:layout_marginStart="4dp"/>
</LinearLayout>
</LinearLayout>
<!-- Output Area -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:orientation="vertical"
android:layout_marginBottom="16dp">
<TextView
android:id="@+id/outputStatusLabel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="等待发送"
android:textSize="12sp"
android:textColor="@android:color/holo_blue_dark"
android:layout_marginBottom="4dp"/>
<TextView
android:id="@+id/outputTextView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:background="@drawable/edittext_border"
android:padding="8sp"
android:textSize="14sp"
android:text="发送消息后结果将在此显示"
android:textColor="@android:color/black"/>
<Button
android:id="@+id/copyButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="复制"
android:textSize="12sp"
android:layout_gravity="end"
android:layout_marginTop="4dp"/>
</LinearLayout>
<!-- Custom Buttons -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/btnCopyResult"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="复制结果"
android:textSize="14sp"
android:layout_marginEnd="4dp"/>
</LinearLayout>
</LinearLayout> </LinearLayout>