MyNote极简便签

  • build.gradle.kts
    // Room数据库
    val roomVersion = "2.6.1"
    //noinspection UseTomlInstead
    implementation("androidx.room:room-runtime:$roomVersion")
    //noinspection UseTomlInstead
    annotationProcessor("androidx.room:room-compiler:$roomVersion")

    // RecyclerView列表
    //noinspection UseTomlInstead
    implementation("androidx.recyclerview:recyclerview:1.3.2")
import androidx.room.Database;
import androidx.room.Room;
import androidx.room.RoomDatabase;
import android.content.Context;

@Database(entities = Note.class, version = 3, exportSchema = false)
public abstract class NoteDatabase extends RoomDatabase {
    private static NoteDatabase instance;
    public abstract NoteDao noteDao();

    // 单例模式,避免重复创建数据库
    public static synchronized NoteDatabase getInstance(Context context) {
        if (instance == null) {
            instance = Room.databaseBuilder(context.getApplicationContext(),
                            NoteDatabase.class, "note_database")
                    .fallbackToDestructiveMigration()
                    .build();
        }
        return instance;
    }
}

import androidx.room.Dao;
import androidx.room.Delete;
import androidx.room.Insert;
import androidx.room.Query;
import androidx.room.Update;
import java.util.List;

@Dao
public interface NoteDao {
    // 增
    @Insert
    void insert(Note note);

    // 删
    @Delete
    void delete(Note note);

    // 改
    @Update
    void update(Note note);

    // 查所有便签(按时间倒序)
    @Query("SELECT * FROM note_table ORDER BY timestamp DESC")
    List<Note> getAllNotes();

    // 根据id查询单条便签
    @Query("SELECT * FROM note_table WHERE id = :id")
    Note getNoteById(int id);
}

import androidx.room.Entity;
import androidx.room.PrimaryKey;

@Entity(tableName = "note_table") // 数据库表名
public class Note {
    // 自增主键
    @PrimaryKey(autoGenerate = true)
    private int id;
    private String content;     // 便签内容
    private long timestamp;        // 创建/修改时间戳

    // 构造函数
    public Note(String content, long timestamp) {
        this.content = content;
        this.timestamp = timestamp;
    }

    // Getter & Setter
    public int getId() { return id; }
    public void setId(int id) { this.id = id; }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public long getTimestamp() {
        return timestamp;
    }

    public void setTimestamp(long timestamp) {
        this.timestamp = timestamp;
    }
}

  • activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/page_background"
    android:orientation="vertical">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:paddingVertical="60dp" />

    <ImageButton
        android:id="@+id/btn_add"
        android:layout_width="56dp"
        android:layout_height="56dp"
        android:layout_gravity="end"
        android:layout_margin="16dp"
        android:background="@drawable/btn_fab_background"
        android:contentDescription="@string/str_add"
        android:elevation="10dp"
        android:src="@android:drawable/ic_menu_add"
        android:stateListAnimator="@null"
        android:text="@null"
        android:tint="@android:color/white"
        android:translationZ="10dp" />

</LinearLayout>

  • note_item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginLeft="12dp"
    android:layout_marginTop="4dp"
    android:layout_marginRight="12dp"
    android:layout_marginBottom="8dp"
    android:background="@drawable/item_bg_rounded"
    android:orientation="vertical"
    android:padding="16dp">

    <!-- 内容文本 -->
    <TextView
        android:id="@+id/tv_content"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="4dp"
        android:ellipsize="end"
        android:lineSpacingExtra="2dp"
        android:maxLines="2"
        android:textColor="@color/black"
        android:textSize="14sp" />

    <!-- 时间文本 -->
    <TextView
        android:id="@+id/tv_time"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="6dp"
        android:textColor="#666666"
        android:textSize="12sp" />

    <!-- 删除按钮 -->
    <Button
        android:id="@+id/btn_delete"
        android:layout_width="80dp"
        android:layout_height="32dp"
        android:layout_gravity="end"
        android:layout_marginTop="12dp"
        android:background="@drawable/btn_delete_bg"
        android:text="@string/str_delete"
        android:textColor="@android:color/white"
        android:textSize="12sp"
        android:visibility="gone" />

</LinearLayout>

  • item_bg_rounded.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- 背景色 -->
    <solid android:color="@android:color/white" />
    <!-- 圆角 -->
    <corners android:radius="12dp" />
    <!-- 描边(可选) -->
    <stroke
        android:width="0.5dp"
        android:color="#EEEEEE" />
</shape>

  • btn_delete_bg.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- 按钮背景色 -->
    <solid android:color="#FF4444" />
    <!-- 圆角 -->
    <corners android:radius="16dp" />
    <!-- 点击水波纹效果(可选),可以在布局中用 android:foreground="?attr/selectableItemBackground" 替代 -->
