功能优化

This commit is contained in:
lihongwei 2025-06-30 14:18:51 +08:00
parent 73e2478168
commit 5a78b467fc
8 changed files with 197 additions and 78 deletions

View File

@ -9,8 +9,11 @@ import com.auto.clicker.autoclicker.room.entity.Solution;
import java.util.List; import java.util.List;
import com.google.gson.annotations.Expose; // 确保导入 @Expose
public class SolutionWithEventsAndPoints { public class SolutionWithEventsAndPoints {
@Embedded @Embedded
@Expose
public Solution solution; public Solution solution;
@Relation( @Relation(
@ -18,10 +21,12 @@ public class SolutionWithEventsAndPoints {
entityColumn = "solution_id", entityColumn = "solution_id",
entity = EventEntity.class entity = EventEntity.class
) )
@Expose
public List<EventWithPoints> events; public List<EventWithPoints> events;
public static class EventWithPoints { public static class EventWithPoints {
@Embedded @Embedded
@Expose
public EventEntity eventEntity; public EventEntity eventEntity;
@Relation( @Relation(
@ -29,6 +34,7 @@ public class SolutionWithEventsAndPoints {
entityColumn = "pointId", entityColumn = "pointId",
entity = TouchPoint.class entity = TouchPoint.class
) )
@Expose
public TouchPoint clickTouchPoint; public TouchPoint clickTouchPoint;
@Relation( @Relation(
@ -36,6 +42,7 @@ public class SolutionWithEventsAndPoints {
entityColumn = "pointId", entityColumn = "pointId",
entity = TouchPoint.class entity = TouchPoint.class
) )
@Expose
public TouchPoint slideStartTouchPoint; public TouchPoint slideStartTouchPoint;
@Relation( @Relation(
@ -43,6 +50,7 @@ public class SolutionWithEventsAndPoints {
entityColumn = "pointId", entityColumn = "pointId",
entity = TouchPoint.class entity = TouchPoint.class
) )
@Expose
public TouchPoint slideEndTouchPoint; public TouchPoint slideEndTouchPoint;
} }
} }

View File

@ -164,6 +164,73 @@ public class EventRepository {
}); });
} }
@Transaction
public void importSolutionFromData(SolutionWithEventsAndPoints importedData, RepositoryCallback<Void> callback) {
databaseWriteExecutor.execute(() -> {
try {
Solution newSolution = new Solution(importedData.solution.solutionName, importedData.solution.mode);
long newSolutionId = solutionDao.insertSolution(newSolution);
if (newSolutionId == -1) {
throw new RuntimeException("Failed to import solution: Could not insert new Solution.");
}
for (SolutionWithEventsAndPoints.EventWithPoints eventWithPoints : importedData.events) {
Long newClickPointId = null;
if (eventWithPoints.clickTouchPoint != null) {
TouchPoint newClickPoint = new TouchPoint(eventWithPoints.clickTouchPoint.x, eventWithPoints.clickTouchPoint.y);
newClickPointId = touchPointDao.insertPoint(newClickPoint);
if (newClickPointId == -1) throw new RuntimeException("Failed to import click point.");
}
Long newSlideStartPointId = null;
if (eventWithPoints.slideStartTouchPoint != null) {
TouchPoint newSlideStartPoint = new TouchPoint(eventWithPoints.slideStartTouchPoint.x, eventWithPoints.slideStartTouchPoint.y);
newSlideStartPointId = touchPointDao.insertPoint(newSlideStartPoint);
if (newSlideStartPointId == -1) throw new RuntimeException("Failed to import slide start point.");
}
Long newSlideEndPointId = null;
if (eventWithPoints.slideEndTouchPoint != null) {
TouchPoint newSlideEndPoint = new TouchPoint(eventWithPoints.slideEndTouchPoint.x, eventWithPoints.slideEndTouchPoint.y);
newSlideEndPointId = touchPointDao.insertPoint(newSlideEndPoint);
if (newSlideEndPointId == -1) throw new RuntimeException("Failed to import slide end point.");
}
EventEntity newEventEntity = new EventEntity(
newSolutionId, // 关联到新生成的方案ID
eventWithPoints.eventEntity.eventType,
eventWithPoints.eventEntity.orderInSolution,
newClickPointId,
newSlideStartPointId,
newSlideEndPointId,
eventWithPoints.eventEntity.duration
);
long newEventEntityId = eventEntityDao.insertEventEntity(newEventEntity);
if (newEventEntityId == -1) {
throw new RuntimeException("Failed to import event entity.");
}
}
if (callback != null) callback.onComplete(null);
} catch (Exception e) {
Log.e("EventRepository", "Error importing solution: " + e.getMessage(), e);
if (callback != null) {
callback.onError(e);
}
}
});
}
public SolutionWithEventsAndPoints importSolutionFromJson(String jsonString) {
try {
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
return gson.fromJson(jsonString, SolutionWithEventsAndPoints.class);
} catch (Exception e) {
Log.e("EventRepository", "Error parsing JSON for import: " + e.getMessage(), e);
return null;
}
}
@Transaction @Transaction
public void saveDuplicateSolution(SolutionWithEventsAndPoints originalData, RepositoryCallback<Void> callback) { public void saveDuplicateSolution(SolutionWithEventsAndPoints originalData, RepositoryCallback<Void> callback) {
databaseWriteExecutor.execute(() -> { databaseWriteExecutor.execute(() -> {

View File

@ -8,7 +8,6 @@ import android.os.Looper;
import android.util.Log; import android.util.Log;
import android.view.View; import android.view.View;
import android.widget.EditText; import android.widget.EditText;
import android.widget.Toast;
import androidx.activity.EdgeToEdge; import androidx.activity.EdgeToEdge;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
@ -28,6 +27,9 @@ import com.auto.clicker.autoclicker.ui.adapter.floating.SolutionAdapter;
import com.auto.clicker.autoclicker.ui.dialog.CreateModeSelectionDialog; import com.auto.clicker.autoclicker.ui.dialog.CreateModeSelectionDialog;
import com.auto.clicker.autoclicker.view.FloatingViewManager; import com.auto.clicker.autoclicker.view.FloatingViewManager;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -40,6 +42,7 @@ public class ScriptsActivity extends AppCompatActivity implements SolutionAdapte
private static final String TAG = "ScriptsActivity"; private static final String TAG = "ScriptsActivity";
private static final int CREATE_FILE_REQUEST_CODE = 1; private static final int CREATE_FILE_REQUEST_CODE = 1;
private static final int PICK_FILE_REQUEST_CODE = 2;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
@ -54,12 +57,10 @@ public class ScriptsActivity extends AppCompatActivity implements SolutionAdapte
}); });
binding.scriptsRecyclerView.setLayoutManager(new LinearLayoutManager(this)); binding.scriptsRecyclerView.setLayoutManager(new LinearLayoutManager(this));
// this 作为 OnSolutionClickListener OnOptionMenuClickListener 传入
solutionsAdapter = new SolutionAdapter(new ArrayList<>(), this::onSolutionSelected, this); solutionsAdapter = new SolutionAdapter(new ArrayList<>(), this::onSolutionSelected, this);
binding.scriptsRecyclerView.setAdapter(solutionsAdapter); binding.scriptsRecyclerView.setAdapter(solutionsAdapter);
// 确保 FloatingViewManager Activity 创建时被初始化 floatingViewManager = new FloatingViewManager(this);
floatingViewManager = new FloatingViewManager(this); // 如果 FloatingViewManager 依赖 Context这里需要传递
binding.btnBack.setOnClickListener(v -> finish()); binding.btnBack.setOnClickListener(v -> finish());
@ -69,13 +70,11 @@ public class ScriptsActivity extends AppCompatActivity implements SolutionAdapte
}); });
binding.importScripts.setOnClickListener(v -> { binding.importScripts.setOnClickListener(v -> {
Toast.makeText(this, "导入方案功能待实现", Toast.LENGTH_SHORT).show(); pickJsonFileForImport();
// TODO: 实现导入方案的逻辑
}); });
binding.fabImportScripts.setOnClickListener(v -> { binding.customImportView.setOnClickListener(v -> {
Toast.makeText(this, "右下角导入方案功能待实现", Toast.LENGTH_SHORT).show(); pickJsonFileForImport();
// TODO: 实现右下角导入方案的逻辑可能与上面的importScripts按钮功能相同或类似
}); });
loadSolutions(); loadSolutions();
@ -90,24 +89,16 @@ public class ScriptsActivity extends AppCompatActivity implements SolutionAdapte
private void loadSolutions() { private void loadSolutions() {
if (floatingViewManager == null) { if (floatingViewManager == null) {
Log.e(TAG, "FloatingViewManager 未设置,无法加载方案列表。"); Log.e(TAG, "FloatingViewManager 未设置,无法加载方案列表。");
Toast.makeText(this, "加载方案列表功能不可用,请联系开发者。", Toast.LENGTH_SHORT).show();
updateLayout(true); updateLayout(true);
return; return;
} }
// 确保 FloatingViewManager 能够提供 EventRepository 实例
// 假设 FloatingViewManager 内部管理着 EventRepository
floatingViewManager.getEventRepository().getAllSolutions(new EventRepository.RepositoryCallback<List<Solution>>() { floatingViewManager.getEventRepository().getAllSolutions(new EventRepository.RepositoryCallback<List<Solution>>() {
@Override @Override
public void onComplete(List<Solution> solutions) { public void onComplete(List<Solution> solutions) {
new Handler(Looper.getMainLooper()).post(() -> { new Handler(Looper.getMainLooper()).post(() -> {
solutionsAdapter.setSolutions(solutions); solutionsAdapter.setSolutions(solutions);
updateLayout(solutions.isEmpty()); updateLayout(solutions.isEmpty());
if (solutions.isEmpty()) {
Toast.makeText(ScriptsActivity.this, "没有可加载的方案。", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(ScriptsActivity.this, "方案加载成功!", Toast.LENGTH_SHORT).show();
}
}); });
} }
@ -115,7 +106,6 @@ public class ScriptsActivity extends AppCompatActivity implements SolutionAdapte
public void onError(Exception e) { public void onError(Exception e) {
new Handler(Looper.getMainLooper()).post(() -> { new Handler(Looper.getMainLooper()).post(() -> {
Log.e(TAG, "加载方案列表失败: " + e.getMessage(), e); Log.e(TAG, "加载方案列表失败: " + e.getMessage(), e);
Toast.makeText(ScriptsActivity.this, "加载方案列表失败。", Toast.LENGTH_SHORT).show();
updateLayout(true); updateLayout(true);
}); });
} }
@ -126,11 +116,11 @@ public class ScriptsActivity extends AppCompatActivity implements SolutionAdapte
if (isEmpty) { if (isEmpty) {
binding.noScriptsGroup.setVisibility(View.VISIBLE); binding.noScriptsGroup.setVisibility(View.VISIBLE);
binding.scriptsRecyclerView.setVisibility(View.GONE); binding.scriptsRecyclerView.setVisibility(View.GONE);
binding.fabImportScripts.setVisibility(View.GONE); binding.customImportView.setVisibility(View.GONE);
} else { } else {
binding.noScriptsGroup.setVisibility(View.GONE); binding.noScriptsGroup.setVisibility(View.GONE);
binding.scriptsRecyclerView.setVisibility(View.VISIBLE); binding.scriptsRecyclerView.setVisibility(View.VISIBLE);
binding.fabImportScripts.setVisibility(View.VISIBLE); binding.customImportView.setVisibility(View.VISIBLE);
} }
} }
@ -138,10 +128,8 @@ public class ScriptsActivity extends AppCompatActivity implements SolutionAdapte
if (floatingViewManager != null) { if (floatingViewManager != null) {
floatingViewManager.showFloatingViews(solution.getMode()); floatingViewManager.showFloatingViews(solution.getMode());
floatingViewManager.loadSolution(solution.getSolutionId()); floatingViewManager.loadSolution(solution.getSolutionId());
Toast.makeText(this, "正在加载方案: " + solution.getSolutionName(), Toast.LENGTH_SHORT).show();
} else { } else {
Log.e(TAG, "FloatingViewManager 未设置!无法加载方案。"); Log.e(TAG, "FloatingViewManager 未设置!无法加载方案。");
Toast.makeText(this, "加载功能不可用,请联系开发者。", Toast.LENGTH_SHORT).show();
} }
} }
@ -180,8 +168,7 @@ public class ScriptsActivity extends AppCompatActivity implements SolutionAdapte
@Override @Override
public void onComplete(Void result) { public void onComplete(Void result) {
new Handler(Looper.getMainLooper()).post(() -> { new Handler(Looper.getMainLooper()).post(() -> {
Toast.makeText(ScriptsActivity.this, "方案已重命名为: " + newName, Toast.LENGTH_SHORT).show(); loadSolutions();
loadSolutions(); // 刷新列表
}); });
} }
@ -189,14 +176,9 @@ public class ScriptsActivity extends AppCompatActivity implements SolutionAdapte
public void onError(Exception e) { public void onError(Exception e) {
new Handler(Looper.getMainLooper()).post(() -> { new Handler(Looper.getMainLooper()).post(() -> {
Log.e(TAG, "重命名失败: " + e.getMessage(), e); Log.e(TAG, "重命名失败: " + e.getMessage(), e);
Toast.makeText(ScriptsActivity.this, "重命名失败: " + e.getMessage(), Toast.LENGTH_SHORT).show();
}); });
} }
}); });
} else if (newName.isEmpty()) {
Toast.makeText(ScriptsActivity.this, "方案名称不能为空。", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(ScriptsActivity.this, "新名称与原名称相同。", Toast.LENGTH_SHORT).show();
} }
}); });
builder.setNegativeButton("取消", (dialog, which) -> dialog.cancel()); builder.setNegativeButton("取消", (dialog, which) -> dialog.cancel());
@ -211,23 +193,18 @@ public class ScriptsActivity extends AppCompatActivity implements SolutionAdapte
floatingViewManager.getEventRepository().saveDuplicateSolution(originalData, new EventRepository.RepositoryCallback<Void>() { floatingViewManager.getEventRepository().saveDuplicateSolution(originalData, new EventRepository.RepositoryCallback<Void>() {
@Override @Override
public void onComplete(Void result) { public void onComplete(Void result) {
new Handler(Looper.getMainLooper()).post(() -> { new Handler(Looper.getMainLooper()).post(ScriptsActivity.this::loadSolutions);
Toast.makeText(ScriptsActivity.this, "方案 '" + originalSolution.getSolutionName() + "' 已复制成功!", Toast.LENGTH_SHORT).show();
loadSolutions(); // 刷新列表
});
} }
@Override @Override
public void onError(Exception e) { public void onError(Exception e) {
new Handler(Looper.getMainLooper()).post(() -> { new Handler(Looper.getMainLooper()).post(() -> {
Log.e(TAG, "复制方案失败: " + e.getMessage(), e); Log.e(TAG, "复制方案失败: " + e.getMessage(), e);
Toast.makeText(ScriptsActivity.this, "复制方案失败: " + e.getMessage(), Toast.LENGTH_SHORT).show();
}); });
} }
}); });
} else { } else {
new Handler(Looper.getMainLooper()).post(() -> { new Handler(Looper.getMainLooper()).post(() -> {
Toast.makeText(ScriptsActivity.this, "无法找到原始方案数据进行复制。", Toast.LENGTH_SHORT).show();
}); });
} }
} }
@ -236,7 +213,6 @@ public class ScriptsActivity extends AppCompatActivity implements SolutionAdapte
public void onError(Exception e) { public void onError(Exception e) {
new Handler(Looper.getMainLooper()).post(() -> { new Handler(Looper.getMainLooper()).post(() -> {
Log.e(TAG, "获取原始方案数据失败: " + e.getMessage(), e); Log.e(TAG, "获取原始方案数据失败: " + e.getMessage(), e);
Toast.makeText(ScriptsActivity.this, "获取原始方案数据失败: " + e.getMessage(), Toast.LENGTH_SHORT).show();
}); });
} }
}); });
@ -256,8 +232,7 @@ public class ScriptsActivity extends AppCompatActivity implements SolutionAdapte
@Override @Override
public void onComplete(Void result) { public void onComplete(Void result) {
new Handler(Looper.getMainLooper()).post(() -> { new Handler(Looper.getMainLooper()).post(() -> {
Toast.makeText(ScriptsActivity.this, "方案 '" + solution.getSolutionName() + "' 已删除。", Toast.LENGTH_SHORT).show(); loadSolutions();
loadSolutions(); // 刷新列表
}); });
} }
@ -265,12 +240,18 @@ public class ScriptsActivity extends AppCompatActivity implements SolutionAdapte
public void onError(Exception e) { public void onError(Exception e) {
new Handler(Looper.getMainLooper()).post(() -> { new Handler(Looper.getMainLooper()).post(() -> {
Log.e(TAG, "删除方案失败: " + e.getMessage(), e); Log.e(TAG, "删除方案失败: " + e.getMessage(), e);
Toast.makeText(ScriptsActivity.this, "删除方案失败: " + e.getMessage(), Toast.LENGTH_SHORT).show();
}); });
} }
}); });
} }
private void pickJsonFileForImport() {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("application/json");
startActivityForResult(intent, PICK_FILE_REQUEST_CODE);
}
private void exportSolution(final Solution solution) { private void exportSolution(final Solution solution) {
floatingViewManager.getEventRepository().getSolutionWithEventsAndPoints(solution.getSolutionId(), new EventRepository.RepositoryCallback<SolutionWithEventsAndPoints>() { floatingViewManager.getEventRepository().getSolutionWithEventsAndPoints(solution.getSolutionId(), new EventRepository.RepositoryCallback<SolutionWithEventsAndPoints>() {
@Override @Override
@ -283,11 +264,7 @@ public class ScriptsActivity extends AppCompatActivity implements SolutionAdapte
intent.setType("application/json"); intent.setType("application/json");
intent.putExtra(Intent.EXTRA_TITLE, solution.getSolutionName() + ".json"); intent.putExtra(Intent.EXTRA_TITLE, solution.getSolutionName() + ".json");
startActivityForResult(intent, CREATE_FILE_REQUEST_CODE); startActivityForResult(intent, CREATE_FILE_REQUEST_CODE);
} else {
Toast.makeText(ScriptsActivity.this, "无法生成导出数据。", Toast.LENGTH_SHORT).show();
} }
} else {
Toast.makeText(ScriptsActivity.this, "无法获取方案数据进行导出。", Toast.LENGTH_SHORT).show();
} }
} }
@ -295,12 +272,57 @@ public class ScriptsActivity extends AppCompatActivity implements SolutionAdapte
public void onError(Exception e) { public void onError(Exception e) {
new Handler(Looper.getMainLooper()).post(() -> { new Handler(Looper.getMainLooper()).post(() -> {
Log.e(TAG, "获取方案数据失败: " + e.getMessage(), e); Log.e(TAG, "获取方案数据失败: " + e.getMessage(), e);
Toast.makeText(ScriptsActivity.this, "获取方案数据失败: " + e.getMessage(), Toast.LENGTH_SHORT).show();
}); });
} }
}); });
} }
private void importSolutionFromFile(Uri uri) {
try {
InputStream inputStream = getContentResolver().openInputStream(uri);
if (inputStream == null) {
return;
}
StringBuilder stringBuilder = new StringBuilder();
try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
String line;
while ((line = reader.readLine()) != null) {
stringBuilder.append(line);
}
}
String jsonString = stringBuilder.toString();
if (jsonString.isEmpty()) {
return;
}
SolutionWithEventsAndPoints importedData = floatingViewManager.getEventRepository().importSolutionFromJson(jsonString);
if (importedData != null && importedData.solution != null) {
// 保存到数据库
floatingViewManager.getEventRepository().importSolutionFromData(importedData, new EventRepository.RepositoryCallback<Void>() {
@Override
public void onComplete(Void result) {
new Handler(Looper.getMainLooper()).post(() -> {
loadSolutions(); // 刷新列表
});
}
@Override
public void onError(Exception e) {
new Handler(Looper.getMainLooper()).post(() -> {
Log.e(TAG, "导入方案失败: " + e.getMessage(), e);
});
}
});
}
} catch (Exception e) {
Log.e(TAG, "读取或解析导入文件失败: " + e.getMessage(), e);
}
}
@Override @Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data); super.onActivityResult(requestCode, resultCode, data);
@ -311,20 +333,20 @@ public class ScriptsActivity extends AppCompatActivity implements SolutionAdapte
try (OutputStream outputStream = getContentResolver().openOutputStream(uri)) { try (OutputStream outputStream = getContentResolver().openOutputStream(uri)) {
if (outputStream != null) { if (outputStream != null) {
outputStream.write(tempExportJson.getBytes()); outputStream.write(tempExportJson.getBytes());
Toast.makeText(this, "方案已成功导出!", Toast.LENGTH_SHORT).show();
} }
} catch (Exception e) { } catch (Exception e) {
Log.e(TAG, "保存文件失败: " + e.getMessage(), e); Log.e(TAG, "保存文件失败: " + e.getMessage(), e);
Toast.makeText(this, "保存文件失败: " + e.getMessage(), Toast.LENGTH_SHORT).show();
} finally { } finally {
tempExportJson = null; // 清除临时数据 tempExportJson = null;
} }
} else {
Toast.makeText(this, "没有可导出的数据。", Toast.LENGTH_SHORT).show();
} }
} else { }
Toast.makeText(this, "文件创建操作被取消或数据无效。", Toast.LENGTH_SHORT).show(); } else if (requestCode == PICK_FILE_REQUEST_CODE && resultCode == RESULT_OK) {
if (data != null && data.getData() != null) {
Uri uri = data.getData();
importSolutionFromFile(uri);
} }
} }
} }
} }

