Android记事本

   日期:2020-11-01     浏览:99    评论:0    
核心提示:目录前言开发环境功能介绍主界面新增界面编辑界面搜索界面文件清单代码效果预览后记如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX数学公式新的甘特图功能,丰富你的文章UML 图表FLowchart流程图导出与导入导出导入前言1开发环境你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章,了解一下Markdown

目录

  • 一、前言
  • 二、开发环境
  • 三、效果预览
  • 四、应用介绍
    • 4.1 主界面
    • 4.2 新增界面
    • 4.3 编辑界面
    • 4.4 搜索界面
    • 4.5 数据库
    • 4.6 黑夜模式
  • 五、文件列表
  • 六、可能出现的问题
  • 七、关键功能
    • 7.1 RecyclerView的使用
    • 7.2 Material库控件的使用
    • 7.3 FloatingActionButton的使用
    • 7.4 滑动菜单
    • 7.5 TimePickerDialog的使用
    • 7.6 通知的使用
    • 7.7 服务的使用
    • 7.8 数据库的使用
  • 八、后记

一、前言

上学期做了个简单的Android的记事本APP(功能基本就是书里的杂糅),也是我第一次接触Android,一时兴起,趁还没忘光先记录下来,一直以来都在看别人的自己没有分享过东西,如果能提供到一点点的帮助也是极好的。

教材选的是郭霖老师的《第一行代码》,买了第二版回来以后,没想到没过几周,在逛csdn的时候就看到第三版也出了,果断也买了。第三版里根据Android版本的更新更新了很多控件的使用,但是全书使用的是Kotlin语言,我的做法是将第二、三版结合起来一起看,仅供参考。

二、开发环境

Android Studio3.4
JDK13.0.2

版本:
Android Q(10.0)
API 29
Gradle 6.2.2

在项目中的一些控件的引用与方法的使用都是基于上述版本的,可能会出现和其他版本中不一致的情况。

三、效果预览

四、应用介绍

功能包括笔记的增删查改和定时提醒之类,总体上将最主要的功能放在最显眼的地方展示出来,而次要的功能放在菜单里。应用总共有四个界面,每个界面都是一个Activity。

4.1 主界面

进入应用看到的界面,应用的所有界面都隐藏了原先自带的标题栏ActionBar,改为使用自定义的Toolbar。向下滑动以后顶部的图片会折叠起来,向上滑动会重新显示。具体是使用CoordinatorLayout + AppBarLayout + CollapsingToolbarLayout + ImageView

用RecyclerView显示笔记,新建功能单独放在下方的FloatingActionButton里,其他的功能放在标题栏的菜单中。

活动模式在AndroidManifest里选用的是singleTop,这样从其他界面回到主界面的时候就可以保证数据更新。

笔记显示方式可以选用默认的垂直式(LinearLayoutManager)或者网格式(StaggeredGridLayoutManager)。虽然上面写着瀑布布局但不是真的瀑布式

CoordinatorLayout、AppBarLayout、CollapsingToolbarLayout

1、CoordinatorLayout可以监听所有子控件的所有事件,可以响应滚动事件,加强版的FrameLayout;
2、AppBarLayout解决RecyclerView和Toolbar之间互相覆盖的问题,垂直方向的LinearLayout;
3、CollapsingToolbarLayout作用于Toolbar之上,可以实现折叠图片的效果;
4、CollapsingToolbarLayout只能作为AppBarLayout的直接子布局,AppBarLayout只能作为CoordinatorLayout的子布局。
我对三者的嵌套关系认识是 :
< CoordinatorLayout>
 < AppbarLayout>
  < CollapsingToolbarLayout>
  < /CollapsingToolbarLayout>
 < /AppbarLayout>
< /CoordinatorLayout>

RecyclerView

滚动显示内容的控件,可以自定义子项的布局。使用的时候需要为RecyclerView准备一个适配器以将数据传递给控件,通过泛型来指定要适配的数据类型,然后数据传入。

FloatingActionButton

悬浮按钮,可以自行调整位置和悬浮的高度,需要Material库

其他

