网络资源模板--基于 Android Studio 实现图片日记本

目录

一、项目测试视频

二、项目简介

三、项目测试环境

四、项目详情设计

Get 项目模板源码


原创 图片日记本:Android Studio 实现图片日记本

一、项目测试视频

网络资源模板--基于Android studio 实现图片日记本

二、项目简介

登录注册、添加日记、删除 获取本地图片
数据基于本地数据库sqlite进行存储。

三、项目测试环境

四、项目详情设计

package com.example.myactivity;

import android.content.ContentValues;
import android.content.Intent;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

import com.example.myactivity.db.MyDBHelper;

import androidx.appcompat.app.AppCompatActivity;

import javax.security.auth.login.LoginException;

public class RegisterActivity extends AppCompatActivity {
    //3 定义对象
    EditText et_name, et_pwd, et_email, et_phone;
    Button btn_register, btn_cancel;
    MyDBHelper mhelper;//创建一个数据库类文件
    SQLiteDatabase db;//创建一个可以操作的数据库对


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_register);
        //4 绑定控件
        initView();
        //5 注册按钮功能的实现
        btnRegister();
        //6 取消按钮功能的实现
        btnCancel();

    }

    //4 绑定控件--------代码
    private void initView() {
        et_name = findViewById(R.id.et_name_rg);
        et_pwd = findViewById(R.id.et_pwd_rg);
        et_email = findViewById(R.id.et_email_rg);
        et_phone = findViewById(R.id.et_phone_rg);
        btn_register = findViewById(R.id.bt_ok_rg);
        btn_cancel = findViewById(R.id.bt_cancel_rg);
        mhelper = new MyDBHelper(RegisterActivity.this);
        db = mhelper.getWritableDatabase();
    }

    //5 注册按钮功能的实现--------------------代码
    private void btnRegister() {
        btn_register.setOnClickListener(v -> {
            //创建一个对象,用来封装一行数据
            ContentValues values = new ContentValues();
            values.put("name", et_name.getText().toString());//将输入的用户名放到 name 列
            values.put("pwd", et_pwd.getText().toString());//将输入的密码放到 pwd 列
            values.put("email", et_email.getText().toString());//将输入的邮箱放到 email 列
            values.put("phone", et_phone.getText().toString());//将输入的电话放到 phone 列
            //将封装好的一行数据保存到数据库的 tb_userinfo 表中
            db.insert("tb_userinfo", null, values);
            Intent intent = new Intent(RegisterActivity.this, LoginActivity.class);
            Toast.makeText(RegisterActivity.this, "注册成功", Toast.LENGTH_SHORT).show();
            startActivity(intent);
            finish();
        });
    }

    //6 取消按钮功能的实现-------------------代码
    private void btnCancel() {
        btn_cancel.setOnClickListener(v -> {
            Intent intent = new Intent(RegisterActivity.this, LoginActivity.class);
            startActivity(intent);
            finish();
        });
    }

}

1. **导入必要的类和包**:
   - 导入了与界面交互、数据库操作和应用程序逻辑相关的类和包。

2. **Activity 类定义**:
   - `RegisterActivity` 类继承自 `AppCompatActivity`,表示这是一个基于 `AppCompatActivity` 的注册活动。

3. **成员变量定义**:
   - 定义了几个 `EditText` 和 `Button` 对象,用于获取用户输入和触发事件。
   - `MyDBHelper` 对象 `mhelper` 用于管理数据库。
   - `SQLiteDatabase` 对象 `db` 用于执行数据库操作。

4. **`onCreate()` 方法**:
   - 在 `onCreate()` 方法中,进行了布局文件的关联和视图初始化。
   - 调用了 `initView()` 方法,用于绑定界面上的控件并初始化数据库相关对象。
   - 调用了 `btnRegister()` 方法和 `btnCancel()` 方法,分别实现了注册按钮和取消按钮的功能。

