目錄
- 一、前言
- 二、開發環境
- 三、效果預覽
- 四、應用介紹
- 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)會是個不錯的選擇,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/198206.html
標籤:其他
