最近,我发现许多抖音用户喜欢将小说内容一句一句地发到游戏评论框中。这种行为虽然能让更多人看到这些内容,但手动复制和粘贴却是一个繁琐的过程。为了简化这一操作,我决定开发一个应用,可以一键输入大量文本,并将其根据句号、逗号和分号划分成句子。用户可以通过一个悬浮窗轻松访问上一句、当前句子和下一句,进一步简化操作。
在这个应用的开发过程中,我使用了 Android 的三个重要组件:Fragment
、ViewModel
和 View Binding
。下面,我将详细介绍这三者的关系以及如何实现具体功能的代码。
1. Fragment
Fragment
是 Android UI 的一部分,可以被看作一个独立的界面模块。在应用中,多个 Fragment
可以在同一个 Activity
中共存,各自管理自己的 UI 和生命周期事件。在我们的应用中,HomeFragment
负责与用户交互,例如输入文本和处理按钮点击事件。
以下是 HomeFragment
的完整代码:
java
package cn.techfanyi.ui.home;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import cn.techfanyi.databinding.FragmentHomeBinding;
public class HomeFragment extends Fragment {
private FragmentHomeBinding binding;
private HomeViewModel homeViewModel;
public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
// 创建ViewModel
homeViewModel = new ViewModelProvider(this).get(HomeViewModel.class);
// 绑定视图
binding = FragmentHomeBinding.inflate(inflater, container, false);
View root = binding.getRoot();
// 获取输入框和按钮
EditText editText = binding.editText; // 假设在XML中有一个EditText用于输入
Button recognizeButton = binding.recognizeButton; // 假设在XML中有一个按钮用于识别
TextView resultTextView = binding.resultTextView; // 显示结果的TextView
// 设置按钮点击事件
recognizeButton.setOnClickListener(v -> {
String inputText = editText.getText().toString();
if (TextUtils.isEmpty(inputText)) {
Toast.makeText(getContext(), "请输入文本", Toast.LENGTH_SHORT).show();
return;
}
// 将输入的文本划分为句子
String[] sentences = inputText.split("[。;,]");
homeViewModel.setSentences(sentences); // 将句子存储到ViewModel中
// 更新结果文本
resultTextView.setText(TextUtils.join("\n", sentences));
});
return root;
}
@Override
public void onDestroyView() {
super.onDestroyView();
binding = null;
}
}
2. ViewModel
ViewModel
是 Android 架构组件,用于存储和管理与 UI 相关的数据。其主要作用是持久化数据,使其能够在 Fragment
或 Activity
的生命周期变化(如屏幕旋转)时依然可用。在我们的应用中,HomeViewModel
用于管理文本数据和句子划分的逻辑。
以下是 HomeViewModel
的代码:
java
package cn.techfanyi.ui.home;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
public class HomeViewModel extends ViewModel {
private final MutableLiveData<String[]> sentences;
public HomeViewModel() {
sentences = new MutableLiveData<>();
}
// 设置句子
public void setSentences(String[] sentencesArray) {
sentences.setValue(sentencesArray);
}
// 获取句子
public LiveData<String[]> getSentences() {
return sentences;
}
}
3. View Binding
View Binding
是一种更安全和高效的方式来访问 XML 布局中的视图。它会为每个 XML 布局文件生成一个绑定类,可以直接通过绑定类访问布局中的视图,而无需手动调用 findViewById()
。
在本应用中,假设我们的布局文件为 fragment_home.xml
,以下是示例 XML 布局代码:
xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.home.HomeFragment">
<EditText
android:id="@+id/editText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="请输入长句子" />
<Button
android:id="@+id/recognizeButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="识别句子"
app:layout_constraintTop_toBottomOf="@id/editText"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<TextView
android:id="@+id/resultTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/recognizeButton"
android:padding="16dp" />
</androidx.constraintlayout.widget.ConstraintLayout>
悬浮窗功能实现
悬浮窗的功能主要实现上一句、当前句子、下一句的展示与复制。为了实现这个功能,我们可以使用 WindowManager
来创建一个悬浮窗,并在其中显示相应的句子。
java
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
import android.widget.TextView;
public class FloatingWindowManager {
private Context context;
private WindowManager windowManager;
private View floatingView;
private TextView previousSentenceTextView;
private TextView currentSentenceTextView;
private TextView nextSentenceTextView;
public FloatingWindowManager(Context context) {
this.context = context;
windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
createFloatingView();
}
private void createFloatingView() {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
floatingView = inflater.inflate(R.layout.floating_window_layout, null);
previousSentenceTextView = floatingView.findViewById(R.id.previousSentence);
currentSentenceTextView = floatingView.findViewById(R.id.currentSentence);
nextSentenceTextView = floatingView.findViewById(R.id.nextSentence);
// 设置句子点击事件
currentSentenceTextView.setOnClickListener(v -> {
// 复制当前句子到剪贴板
ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
ClipData clip = ClipData.newPlainText("sentence", currentSentenceTextView.getText());
clipboard.setPrimaryClip(clip);
});
// 添加悬浮窗
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT);
params.gravity = Gravity.TOP | Gravity.LEFT; // 悬浮窗的位置
params.x = 0;
params.y = 100;
windowManager.addView(floatingView, params);
}
public void updateSentences(String[] sentences, int currentIndex) {
if (currentIndex > 0) {
previousSentenceTextView.setText(sentences[currentIndex - 1]);
}
currentSentenceTextView.setText(sentences[currentIndex]);
if (currentIndex < sentences.length - 1) {
nextSentenceTextView.setText(sentences[currentIndex + 1]);
}
}
}
关系及绑定
在应用中,Fragment
、ViewModel
和 View Binding
之间的关系如下:
- Fragment :负责管理 UI 和用户交互逻辑,通过
ViewModel
获取和存储数据,使用View Binding
直接访问布局中的视图。 - ViewModel:负责存储和管理与 UI 相关的数据,确保数据在生命周期变化时的持久性。
- View Binding:简化了视图的访问,提高了代码的安全性和可读性。
总结
通过使用 Fragment
、ViewModel
和 View Binding
,我们可以创建一个简化小说内容分享的应用。应用不仅提升了用户体验
,同时也展示了 Android 开发中这三者之间的紧密关系。希望这篇博客能够为你的 Android 开发之路提供一些启示!