5. **`initView()` 方法**:
   - `initView()` 方法中通过 `findViewById()` 方法找到布局文件中对应的控件,并将它们赋值给类中定义的成员变量。

6. **注册按钮功能实现 (`btnRegister()` 方法)**:
   - 使用 Lambda 表达式设置了注册按钮的点击事件监听器。
   - 当注册按钮被点击时,获取用户输入的用户名、密码、邮箱和电话,并将它们存储到 `ContentValues` 对象中。
   - 使用 `db.insert()` 方法将封装好的数据插入到名为 `tb_userinfo` 的数据库表中。
   - 弹出注册成功的 Toast 提示。
   - 创建并启动一个新的 `Intent` 对象,跳转到 `LoginActivity` 并关闭当前活动。

7. **取消按钮功能实现 (`btnCancel()` 方法)**:
   - 使用 Lambda 表达式设置了取消按钮的点击事件监听器。
   - 当取消按钮被点击时,创建并启动一个新的 `Intent` 对象,同样是跳转到 `LoginActivity` 并关闭当前活动。

这段代码实现了一个简单的注册功能,用户输入的数据通过 SQLite 数据库存储在本地,并提供了取消注册的选项。需要确保布局文件 `activity_register.xml` 中包含相应的 EditText、Button 控件,并正确设置了其 ID。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
    android:orientation="vertical"
    android:background="@drawable/loginbg"
   >
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:layout_marginTop="320dp"
        android:layout_marginLeft="60dp"
        android:layout_marginRight="60dp">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="用户名"
            android:textSize="15sp"
            android:textColor="#000000"/>
        <EditText
            android:id="@+id/et_name_lg"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@null"
            android:gravity="center"/>
    </LinearLayout>
    <View
        android:layout_width="match_parent"
        android:layout_height="2dp"
        android:background="#3700b3"
        android:layout_marginRight="60dp"
        android:layout_marginLeft="60dp"/>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:layout_marginTop="20dp"
        android:layout_marginLeft="60dp"
        android:layout_marginRight="60dp">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="密 码"
            android:textSize="15sp"
            android:textColor="#000000"/>
        <EditText
            android:id="@+id/et_pwd_lg"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@null"
            android:inputType="textPassword"
            android:gravity="center"/>
    </LinearLayout>
    <View
        android:layout_width="match_parent"
        android:layout_height="2dp"
        android:background="#3700b3"
        android:layout_marginRight="60dp"
        android:layout_marginLeft="60dp"/>
    <Button
        android:id="@+id/bt_newregister_lg"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="新用户注册"
        android:textSize="20sp"
        android:textColor="#000000"
        android:layout_marginLeft="60dp"
        android:layout_marginRight="60dp"
        android:background="@null"
        android:layout_marginTop="20dp"
        android:layout_gravity="right"/>
    <Button
        android:id="@+id/bt_login_lg"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="登录"
        android:textSize="20sp"
        android:textColor="#ffffff"
        android:layout_marginLeft="60dp"
        android:layout_marginRight="60dp"
        android:background="@drawable/btn_style_one"
        android:layout_marginTop="40dp"/>

</LinearLayout>

package com.example.myactivity;

import android.app.ProgressDialog;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.graphics.Bitmap;
import android.media.MediaMetadataRetriever;
import android.net.Uri;
import android.os.Bundle;

import android.provider.MediaStore;
import android.text.TextUtils;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import com.example.myactivity.comm.GlideSimpleLoader;
import com.example.myactivity.util.ContentUriUtil;
import com.example.myactivity.view.imagewatcher.ImageWatcherHelper;
import com.example.myactivity.ui.BaseActivity;
import com.example.myactivity.view.RichTextEditor;
import com.example.myactivity.db.bean.Group;
import com.example.myactivity.db.bean.Note;

import com.example.myactivity.comm.MyGlideEngine;
import com.example.myactivity.db.GroupDao;
import com.example.myactivity.db.NoteDao;