1.折叠图片
在res - values - styles.xml中,将AppTheme改成

name="AppTheme" parent="Theme.MaterialComponents.DayNight.NoActionBar.Bridge"

在布局文件中嵌套使用CoordinatorLayout、AppBarLayout、CollapsingLayout、ImageView

在build.gradle中添加Glide库的依赖,Glide用来加载图片

implementation 'com.github.bumptech.glide:glide:3.7.0

加载图片

Glide.with(this).load(R.mipmap.图片名称).into(ImageView的id);

2.实现标题栏和状态栏融为一体的效果
在CoordinatorLayout、AppBarLayout、CollapsingLayout都加上属性

android:fitsSystemWindow="true"

styles.xml(res目录New - Directory新建values-v21目录,在values-v21中New - Values resource file)

<resources>
	<style name="MainActivityTheme" parent="AppTheme"> <item name="android:statusBarColor">@android:color/transparent</item> </style>
</resources>

在res - values - styles.xml中加上

<style name="MainActivityTheme" parent="AppTheme">

在AndroidManifest.xml中的activity标签里加上

android:theme="@style/MainActivityTheme"

activity_main.xml

<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" android:background="#FFFBF0">

    <com.google.android.material.appbar.AppBarLayout android:id="@+id/appBar" android:layout_width="match_parent" android:layout_height="250dp" android:fitsSystemWindows="true">
        <com.google.android.material.appbar.CollapsingToolbarLayout android:id="@+id/collapsingToolbar" android:layout_width="match_parent" android:layout_height="match_parent" android:theme="@style/ThemeOverlay.AppCompat.DayNight.ActionBar" android:fitsSystemWindows="true" app:contentScrim="@color/colorPrimary" app:layout_scrollFlags="scroll|exitUntilCollapsed">
            <ImageView android:id="@+id/toolbarImageView" android:layout_width="match_parent" android:layout_height="match_parent" android:scaleType="centerCrop" android:fitsSystemWindows="true" app:layout_collapseMode="parallax" android:adjustViewBounds="true" android:contentDescription="This is picture of title" tools:ignore="HardcodedText"/>
            <androidx.appcompat.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" app:layout_collapseMode="pin" />
        </com.google.android.material.appbar.CollapsingToolbarLayout>
    </com.google.android.material.appbar.AppBarLayout>

    <androidx.recyclerview.widget.RecyclerView android:id="@+id/recyclerView" android:layout_width="match_parent" android:layout_height="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior"/>

    <com.google.android.material.floatingactionbutton.FloatingActionButton android:id="@+id/fab" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="16dp" android:layout_gravity="bottom|end" android:src="@drawable/pencil_48px" app:elevation="8dp"/>

</androidx.coordinatorlayout.widget.CoordinatorLayout>

4.2 新增界面

功能简单,就是创建新的笔记,整个界面只有一个Button和一个EditText。

圆角的卡片效果是使用CardView实现的。点击保存按钮以后,会生成一个随机数,并将笔记的内容、当前时间和这个随机数一起保存进数据库中。
(为什么要生成随机数:详见4.5)

NestedScrollView

可以滚动查看屏幕以外的数据,可以嵌套响应滚动事件,用在CoordinatorLayout内部,因为后者本身可以响应滚动事件。

CardView

实现卡片式布局效果的控件。

其他

1.使用toolbar导入包的时候选

import androidx.appcompat.widget.Toolbar;

2.加上这段代码可以防止NestedScrollView和EditText嵌套使用时出现滑动冲突:

editText.setOnTouchListener(new View.OnTouchListener(){ 
            @Override
            public boolean onTouch(View view, MotionEvent motionEvent){ 
                if(motionEvent.getAction()==MotionEvent.ACTION_DOWN){ 
                    view.getParent().requestDisallowInterceptTouchEvent(true);
                }
                if(motionEvent.getAction()==MotionEvent.ACTION_MOVE){ 
                    view.getParent().requestDisallowInterceptTouchEvent(true);
                }
                if(motionEvent.getAction()==MotionEvent.ACTION_UP){ 
                    view.getParent().requestDisallowInterceptTouchEvent(false);
                }
                return false;
            }
        });