View File

@ -76,12 +76,12 @@ public class SolutionAdapter extends RecyclerView.Adapter<SolutionAdapter.Soluti
}); });
if (optionMenuListener != null) { if (optionMenuListener != null) {
holder.option.setVisibility(View.VISIBLE); // 显示按钮 holder.option.setVisibility(View.VISIBLE);
holder.option.setOnClickListener(v -> { holder.option.setOnClickListener(v -> {
holder.showOptionMenu(solution, optionMenuListener); // 调用方法显示菜单 holder.showOptionMenu(solution, optionMenuListener);
}); });
} else { } else {
holder.option.setVisibility(View.GONE); // 隐藏按钮 holder.option.setVisibility(View.GONE);
} }
} }
@ -114,28 +114,25 @@ public class SolutionAdapter extends RecyclerView.Adapter<SolutionAdapter.Soluti
option = itemView.findViewById(R.id.option); option = itemView.findViewById(R.id.option);
} }
// 显示选项菜单的私有方法
private void showOptionMenu(Solution solution, OnOptionMenuClickListener menuListener) { private void showOptionMenu(Solution solution, OnOptionMenuClickListener menuListener) {
View popupView = LayoutInflater.from(context).inflate(R.layout.option_menu_layout, null); View popupView = LayoutInflater.from(context).inflate(R.layout.option_menu_layout, null);
PopupWindow popupWindow = new PopupWindow( PopupWindow popupWindow = new PopupWindow(
popupView, popupView,
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT,
true // 设置为 true允许点击外部关闭 true
); );
popupWindow.setElevation(8f); // 添加阴影 popupWindow.setElevation(8f);
popupWindow.setBackgroundDrawable(context.getDrawable(android.R.color.transparent)); // 设置透明背景以便显示阴影 popupWindow.setBackgroundDrawable(context.getDrawable(android.R.color.transparent));
// 查找 popupView 中的 TextView
TextView tvRename = popupView.findViewById(R.id.tv_rename); TextView tvRename = popupView.findViewById(R.id.tv_rename);
TextView tvDuplicate = popupView.findViewById(R.id.tv_duplicate); TextView tvDuplicate = popupView.findViewById(R.id.tv_duplicate);
TextView tvDelete = popupView.findViewById(R.id.tv_delete); TextView tvDelete = popupView.findViewById(R.id.tv_delete);
TextView tvExport = popupView.findViewById(R.id.tv_export); TextView tvExport = popupView.findViewById(R.id.tv_export);
// 设置点击监听器
tvRename.setOnClickListener(v -> { tvRename.setOnClickListener(v -> {
menuListener.onRenameClick(solution); menuListener.onRenameClick(solution);
popupWindow.dismiss(); // 关闭弹窗 popupWindow.dismiss();
}); });
tvDuplicate.setOnClickListener(v -> { tvDuplicate.setOnClickListener(v -> {
@ -153,7 +150,6 @@ public class SolutionAdapter extends RecyclerView.Adapter<SolutionAdapter.Soluti
popupWindow.dismiss(); popupWindow.dismiss();
}); });
// 显示 PopupWindow锚定在 option 按钮下方
popupWindow.showAsDropDown(option); popupWindow.showAsDropDown(option);
} }
} }

