Bootstrap

简易安卓句分器实现

最近,我发现许多抖音用户喜欢将小说内容一句一句地发到游戏评论框中。这种行为虽然能让更多人看到这些内容,但手动复制和粘贴却是一个繁琐的过程。为了简化这一操作,我决定开发一个应用,可以一键输入大量文本,并将其根据句号、逗号和分号划分成句子。用户可以通过一个悬浮窗轻松访问上一句、当前句子和下一句,进一步简化操作。

在这个应用的开发过程中,我使用了 Android 的三个重要组件:FragmentViewModelView Binding。下面,我将详细介绍这三者的关系以及如何实现具体功能的代码。

1. Fragment

Fragment 是 Android UI 的一部分,可以被看作一个独立的界面模块。在应用中,多个 Fragment 可以在同一个 Activity 中共存,各自管理自己的 UI 和生命周期事件。在我们的应用中,HomeFragment 负责与用户交互,例如输入文本和处理按钮点击事件。

以下是 HomeFragment 的完整代码:

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 相关的数据。其主要作用是持久化数据,使其能够在 FragmentActivity 的生命周期变化(如屏幕旋转)时依然可用。在我们的应用中,HomeViewModel 用于管理文本数据和句子划分的逻辑。

以下是 HomeViewModel 的代码:

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 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 来创建一个悬浮窗,并在其中显示相应的句子。

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]);
        }
    }
}

关系及绑定

在应用中,FragmentViewModelView Binding 之间的关系如下:

  • Fragment:负责管理 UI 和用户交互逻辑,通过 ViewModel 获取和存储数据,使用 View Binding 直接访问布局中的视图。
  • ViewModel:负责存储和管理与 UI 相关的数据,确保数据在生命周期变化时的持久性。
  • View Binding:简化了视图的访问,提高了代码的安全性和可读性。

总结

通过使用 FragmentViewModelView Binding,我们可以创建一个简化小说内容分享的应用。应用不仅提升了用户体验

,同时也展示了 Android 开发中这三者之间的紧密关系。希望这篇博客能够为你的 Android 开发之路提供一些启示!

;