3.获取当前时间:

private String getTime(){ 
        @SuppressLint("SimpleDateFormat") SimpleDateFormat date = new SimpleDateFormat("yyyy-MM-dd");
        Date curDate = new Date(System.currentTimeMillis());
        return date.format(curDate);// 不需要赋值给中间变量再返回
    }

4.生成随机数:

@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    private static int getRandom(){ // 获取随机数
        return ThreadLocalRandom.current().nextInt();
    }

activity_add.xml

<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">

    <com.google.android.material.appbar.AppBarLayout android:id="@+id/appBar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize">
        <androidx.appcompat.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="#FFDAB9" android:theme="@style/ThemeOverlay.AppCompat.DayNight.ActionBar" tools:ignore="MissingConstraints"/>
    </com.google.android.material.appbar.AppBarLayout>

    <androidx.core.widget.NestedScrollView android:layout_width="match_parent" android:layout_height="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior">

        <LinearLayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent">

            <com.google.android.material.card.MaterialCardView android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginTop="15dp" android:layout_marginBottom="20dp" android:layout_marginLeft="15dp" android:layout_marginStart="15dp" android:layout_marginRight="15dp" android:layout_marginEnd="15dp" app:cardBackgroundColor="#FFFBF0" app:cardCornerRadius="4dp" app:layout_constraintTop_toBottomOf="@+id/toolbar">
                <EditText android:id="@+id/editText" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="top" android:layout_margin="10dp" android:inputType="textMultiLine" android:lines="27" android:importantForAutofill="no" android:background="@null" android:scrollbars="vertical" android:hint="@string/add_hint"/>
            </com.google.android.material.card.MaterialCardView>
        </LinearLayout>
    </androidx.core.widget.NestedScrollView>

</androidx.coordinatorlayout.widget.CoordinatorLayout>

4.3 编辑界面

风格和新增界面保持一致,除了保存功能,其他的小功能都放入了左上角的滑动菜单中(NavigationView)。小功能包括快速新建笔记、获取灵感、定时提醒和删除笔记。

  • 快速新建笔记:
    免除回到主界面再新建的繁琐,直接在编辑界面进入新增界面。

  • 获取灵感:
    凑数的功能,主要是菜单里只有三个功能不好看加上去的。实际是弹出一个dialog,里面随机显示一条金句,金句是事先放在一个数组里的。

  • 定时提醒:
    系统在设定的时间发送一条通知。具体实现是在弹出的TimerPickerDialog里用AlarmManager设置提醒时间,将设好的时间传递给Service,到了设定的时间以后Service会启动Notification进行通知。

  • 删除笔记:
    将当前的笔记从数据库删除。

NavigationView
实现滑动菜单的控件,分为上半部分的headerLayout和下半部分menu。

TimePickerDialog
时间对话框。

AlarmManager
Android中用来实现定时任务的其中一种方式,另一种是使用Timer类。

Service
服务,四大组件之一,一般负责一些后台功能,不依赖任何用户界面,适合一些不需要和用户交互的任务。

Notification
通知,应用会在上方的状态栏显示一个通知的图标,可以设置通知图标、是否有响声。

activity_edit.xml