import com.example.myactivity.util.CommonUtil;
import com.example.myactivity.util.ImageUtils;
import com.example.myactivity.util.SDCardUtil;
import com.example.myactivity.util.StringUtils;
import com.example.myactivity.view.matisse.Matisse;
import com.example.myactivity.view.matisse.MimeType;
import com.example.myactivity.view.matisse.internal.entity.CaptureStrategy;


import java.io.File;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import androidx.appcompat.widget.Toolbar;

import io.reactivex.Observable;
import io.reactivex.ObservableEmitter;
import io.reactivex.ObservableOnSubscribe;
import io.reactivex.Observer;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;

/**
 * 新建日记
 */
public class NewActivity extends BaseActivity {
    private static final int REQUEST_CODE_CHOOSE = 23;//定义请求码常量

    private EditText et_new_title;
    private RichTextEditor et_new_content;
    private TextView tv_new_time;
    private TextView tv_new_group;

    private GroupDao groupDao;
    private NoteDao noteDao;
    private Note note;//日记对象
    private String myTitle;
    private String myContent;
    private String myGroupName;
    private String myNoteTime;
    private int flag;//区分是新建日记还是编辑日记

    private static final int cutTitleLength = 20;//截取的标题长度

    private ProgressDialog loadingDialog;
    private ProgressDialog insertDialog;
    private int screenWidth;
    private int screenHeight;
    private Disposable subsLoading;
    private Disposable subsInsert;
    private ImageWatcherHelper iwHelper;

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

