主頁 > 後端開發 > Android記事本

Android記事本

2020-11-02 04:46:15 後端開發

目錄

  • 一、前言
  • 二、開發環境
  • 三、效果預覽
  • 四、應用介紹
    • 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來進行資料庫操作,

欄位型別含義
idint自帶的
textString筆記內容
timeString筆記創建時間
tagint筆記標識

說明:
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)會是個不錯的選擇,

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/199000.html

標籤:java

上一篇:專案實戰:Qt+Android模擬操作器(模擬操作app,打開,點擊,輸入,獲取驗證碼等等)

下一篇:【小技巧】怎樣保存ins里的圖片?一鍵下載,簡單方便

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more