<androidx.drawerlayout.widget.DrawerLayout 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:id="@+id/drawerLayout" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".EditActivity">

    <androidx.coordinatorlayout.widget.CoordinatorLayout android:layout_width="match_parent" android:layout_height="match_parent">

        <com.google.android.material.appbar.AppBarLayout android:id="@+id/appBar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize">
            <androidx.appcompat.widget.Toolbar android:id="@+id/toolBar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="#FFDAB9" style="@style/ThemeOverlay.AppCompat.DayNight.ActionBar" tools:ignore="MissingConstraints"/>
        </com.google.android.material.appbar.AppBarLayout>

        <androidx.core.widget.NestedScrollView android:layout_width="match_parent" android:layout_height="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior">

            <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent">
                <com.google.android.material.card.MaterialCardView android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginTop="15dp" android:layout_marginStart="15dp" android:layout_marginLeft="15dp" android:layout_marginEnd="15dp" android:layout_marginRight="15dp" android:layout_marginBottom="20dp" app:cardBackgroundColor="#FFFBF0" app:cardCornerRadius="4dp" app:layout_constraintTop_toBottomOf="@id/toolBar">
                    <EditText android:id="@+id/editText" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="top" android:layout_margin="10dp" android:inputType="textMultiLine" android:lines="27" android:importantForAutofill="no" android:background="@null" android:scrollbars="vertical" android:hint="@null" android:textColor="#232323"/>
                </com.google.android.material.card.MaterialCardView>
            </LinearLayout>
        </androidx.core.widget.NestedScrollView>
    </androidx.coordinatorlayout.widget.CoordinatorLayout>

    <com.google.android.material.navigation.NavigationView android:id="@+id/navView" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="start" app:menu="@menu/nav_menu" app:headerLayout="@layout/nav_header"/>
</androidx.drawerlayout.widget.DrawerLayout>

4.4 搜索界面

点击搜索按钮以后进入的界面,根据关键字搜索已创建的笔记,点击搜索显示符合条件的笔记(RecyclerView),点击取消回到主界面。界面包含一个EditText、两个Button和一个RecyclerView。文本框的提示信息在布局文件里用hint属性来实现。

activity_search.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical">

    <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:background="#989795">
        <EditText android:id="@+id/editText" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:inputType="text" android:importantForAutofill="no" android:hint="@string/add_hint"/>
        <Button android:id="@+id/buttonSearch" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/search"/>
        <Button android:id="@+id/buttonBack" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/cancel"/>
    </LinearLayout>

    <androidx.recyclerview.widget.RecyclerView android:id="@+id/recyclerView" android:layout_width="match_parent" android:layout_height="match_parent"/>

</LinearLayout>

4.5 数据库

选择了Android自带的Sqlite数据库,使用了LitePal来进行数据库操作。

字段 类型 含义
id int 自带的
text String 笔记内容
time String 笔记创建时间
tag int 笔记标识

说明:
id字段好像是使用LitePal以后会自动生成的,我不会用
tag字段用来代替id来唯一标识每一篇笔记,具体是在新建笔记的时候生成一个随机数给它。使用一个准确的时间(比如精确到秒)来作标识可能会更好,可以防止翻车。

打开在右下角边栏的Device File Explorer,在data - data - 包名下可以找到生成的数据库文件(.db)。

LitePal
开源的Android数据库框架,将一些数据库常用的功能进行了封装。

4.6 黑夜模式

Android 10以后才能使用的功能。
colors.xml(res目录New - Directory新建values-night,在values-night里New - Values resource file)

<resources>
	<color name="colorPrimary">#303030</color>
	<color name="colorPrimaryDark">#232323</color>
	<color name="colorAccent">#008577</color>
</resources>

剩下的我自己也没整明白,就不提了。

五、文件列表

主界面
MainActivity.java:活动
activity_main.xml:主界面布局
toolbar.xml:菜单

新建界面
AddActivity.java:活动
activity_add.xml:新建界面布局
toolbar_add.xml:标题栏菜单

编辑界面
EditActivity.java:活动
NoticeService.java:服务
activity_edit.xml:编辑界面布局
toolbar_add.xml:标题栏菜单
nav_header.xml:滑动菜单布局
nav_menu.xml:滑动菜单

搜索界面
SearchActivity.java:活动
activity_search.xml:搜索界面布局

其他
(RecyclerView用的)
Note.java:自定义的泛型
NoteAdapter.java:自定义的适配器
note_item.xml:子项的布局
(数据库用的)
NoteBook.java:数据库表
litepal.xml:Litepal配置

六、可能出现的问题

报错

Casued by:java.lang.reflect.InvocationTargetException

Casued by:java.lang.IllegalArgumentException:The style on this compoent requires your app theme to Theme

说明:
需要更新控件的主题到Theme.MaterialComponent

解决
在styles.xml中,将

<style name="AppTheme"parent="Theme.AppCompat.Light.NoActionBar">