View File

@ -84,7 +84,7 @@ public class FloatingViewManager {
private static final String KEY_CONTROL_BAR_WIDTH = "control_bar_width"; private static final String KEY_CONTROL_BAR_WIDTH = "control_bar_width";
private static final int DEFAULT_TOUCH_POINT_SIZE = 100; private static final int DEFAULT_TOUCH_POINT_SIZE = 100;
private static final int DEFAULT_CONTROL_BAR_WIDTH_DP = 38; private static final int DEFAULT_CONTROL_BAR_WIDTH_DP = 50;
private ImageView playButton; private ImageView playButton;

View File

@ -96,8 +96,9 @@
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="0dp" android:layout_height="0dp"
android:layout_marginTop="16dp" android:layout_marginTop="16dp"
android:layout_marginBottom="15dp"
android:visibility="gone" android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toTopOf="@+id/custom_import_view"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0" app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
@ -105,19 +106,42 @@
app:layout_constraintVertical_bias="1.0" app:layout_constraintVertical_bias="1.0"
tools:visibility="visible" /> tools:visibility="visible" />
<com.google.android.material.floatingactionbutton.FloatingActionButton <LinearLayout
android:id="@+id/fab_import_scripts" android:id="@+id/custom_import_view"
android:layout_width="127dp" android:layout_width="wrap_content"
android:layout_height="46dp" android:layout_height="wrap_content"
android:layout_marginEnd="24dp" android:layout_marginEnd="24dp"
android:layout_marginBottom="24dp" android:layout_marginBottom="24dp"
android:background="@drawable/rounded_edittext_background"
android:backgroundTint="@color/blue"
android:clickable="true" android:clickable="true"
android:focusable="true" android:focusable="true"
android:src="@drawable/import_script" android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingStart="16dp"
android:paddingTop="10dp"
android:paddingEnd="24dp"
android:paddingBottom="10dp"
android:visibility="gone" android:visibility="gone"
app:backgroundTint="#0BC4FC"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent">
app:tint="@color/white" />
<ImageView
android:id="@+id/icon_import"
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@drawable/import_script" />
<TextView
android:id="@+id/text_import"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:text="Import"
android:textColor="@color/white"
android:textSize="18sp"
android:textStyle="bold" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -48,7 +48,9 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:src="@drawable/script_option" android:src="@drawable/script_option"
android:visibility="gone" android:paddingEnd="10dp"
android:paddingStart="10dp"
android:visibility="visible"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />

View File

@ -14,7 +14,7 @@
android:background="?attr/selectableItemBackground" android:background="?attr/selectableItemBackground"
android:padding="12dp" android:padding="12dp"
android:text="Rename" android:text="Rename"
android:textColor="@color/black" android:textColor="@color/white"
android:textSize="16sp" /> android:textSize="16sp" />
<TextView <TextView
@ -24,7 +24,7 @@
android:background="?attr/selectableItemBackground" android:background="?attr/selectableItemBackground"
android:padding="12dp" android:padding="12dp"
android:text="Duplicate" android:text="Duplicate"
android:textColor="@color/black" android:textColor="@color/white"
android:textSize="16sp" /> android:textSize="16sp" />
<TextView <TextView
@ -34,7 +34,7 @@
android:background="?attr/selectableItemBackground" android:background="?attr/selectableItemBackground"
android:padding="12dp" android:padding="12dp"
android:text="Delete" android:text="Delete"
android:textColor="@color/black" android:textColor="@color/white"
android:textSize="16sp" /> android:textSize="16sp" />
<TextView <TextView
@ -44,7 +44,7 @@
android:background="?attr/selectableItemBackground" android:background="?attr/selectableItemBackground"
android:padding="12dp" android:padding="12dp"
android:text="Export" android:text="Export"
android:textColor="@color/black" android:textColor="@color/white"
android:textSize="16sp" /> android:textSize="16sp" />
</LinearLayout> </LinearLayout>