        initView();

    }

 
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu_new, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.action_insert_image:
                closeSoftKeyInput();//关闭软键盘
                callGallery();
                break;
            case R.id.action_new_save:
                try {
                    saveNoteData(false);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                break;
        }
        return super.onOptionsItemSelected(item);
    }

    /**
     * 调用图库选择
     */
    private void callGallery() {
        //调用系统图库
//        Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
//        intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "video/*");// 相片类型
//        startActivityForResult(intent, 1);

        Matisse.from(this)
//                .choose(MimeType.ofAll())
                .choose(MimeType.of(MimeType.JPEG, MimeType.PNG, MimeType.GIF))//MimeType.MP4
                .countable(true)//true:选中后显示数字;false:选中后显示对号
                .maxSelectable(3)//最大选择数量为9
                //.addFilter(new GifSizeFilter(320, 320, 5 * Filter.K * Filter.K))
                .gridExpectedSize(getResources().getDimensionPixelSize(R.dimen.grid_expected_size))//图片显示表格的大小
                .restrictOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED)//图像选择和预览活动所需的方向
                .thumbnailScale(0.85f)//缩放比例
                .theme(R.style.Matisse_Zhihu)//主题  暗色主题 R.style.Matisse_Dracula
                .imageEngine(new MyGlideEngine())//图片加载方式,Glide4需要自定义实现
                .capture(true) //是否提供拍照功能,兼容7.0系统需要下面的配置
                //参数1 true表示拍照存储在共有目录,false表示存储在私有目录;参数2与 AndroidManifest中authorities值相同,用于适配7.0系统 必须设置
                .captureStrategy(new CaptureStrategy(true, "com.example.myactivity.view.matisse.fileprovider"))//存储到哪里
                .forResult(REQUEST_CODE_CHOOSE);//请求码

    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, final Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (resultCode == RESULT_OK) {
            if (data != null) {
                if (requestCode == 1) {
                    //处理调用系统图库
                    Uri uri = data.getData();
                    String mFilePath = ContentUriUtil.getPath(this, uri);
//                    Log.e("视频路径","视频路径 mFilePath = "+mFilePath);
                } else if (requestCode == REQUEST_CODE_CHOOSE) {
                    //异步方式插入图片
                    insertImagesSync(data);
                }
            }
        }
    }

    /**
     * 异步方式插入图片
     */
    private void insertImagesSync(final Intent data) {
        insertDialog.show();

        Observable.create(new ObservableOnSubscribe<String>() {
            @Override
            public void subscribe(ObservableEmitter<String> emitter) {
                try {
                    et_new_content.measure(0, 0);
                    List<Uri> mSelected = Matisse.obtainResult(data);
                    // 可以同时插入多张图片
                    for (Uri imageUri : mSelected) {
                        String imagePath = SDCardUtil.getFilePathFromUri(NewActivity.this, imageUri);
                        emitter.onNext(imagePath);
                    }
                    emitter.onComplete();
                } catch (Exception e) {
                    e.printStackTrace();
                    emitter.onError(e);
                }
            }
        })
                //.onBackpressureBuffer()
                .subscribeOn(Schedulers.io())//生产事件在io
                .observeOn(AndroidSchedulers.mainThread())//消费事件在UI线程
                .subscribe(new Observer<String>() {
                    @Override
                    public void onComplete() {
                        if (insertDialog != null && insertDialog.isShowing()) {
                            insertDialog.dismiss();
                        }
                        showToast("插入成功");
                    }

                    @Override
                    public void onError(Throwable e) {
                        if (insertDialog != null && insertDialog.isShowing()) {
                            insertDialog.dismiss();
                        }
                        showToast("插入失败:" + e.getMessage());
                    }

                    @Override
                    public void onSubscribe(Disposable d) {
                        subsInsert = d;
                    }

                    @Override
                    public void onNext(String imagePath) {
                        et_new_content.insertImage(imagePath);
                    }
                });
    }

    @Override
    protected void onResume() {
        super.onResume();
    }

    @Override
    protected void onStop() {
        super.onStop();
        try {
            //如果APP处于后台,或者手机锁屏,则保存数据
            if (CommonUtil.isAppOnBackground(getApplicationContext()) ||
                    CommonUtil.isLockScreeen(getApplicationContext())) {
                saveNoteData(true);//处于后台时保存数据
            }

            if (subsLoading != null && subsLoading.isDisposed()) {
                subsLoading.dispose();
            }
            if (subsInsert != null && subsInsert.isDisposed()) {
                subsInsert.dispose();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 退出处理
     */
    private void dealwithExit() {
        try {
            String noteTitle = et_new_title.getText().toString();
            String noteContent = getEditData();
            String groupName = tv_new_group.getText().toString();
            String noteTime = tv_new_time.getText().toString();
            if (flag == 0) {//新建日记
                if (noteTitle.length() > 0 || noteContent.length() > 0) {
                    saveNoteData(false);
                }
            } else if (flag == 1) {//编辑日记
                if (!noteTitle.equals(myTitle) || !noteContent.equals(myContent)
                        || !groupName.equals(myGroupName) || !noteTime.equals(myNoteTime)) {
                    saveNoteData(false);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        finish();
    }

    @Override
    public void onBackPressed() {
        if (!iwHelper.handleBackPressed()) {
            super.onBackPressed();
        }
        dealwithExit();
    }

}

1. **导入必要的类和包**:
   - 导入了与界面交互、数据操作、工具类和第三方库相关的类和包。

2. **Activity 类定义**:
   - `NoteActivity` 类继承自自定义的 `BaseActivity`,用于展示和处理日记详情。

3. **成员变量定义**:
   - `tv_note_content`:用于显示日记内容的自定义 `RichTextView` 控件。
   - `note`、`myTitle`、`myContent`、`myGroupName`:日记对象和相关信息。
   - `loadingDialog` 和 `mDisposable`:进度对话框和 RxJava 的 Disposable 对象。
   - `iwHelper`:用于显示图片的辅助类。

4. **`onCreate()` 方法**:
   - 设置布局文件并初始化视图。
   - 调用 `initView()` 方法初始化工具栏、浮动操作按钮等控件,并处理返回按钮的点击事件。

5. **`initView()` 方法**:
   - 初始化工具栏(Toolbar),设置标题并添加返回按钮的点击事件监听器。
   - 初始化浮动操作按钮(FloatingActionButton),设置点击事件监听器。
   - 初始化图片查看器辅助类 `iwHelper`,并实例化数据库操作类 `GroupDao`。
   - 显示加载对话框,并根据传入的日记信息设置标题、内容、创建时间和日记分类。

6. **`dealWithContent()` 方法**:
   - 清除并重新显示日记内容。
   - 设置图片点击事件处理逻辑,点击图片时展示图片查看器。

7. **异步显示数据方法 `showDataSync()`**:
   - 使用 RxJava 实现异步显示数据,处理富文本中的图片和文本。
   - 在 IO 线程生产事件,在主线程消费事件,并更新 UI。
   - 处理加载完成、错误和具体内容的显示逻辑。

8. **`showEditData()` 方法**:
   - 实现具体的数据显示逻辑,将富文本内容拆分为图片和文本,并通过 `ObservableEmitter` 发射数据。

9. **菜单方法**:
   - `onCreateOptionsMenu()`:加载并显示选项菜单。
   - `onOptionsItemSelected()`:处理菜单项点击事件,包括编辑日记和分享日记的功能实现。

10. **生命周期方法和返回键处理**:
    - `onResume()` 和 `onStop()` 方法用于管理 RxJava 的 Disposable 对象。
    - `onBackPressed()` 方法中通过图片查看器辅助类 `iwHelper` 处理返回键的逻辑。

<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout 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"
    android:fitsSystemWindows="true">

    <com.google.android.material.appbar.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">

        <androidx.appcompat.widget.Toolbar
            android:id="@+id/toolbar_note"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:popupTheme="@style/AppTheme.PopupOverlay" />

    </com.google.android.material.appbar.AppBarLayout>

    <include layout="@layout/content_note" />

    <com.google.android.material.floatingactionbutton.FloatingActionButton
        android:id="@+id/fab_note"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|end"
        android:layout_margin="@dimen/fab_margin"
        android:visibility="gone"
        app:srcCompat="@android:drawable/ic_dialog_email" />

</androidx.coordinatorlayout.widget.CoordinatorLayout>

Get 项目模板源码

👇👇👇快捷获取方式👇👇👇

  • 29
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
对不起,我无法提供完整的Android Studio代码,因为这涉及到大量的代码编写和详细步骤,而且每个项目都有其独特的需求和设计。不过,我可以为你概述一个基本的图片日记应用的开发流程和关键部分: 1. **设置项目**: - 创建新项目,选择Empty Activity作为起点。 - 添加必要的依赖,如RecyclerView用于展示图片,以及存储图片的库(如Android Storage API)。 2. **UI设计**: - 设计主界面,包括一个RecyclerView用来显示图片,可能还有一个按钮或相机图标用于添加新的图片。 - 使用ImageView显示用户选择的图片。 3. **文件操作**: - 创建一个存储路径,使用`getExternalFilesDir()`获取存储位置。 - 为每个日记条目创建一个单独的目录,例如`"diary_1", "diary_2"`等。 4. **添加图片**: - 当用户点击添加图片时,调用相机API或从相册选择图片,然后保存到指定目录。 5. **数据管理**: - 创建一个实体类(如`DiaryEntry`),包含图片路径、标题或描述等信息。 - 使用ArrayList或Room数据库来存储这些日记条目。 6. **RecyclerView实现**: - 在Adapter中,使用ViewHolder展示每个日记条目的图片和标题/描述。 - 实现onBindViewHolder方法来更新视图。 7. **加载数据**: - 从存储目录读取所有图片文件,生成对应的日记条目,填充到RecyclerView的数据源。 8. **添加编辑和删除功能**: - 提供编辑和删除按钮,根据用户选择更新或移除相应的日记条目。 9. **异常处理**: - 处理文件读写错误,网络请求失败等场景。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

编程乐学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值