改成

<style name="AppTheme"parent="Theme.MaterialComponents.Light.NoActionBar.Bridge">

报错

java.lang.IllegalArgumentException:Unsupported class file major version 57

说明
version 57对应的JDK版本是13,如果是version 56对应的是12

解决
安装更低版本的JDK
(这个问题我忘了是什么时候遇到了,最后我没有降低版本,可能是改用了别的控件,或者把其他什么地方的版本也升级了)

报错

Casued by:org.codehaus.groovy.control.MultipleCompilationErrorsException:startup failed:

说明
原因不清楚

解决
升级Gradle到6.2.2版本可以解决,存在的缺陷是每次打开项目都要手动升级一次

报错

You need to use a Theme.Appcompat.theme:

说明

解决
让活动从继承ActionBarActivity改成继承Activity

七、关键功能

7.1 RecyclerView的使用

在build.gradle中添加依赖(根据版本不同选择其中一个,上面是旧的,下面是新的)

implementation 'com.android.support:recyclerview-v7:29.1.0'
implementation 'androidx.recyclerview:recyclerview:1.1.0'

(忘了用不用这步了)

在File - project structure - dependencies - app - library dependency中搜索recyclerview,选择“androidx.”的最新版本

在布局文件中引用(根据版本不同选择,上面是旧的,下面是新的)

<android.support.v7.widget.RecyclerView android:id="" android:layout_width="" android:layout_height=""/>
<androidx.recyclerview.widget.RecyclerView android:id="" android:layout_width="" android:layout_height=""/>

Note.java(泛型)

public class Note(){ 
	private String noteContent;// 笔记内容
	private String noteTime;// 笔记创建时间
	private int noteTag;// 笔记标识

	public Note(String noteContent, String noteTime, int noteTag){ 
		this.noteContent = noteContent;
		this.noteTime = noteTime;
		this.noteTag = noteTag;
	}

	public String getNoteContent(){  return noteContent; }
	public String getNoteTime(){  return noteTime; }
	public int getNoteTag(){  return noteTag; }
}

note_item.xml(子项的布局)

<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="5dp" app:cardCornerRadius="4dp">

    <LinearLayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="wrap_content">
        <TextView android:id="@+id/noteContent" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginStart="10dp" android:layout_marginLeft="10dp" android:lines="2" android:background="#FFFEF0" android:textSize="24sp" android:textColor="#232323"/>
        <TextView android:id="@+id/noteTime" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginStart="10dp" android:layout_marginLeft="10dp" android:background="#FFDAB9" android:textColor="#008577"/>
    </LinearLayout>
</com.google.android.material.card.MaterialCardView>

NoteAdapter.java(适配器)
让适配器继承自RecyclerView.Adapter,将泛型指定为NoteAdapter.ViewHolder,内部类ViewHolder用于对控件的实例进行缓存。

import android.content.Intent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;

public class NoteAdapter extends RecyclerView.Adapter<NoteAdapter.ViewHolder>{ 
	private List<Note> mNoteList;

	static class ViewHolder extends RecyclerView.ViewHolder{ // 内部类ViewHolder
		View noteView;
		TextView noteContent;
		TextView noteTime;

		public ViewHolder(View view){ // 传入子项的最外层布局
			super(view);
			noteView = view;
			noteContent = (TextView) view.findViewById(R.id.noteContent);// 获取实例
			noteTime = (TextView) view.findViewById(R.id.noteTime);// 获取实例
		}
	}

	public NoteAdapter(List<Note> noteList){ // 传入需要展示的数据
		mNoteList = noteList;
	}