</shape>

  • activity_add_edit_note.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/page_background"
    android:fitsSystemWindows="true"
    android:orientation="vertical"
    android:padding="16dp">

    <EditText
        android:id="@+id/et_content"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_marginTop="16dp"
        android:layout_weight="1"
        android:gravity="top"
        android:hint="@string/str_input_content"
        android:textSize="16sp" />

    <Button
        android:id="@+id/btn_save"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/str_save" />

</LinearLayout>

  • color
<color name="page_background">#2b2d30</color>
  • strings.xml
<resources>
    <string name="app_name">我的便签</string>
    <string name="str_add">添加</string>
    <string name="str_input_content">请输入内容</string>
    <string name="str_save">保存</string>
    <string name="str_delete">删除</string>
</resources>
  • themes.xml
<resources xmlns:tools="http://schemas.android.com/tools">
    <!-- Base application theme. -->
    <style name="AppTheme" parent="android:Theme.DeviceDefault.NoActionBar.TranslucentDecor">
        <!-- Customize your light theme here. -->
        <!-- <item name="colorPrimary">@color/my_light_primary</item> -->
    </style>

    <style name="Theme.Template" parent="AppTheme" />
</resources>
  • AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.Template">
        <activity
            android:name=".ui.MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name=".ui.AddEditNoteActivity"
            android:exported="false"
            android:windowSoftInputMode="adjustResize|stateAlwaysVisible" />
    </application>

</manifest>
import android.app.Activity;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.widget.ImageButton;
import android.widget.Toast;

import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import com.example.note.R;
import com.example.note.db.Note;
import com.example.note.db.NoteDatabase;

import org.jetbrains.annotations.Nullable;

import java.util.List;

public class MainActivity extends Activity {
    public static final int ADD_NOTE_REQUEST = 1;
    public static final int EDIT_NOTE_REQUEST = 2;
    private NoteDatabase noteDatabase;
    private NoteAdapter adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 初始化数据库
        noteDatabase = NoteDatabase.getInstance(this);

        // 列表配置
        RecyclerView recyclerView = findViewById(R.id.recycler_view);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        adapter = new NoteAdapter();
        recyclerView.setAdapter(adapter);

        // 加载便签
        loadNotes();

        // 添加便签
        ImageButton btnAdd = findViewById(R.id.btn_add);
        btnAdd.setOnClickListener(v -> {
            Intent intent = new Intent(MainActivity.this, AddEditNoteActivity.class);
            startActivityForResult(intent, ADD_NOTE_REQUEST);
        });

        // 点击编辑
        adapter.setOnItemClickListener(note -> {
            Intent intent = new Intent(MainActivity.this, AddEditNoteActivity.class);
            intent.putExtra(AddEditNoteActivity.EXTRA_ID, note.getId());
            intent.putExtra(AddEditNoteActivity.EXTRA_CONTENT, note.getContent());
            startActivityForResult(intent, EDIT_NOTE_REQUEST);
        });

        // 删除便签
        adapter.setOnDeleteClickListener(note -> {
            AsyncTask.execute(() -> {
                noteDatabase.noteDao().delete(note);
                runOnUiThread(() -> {
                    Toast.makeText(MainActivity.this, "已删除", Toast.LENGTH_SHORT).show();
                    loadNotes();
                });
            });
        });
    }

    // 加载所有便签(子线程)
    private void loadNotes() {
        AsyncTask.execute(() -> {
            List<Note> notes = noteDatabase.noteDao().getAllNotes();
            runOnUiThread(() -> adapter.setNotes(notes));
        });
    }

    // 处理添加/编辑结果
    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (resultCode == RESULT_OK && data != null) {
            String content = data.getStringExtra(AddEditNoteActivity.EXTRA_CONTENT);
            long time = data.getLongExtra(AddEditNoteActivity.EXTRA_TIME, 0);
            Note note = new Note(content, time);

            // 添加
            if (requestCode == ADD_NOTE_REQUEST) {
                AsyncTask.execute(() -> noteDatabase.noteDao().insert(note));
                Toast.makeText(this, "便签已保存", Toast.LENGTH_SHORT).show();
            }
            // 编辑
            else if (requestCode == EDIT_NOTE_REQUEST) {
                int id = data.getIntExtra(AddEditNoteActivity.EXTRA_ID, -1);
                if (id != -1) {
                    note.setId(id);
                    AsyncTask.execute(() -> noteDatabase.noteDao().update(note));
                    Toast.makeText(this, "便签已更新", Toast.LENGTH_SHORT).show();
                }
            }
            loadNotes();
        }
    }
}

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView;

import com.example.note.R;
import com.example.note.db.Note;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