	@Override
	public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType){ // 创建ViewHolder实例
		View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.note_item, parent, false);// 加载布局
		final ViewHolder holder = new ViewHolder(view);
		
		holder.noteView.setOnClickListener(new View.OnClickListener()){ // 子项点击事件
			@Override
			public void onClick(View v){ 
				int position = holder.getAdapterPosition();
				Note note = mNoteList.get(position);

				Intent intent = new Intent(v.getContext(), EditActivity.class);
				String contentData = note.getNoteContent();
				int tagData = note.getNoteTag();
				// intent.putExtra("键",数据)
				intent.putExtra("content_data", contentData);
				intent.putExtra("tag_data", String.valueOf(tagData));
				v.getContext.startActivity(intent);
			}
		});
		holder.noteTime.setOnClickListener(new View.OnClickListener(){ // 子项点击事件
			@Override
			public void onClick(View v){ 
				int position = holder.getAdapterPosition();
				Note note = mNoteList.get(position);

				String time = note.getNoteTime();
				Toast.makeText(v.getContext(), "笔记创建于" + time, Toast.LENGTH_LONG).show();
			}
		});
		return holder;
	}

	@Override
	public void onBindViewHolder(ViewHolder holder, int position){ // 为子项赋值,子项被滚动到屏幕就执行
		Note note = mNoteList.get(position);
		holder.noteContent.setText(note.getNoteContent());
		holder.noteTime.setText(note.getNoteTime());
	}

	@Override
	public int getItemCount(){ // 告诉RecyclerView一共有多少子项
		return mNoteList.size();
	}
}

更新:
如果获取点击位置的接口被划线不推荐使用了,可能是如下原因(翻译摘自郭霖老师的文章)

这个方法当多个adapter嵌套时会存在歧义。如果你是在一个adapter的上下文中调用这个方法,你可能想要调用的是getBindingAdapterPosition()方法。如果你想获得的position是如同在RecyclerView中看到的那样,你应该调用getAbsoluteAdapterPosition()方法。

这是我这几天写这篇文章的时候看到的,具体文章→ 什么?RecyclerView中获取点击位置的接口被废弃了?

实例化

public class MainActivity extends AppCompatActivity{ 
	
	private List<Note> noteList = new ArrayList<>();// 
	private RecyclerView recyclerView;

	@Override
	protected void onCreate(bundle savedInstanceState){ 
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		recyclerView = (RecyclerView) findViewById(R.id.recycler_view);// 获取RecyclerView实例
		LinearLayoutManager layoutManager = new LinearLayoutManager(this);
		recyclerView.setLayoutManager(layoutManager);// 设置垂直式排列
		NoteAdapter adapter = new NoteAdapter(noteList);// 创建NoteAdapter实例
		recyclerView.setAdapter(adapter);// 完成适配器设置 
	}
}

从数据库获取数据

List<NoteBook> notes = DataSupport.findAll(NoteBook.class);

for(NoteBook note : notes){ 
	Note temp = new Note(note.getContent(), note.getTime(), note.getTag());
	noteList.add(temp);
}

7.2 Material库控件的使用

build.gradle中添加依赖

implementation 'com.google.android.material:material:1.1.0'

7.3 FloatingActionButton的使用

布局文件中引用
app:elevation属性指定的是高度值

<com.google.android.material.floatingactionbutton.FloatingActionButton android:id="@+id/fab" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="16dp" android:layout_gravity="bottom|end" android:src="@drawable/图片名称" app:elevation="8dp"/>

设置点击事件

FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);

fab.setOnClickListener(new View.OnClickListener()){ 
	@Override
	public void onClick(View v){ 
		Intent intent = new Intent(MainActivity.this, AddActivity.class);
		startActivity(intent);
	}
});

7.4 滑动菜单

android.support:desgin库已弃用,如需使用在build.gradle中添加

compile 'com.android.support:design:24.2.1'

nav_header.xml(Layout文件夹New - Layout resource file)

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="180dp">

    <ImageView android:layout_width="match_parent" android:layout_height="match_parent" android:contentDescription="@string/headerText" android:scaleType="centerCrop" android:src="@mipmap/图片名称"/>
</RelativeLayout>

nav_menu.xml(menu文件夹New - Menu resource file)

<menu xmlns:android="http://schemas.android.com/apk/res/android">

    <group android:checkableBehavior="single">
        <item android:id="@+id/nav_add" android:icon="@drawable/图片名称" android:title="@string/add"/>
        <item android:id="@+id/nav_notice" android:icon="@drawable/图片名称" android:title="@string/notice"/>
        <item android:id="@+id/nav_refresh" android:icon="@drawable/图片名称" android:title="@string/refresh"/>
        <item android:id="@+id/nav_delete" android:icon="@drawable/图片名称" android:title="@string/delete"/>
    </group>
</menu>

布局文件中引用

<com.google.android.material.navigation.NavigationView
	android:id="@id/navView"
	android:layout_width="match_parent"
	android:layout_height="match_parent"
	android:layout_gravity="start"
	app:menu="@menu/nav_menu"
	app:headerLayout+"@layout/nav_header"/>

设置点击事件

NavigationView navView = (NavigationView) findViewById(R.id.navView);
navView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener(){ 
	@Override
	public boolean onNavigationItemSelected(@NonNull MenuItem item){ 
		switch(item.getItemId()){ 
			case R.id.nav_add:
				...
			case R.id.nav_notice:
				...
			case R.id.nav_refresh:
				...
			case R.id.nav_delete:
				...
			default:
		}
		return true;
	}
});

7.5 TimePickerDialog的使用

RTC_WAKEUP表示让定时任务的触发时间从1970.1.1的0点开始算起,会唤醒CPU。AlertDialog.THEME_HOLO_LIGHT让TimePickerDialog显示滚动的效果

alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);

Calendar calendar1 = Calendar.getInstance();// 获取实例
int hour = calendar1.get(Calendar.HOUR_OF_DAY);// 获取当前小时
int minute = calendar1.get(Calendar.MINUTE);// 获取当前分钟

// 设置时间对话框
TimePickerDialog timePickerDialog = new TimePickerDialog(EditActivity.this, android.app.AlertDialog.THEME_HOLO_LIGHT, new TimePickerDialog.OnTimeSetListener() { 
	@RequiresApi(api = Build.VERSION_CODES.KITKAT)
    @Override
    public void onTimeSet(TimePicker view, int hourOfDay, int minute) { 
    	Calendar calendar2 = Calendar.getInstance();
        calendar2.set(Calendar.HOUR_OF_DAY, hourOfDay);// 获取设定小时
        calendar2.set(Calendar.MINUTE, minute);// 获取设定分钟

        Intent intent2 = new Intent(EditActivity.this, NoticeService.class);
        PendingIntent pi = PendingIntent.getService(EditActivity.this, 0, intent2, 0);
        // (工作类型, 触发时间, 意图)
        alarmManager.setExact(AlarmManager.RTC_WAKEUP, calendar2.getTimeInMillis(), pi);
    }
}, hour, minute, true);// 初始显示时间
timePickerDialog.show();

7.6 通知的使用

这个通知是写在服务里的。PendingIntent可以理解为延迟执行的Intent。

	@RequiresApi(api = Build.VERSION_CODES.O)// NotificationChannel&IMPORTANCE_HIGH的版本要求
    private void initNotice(){ 
        // getSystemService:确定获取系统的哪个服务
        NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        // API 26(Android8.0)以后需要自己设置NotificationChannel
        String channelId = "channel_01";
        String channelName = "channelName";
        String channelDescription = "this is default channel";
        NotificationChannel channel = new NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_HIGH);
        channel.setDescription(channelDescription);
        assert notificationManager != null;
        notificationManager.createNotificationChannel(channel);

        Intent intentOut = new Intent(NoticeService.this, MainActivity.class);
        // (Context, 0, Intent对象, PendingIntent的行为)
        PendingIntent pi = PendingIntent.getActivity(NoticeService.this, 0, intentOut, 0);
        // API 26(Android8.0)以后需要在Builder中添加id
        Notification notification = new NotificationCompat.Builder(this, channelId)
                .setContentTitle("记事本")
                .setContentText("定时提醒")
                .setWhen(System.currentTimeMillis())
                .setSmallIcon(R.drawable.图片名称)
                .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.图片名称))
                .setContentIntent(pi)
                .setAutoCancel(true)// 让通知可以点击以后自动取消
                .build();
        notificationManager.notify(1, notification);
    }