public class NoteAdapter extends RecyclerView.Adapter<NoteAdapter.NoteHolder> {
    private List<Note> notes = new ArrayList<>();
    private OnItemClickListener listener;
    private OnDeleteClickListener deleteListener;
    private final SimpleDateFormat SDF = new SimpleDateFormat("MM-dd HH:mm:SS", Locale.getDefault());


    
    @Override
    public NoteHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.note_item, parent, false);
        return new NoteHolder(view);
    }

    @Override
    public void onBindViewHolder(NoteHolder holder, int position) {
        Note currentNote = notes.get(position);
        holder.tvContent.setText(currentNote.getContent());
        holder.tvTime.setText(SDF.format(currentNote.getTimestamp()));
    }

    @Override
    public int getItemCount() { return notes.size(); }

    public void setNotes(List<Note> notes) {
        this.notes = notes;
        notifyDataSetChanged();
    }

    public Note getNoteAt(int position) {
        return notes.get(position);
    }

    class NoteHolder extends RecyclerView.ViewHolder {
        private final TextView tvContent, tvTime;
        private final Button btnDelete;

        public NoteHolder(View itemView) {
            super(itemView);
            tvContent = itemView.findViewById(R.id.tv_content);
            tvTime = itemView.findViewById(R.id.tv_time);
            btnDelete = itemView.findViewById(R.id.btn_delete);

            // 点击事件
            itemView.setOnClickListener(v -> {
                int position = getAbsoluteAdapterPosition();
                if (listener != null && position != RecyclerView.NO_POSITION) {
                    listener.onItemClick(notes.get(position));
                }
            });

            itemView.setOnLongClickListener(v -> {
                btnDelete.setVisibility(View.VISIBLE);
                return true;
            });

            // 点击删除
            btnDelete.setOnClickListener(v -> {
                int position = getAbsoluteAdapterPosition();
                if (deleteListener != null && position != RecyclerView.NO_POSITION) {
                    deleteListener.onDeleteClick(notes.get(position));
                }
            });
        }
    }

    public interface OnItemClickListener {
        void onItemClick(Note note);
    }

    public void setOnItemClickListener(OnItemClickListener listener) {
        this.listener = listener;
    }

    // 删除点击
    public interface OnDeleteClickListener {
        void onDeleteClick(Note note);
    }

    public void setOnDeleteClickListener(OnDeleteClickListener listener) {
        this.deleteListener = listener;
    }
}

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

import com.example.note.R;

public class AddEditNoteActivity extends Activity {
    public static final String EXTRA_ID = "extra_id";
    public static final String EXTRA_CONTENT = "extra_content";
    public static final String EXTRA_TIME = "extra_time";

    private EditText etContent;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_add_edit_note);

        etContent = findViewById(R.id.et_content);
        Button btnSave = findViewById(R.id.btn_save);

        // 编辑模式:填充原有数据
        Intent intent = getIntent();
        if (intent.hasExtra(EXTRA_ID)) {
            setTitle("编辑便签");
            etContent.setText(intent.getStringExtra(EXTRA_CONTENT));
        } else {
            setTitle("添加便签");
        }

        // 保存按钮
        btnSave.setOnClickListener(v -> saveNote());

        etContent.requestFocus();


//        // 监听软键盘高度变化(仅 Android 11+ 生效)
//        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
//            getWindow().getDecorView().setOnApplyWindowInsetsListener((view, insets) -> {
//                // 获取软键盘高度(底部内边距)
//                int keyboardHeight = insets.getInsets(WindowInsets.Type.ime()).bottom;
//
//                // 调整按钮底部边距,使其显示在键盘上方
//                LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) btnSave.getLayoutParams();
//                params.bottomMargin = keyboardHeight; // 可自定义间距,如 keyboardHeight + 10
//                btnSave.setLayoutParams(params);
//
//                // 返回处理后的Insets
//                return view.onApplyWindowInsets(insets);
//            });
//            // 强制触发Insets计算
//            getWindow().getDecorView().requestApplyInsets();
//        }
    }

    private void saveNote() {
        String content = etContent.getText().toString().trim();

        if (content.isEmpty()) {
            Toast.makeText(this, "内容不能为空", Toast.LENGTH_SHORT).show();
            return;
        }

        // 保存数据
        Intent data = new Intent();
        data.putExtra(EXTRA_CONTENT, content);
        data.putExtra(EXTRA_TIME, System.currentTimeMillis());

        int id = getIntent().getIntExtra(EXTRA_ID, -1);
        if (id != -1) {
            data.putExtra(EXTRA_ID, id);
        }

        setResult(RESULT_OK, data);
        finish();
    }
}

posted @ 2026-03-23 17:39  良韬  阅读(4)  评论(0)    收藏  举报