这个通知最终实现的效果是,到了设定的时间后,会在系统的顶部弹出一个横幅通知,进行点击可以消除通知并进入记事本应用的主界面。

7.7 服务的使用

NoticeService.java(New - Service - Service)

import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.graphics.BitmapFactory;
import android.os.Build;
import android.os.IBinder;
import androidx.annotation.RequiresApi;
import androidx.core.app.NotificationCompat;

public class NoticeService extends Service { 
    public NoticeService() { 
    }

    @Override
    public IBinder onBind(Intent intent) { 
        // TODO: Return the communication channel to the service.
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId){ 
        new Thread(new Runnable() { // 创建和启动子线程
            @RequiresApi(api = Build.VERSION_CODES.O)
            @Override
            public void run() { // 处理具体逻辑
                initNotice();// 实现通知的函数

                stopSelf();// 执行完毕后自动停止
            }
        }).start();
        return super.onStartCommand(intent, flags, startId);
    }

	private void initNotice(){ // 见上文
		...
	}
}

7.8 数据库的使用

build.gradle中添加依赖(我写文章的时候看到的最新版本好像是3.2.0)

implementation 'org.litepal.android:core:1.3.2'

在AndroidManifest.xml中

<application android:name="org.litepal.LitePalApplication" ...>
</application>

NoteBook.java(数据库表)

import org.litepal.crud.DataSupport;

public class NoteBook extends DataSupport(){ 
	private int id;
	private String content;
	private String time;
	private int tag;

	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 String getTime(){  return time; }
	public void setTime(String time){  this.time = time; }

	public int getTag(){  return tag; }
	public void setTag(int tag){  this.tag = tag; }
}

litepal.xml(在包名 - app - src下创建assets文件夹,在assets文件夹下创建litepal.xml)

dbname是数据库的名字。version的值一开始是1,每次更新了数据库表(改了字段、新增了表等等)这个值就加1。list标签里是数据库的表,有几个表就写几个< mapping >< /mapping >。

<?xml version="1.0" encoding="utf-8"?>
<litepal>
	<dbname value="NoteBook"></dbname>
	<version value="1"></version>

	<list>
		<mapping class="com.example.notebook.NoteBook"></mapping>
	</list>
</litepal>

连接数据库

SQLiteDataBase db = Connector.getDataBase();

NoteBook note = new NoteBook();
String inputContent = editText.getText().toString();// 笔记的内容
String inputTime = getTime();// 笔记的创建时间
int inputTag = getRandom();// 笔记的标识

note.setContent(inputContent);
note.setTime(inputTime);
note.setTag(inputTag);

DataSupport.deleteAll(NoteBook.class, "tag = ?", tag);// 根据标识删除对应的笔记

String orderContent = editText.getText().toString();// 从文本框获取想要查找的关键字

List<NoteBook> notes = DataSupport.where("content like ?", "%"+orderContent+"%").find(NoteBook.class);// 根据关键字在“内容”里查找

NoteBook note = new NoteBook();
String inputContent = editText.getText().toString();// 从文本框获取更改后的笔记内容

note.setContent(inputContent);
note.updateAll("tag = ?", tag);

八、后记

文章写得比较匆忙,可能会出现很多纰漏和错误,欢迎交流与学习,可能答不上来问题。后续如果想起了遗忘的东西可能会编辑。

程序是一边学一边写的,想到什么就加了什么进去,应该还有很多不合理的地方或者可以优化的地方。

Android的东西更新得比较快,经常过一两年又不一样了,而这个变化的东西恰好没有人发文章提到的时候,个人经验,这个时候去Android Developers查看一下参考文档(REFERENCE)会是个不错的选择。

 
打赏
 本文转载自:网络 
所有权利归属于原作者,如文章来源标示错误或侵犯了您的权利请联系微信13520258486
更多>最近资讯中心
更多>最新资讯中心
0相关评论

推荐图文
推荐资讯中心
点击排行
最新信息
新手指南
采购商服务
供应商服务
交易安全
关注我们
手机网站:
新浪微博:
微信关注:

13520258486

周一至周五 9:00-18:00
(其他时间联系在线客服)

24小时在线客服