├── .gitignore ├── .idea ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── encodings.xml ├── gradle.xml ├── misc.xml ├── modules.xml ├── runConfigurations.xml └── vcs.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── bob │ │ └── com │ │ └── note │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── bob │ │ │ └── com │ │ │ └── note │ │ │ ├── NoteActivity.java │ │ │ ├── NoteContentActivity.java │ │ │ ├── adapter │ │ │ ├── NoteAdapter.java │ │ │ └── NoteDbAdapter.java │ │ │ ├── bean │ │ │ └── Note.java │ │ │ ├── listener │ │ │ └── HidingScrollListener.java │ │ │ └── util │ │ │ ├── CursorFilter.java │ │ │ ├── DateUtil.java │ │ │ ├── RecyclerViewCursorAdapter.java │ │ │ └── SharedPreferencesUtil.java │ └── res │ │ ├── drawable-hdpi │ │ └── ic_action_new.png │ │ ├── drawable-mdpi │ │ └── ic_action_new.png │ │ ├── drawable-xhdpi │ │ └── ic_action_new.png │ │ ├── drawable-xxhdpi │ │ └── ic_action_new.png │ │ ├── layout │ │ ├── activity_main.xml │ │ ├── note_content.xml │ │ └── note_row.xml │ │ ├── menu │ │ └── menu_main.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ ├── values-v21 │ │ └── dimens.xml │ │ ├── values-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── bob │ └── com │ └── note │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── screenshot ├── 1.gif └── 2.png └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 19 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | Android > Lint > Correctness 39 | 40 | 41 | Class structureJava 42 | 43 | 44 | Code maturity issuesJava 45 | 46 | 47 | Java 48 | 49 | 50 | Java language level migration aidsJava 51 | 52 | 53 | Javadoc issuesJava 54 | 55 | 56 | Performance issuesJava 57 | 58 | 59 | Probable bugsJava 60 | 61 | 62 | RELAX NG 63 | 64 | 65 | TestNGJava 66 | 67 | 68 | Threading issuesJava 69 | 70 | 71 | 72 | 73 | Android 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 95 | 96 | 97 | 98 | 99 | 100 | 105 | 106 | 107 | 108 | 109 | 110 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Note(小便签) 2 | 3 | **欢迎访问我的[个人博客](http://www.wensibo.top)** 4 | 5 | 一直想要写一个便签应用,因为我一直在用的是锤子便签和一加便签,觉得体验还是可以的,但是始终觉得自己也是可以做的,这段时间因为有些事情耽误了,项目前几天做好了,一直没有时间上传到[github](https://github.com/Wensibob/Note) (各位大爷路过给个star呗😉) ,今天趁着有时间顺便写了这篇文章,介绍一下写这个便签时遇到的一些问题,当作是跟大家一起分享吧! 6 | 7 | 8 | ## 功能 9 | * 实现最基本的增加、删除、修改便签 10 | * 便签能够保存到本地 11 | * 主界面采用Material Design设计风格,相对美观(勿喷) 12 | * Recycle View上下滑动可以自动隐藏Toolbar,以及Floating Action Button 13 | * Recycle View的Item可以实现如QQ的侧滑效果,可以通过点击删除、置顶进行编辑 14 | * 可以设置便签为星✨,那么将会在便签界面左边增加一个红色的标志,以提醒用户此便签为重要便签 15 | * 变迁主界面标有时间,并且按照编辑时间从新到旧进行排列 16 | 17 | ## 效果 18 | ![效果图](/screenshot/1.gif) 19 | 20 | **Talk is cheap,show me your code** 21 | 22 | ## 如何在RecycleView中使用本地Sqlite数据库数据 23 | RecycleView是google在推出Material Design时着重介绍的一个组件,它对传统的ListView已经可以说是完全代替了,功能强大是他的一个最大优点,但是有一点局限的就是我们自定义的RecycleView必须继承重写 RecyclerView.Adapter 和 RecyclerView.ViewHolder,虽然在里面我们可以随意重写方法,但是可以发现如果我们使用数据库作为数据源,RecyclerView.Adapter是无法支持读取Cursor的,但是开源的力量又再次显现了,直接给上[github地址](https://github.com/flzyup/ExRecyclerViewLibrary),但是我们这里只需要复用其中的两个文件就行了,容我娓娓道来。 24 | 25 | **1、添加下面的RecyclerViewCursorAdapter 和 CursorFilter到工程中** 26 | 由于代码太长影响排版,我就直接附上下载链接 27 | [RecyclerViewCursorAdapter](http://www.wensibo.top/file/img/2017-3-8Note/RecyclerViewCursorAdapter.java) 28 | [CursorFilter](http://www.wensibo.top/file/img/2017-3-8Note/CursorFilter.java) 29 | 30 | **2、新建自定义的Adapter并且继承RecyclerViewCursorAdapter** 31 | * NoteAdapter 32 | 33 | ```java 34 | public class NoteAdapter extends RecyclerViewCursorAdapter { 35 | 36 | private Context mContext; 37 | private RecyclerViewOnItemClickListener mOnItemClickListener; 38 | private onSwipeListener mOnSwipeListener; 39 | 40 | 41 | public NoteAdapter(Context context,Cursor cursor,int flags) { 42 | super(context,cursor,flags); 43 | this.mContext = context; 44 | } 45 | 46 | @Override 47 | public MyNoteViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 48 | View root = LayoutInflater.from(parent.getContext()).inflate(R.layout.note_row, parent, false); 49 | MyNoteViewHolder holder = new MyNoteViewHolder(root); 50 | return holder; 51 | } 52 | 53 | @Override 54 | public void onBindViewHolder(final MyNoteViewHolder holder, Cursor cursor) { 55 | int position = cursor.getPosition(); 56 | holder.tv.setText(cursor.getString(cursor.getColumnIndex(NoteDbAdapter.COL_CONTENT))); 57 | holder.tv_dateTime.setText(cursor.getString(cursor.getColumnIndex(NoteDbAdapter.COL_DATETIME))); 58 | holder.mRowtab.setBackgroundColor(cursor.getInt(cursor.getColumnIndex(NoteDbAdapter.COL_IMPORTANT)) == 1? 59 | mContext.getResources().getColor(R.color.colorAccent):mContext.getResources().getColor(android.R.color.white) 60 | ); 61 | holder.root.setTag(position); 62 | 63 | holder.tv.setOnClickListener(new View.OnClickListener() { 64 | @Override 65 | public void onClick(View view) { 66 | if (mOnItemClickListener != null) { 67 | mOnItemClickListener.onItemClickListener(view, holder.getAdapterPosition()); 68 | } 69 | } 70 | }); 71 | 72 | 73 | } 74 | 75 | @Override 76 | protected void onContentChanged() { 77 | 78 | } 79 | 80 | /** 设置点击事件 */ 81 | public void setRecyclerViewOnItemClickListener(RecyclerViewOnItemClickListener onItemClickListener) { 82 | this.mOnItemClickListener = onItemClickListener; 83 | } 84 | 85 | public RecyclerViewOnItemClickListener getOnItemClickListener() { 86 | return mOnItemClickListener; 87 | } 88 | 89 | /** 点击事件接口 */ 90 | public interface RecyclerViewOnItemClickListener { 91 | void onItemClickListener(View view, int position); 92 | } 93 | 94 | 95 | /** 96 | * 内部类Holder 97 | */ 98 | class MyNoteViewHolder extends RecyclerView.ViewHolder { 99 | private TextView tv; 100 | private TextView tv_dateTime; 101 | private View mRowtab; 102 | private Button btnTop; 103 | private Button btnDelete; 104 | private View root; 105 | 106 | public MyNoteViewHolder(View root) { 107 | super(root); 108 | this.root = root; 109 | tv = (TextView) root.findViewById(R.id.row_text); 110 | tv_dateTime = (TextView) root.findViewById(R.id.tv_note_time); 111 | mRowtab = root.findViewById(R.id.row_tab); 112 | btnTop = (Button) root.findViewById(R.id.btnTop); 113 | btnDelete = (Button) root.findViewById(R.id.btnDelete); 114 | } 115 | } 116 | 117 | } 118 | ``` 119 | **3、在Activity中这样用** 120 | ```java 121 | mRecyclerView = (RecyclerView) findViewById(R.id.recycle_notes); 122 | mRecyclerView.setLayoutManager(new LinearLayoutManager(this)); 123 | mCursor = mNoteDbAdapter.fetchAllNotes(); 124 | mNoteAdapter = new NoteAdapter(this, mCursor, 0); 125 | Log.d(TAG, "mCursor的大小为:" + mCursor.getCount()); 126 | 127 | //设置点击事件 128 | mNoteAdapter.setRecyclerViewOnItemClickListener(new NoteAdapter.RecyclerViewOnItemClickListener() { 129 | @Override 130 | public void onItemClickListener(View view, int position) { 131 | if (mCursor == null || mCursor.isClosed()) { 132 | if (mCursor == null) { 133 | Log.d("NoteActivity", "newCursor is null"); 134 | Toast.makeText(NoteActivity.this, "newCursor is null", Toast.LENGTH_SHORT).show(); 135 | } else if (mCursor.isClosed()){ 136 | Log.d("NoteActivity", "newCursor is closed"); 137 | Toast.makeText(NoteActivity.this, "newCursor is null", Toast.LENGTH_SHORT).show(); 138 | } 139 | } else { 140 | mCursor.moveToPosition(position); 141 | String content = mCursor.getString(mCursor.getColumnIndex(NoteDbAdapter.COL_CONTENT)); 142 | int importtant = mCursor.getInt(mCursor.getColumnIndex(NoteDbAdapter.COL_IMPORTANT)); 143 | int id = mCursor.getInt(mCursor.getColumnIndex(NoteDbAdapter.COL_ID)); 144 | Log.d("NoteActivity", content + importtant); 145 | Note clickNote = new Note(id, content, importtant); 146 | Intent intent = new Intent(); 147 | intent.setClass(NoteActivity.this, NoteContentActivity.class); 148 | Bundle bundle = new Bundle(); 149 | bundle.putSerializable("note", clickNote); 150 | intent.putExtras(bundle); 151 | startActivity(intent); 152 | } 153 | 154 | } 155 | }); 156 | 157 | //设置适配器 158 | mRecyclerView.setAdapter(mNoteAdapter); 159 | ``` 160 | 161 | ## 如何在RecycleView上下滑动时隐藏Toolbar和FAB按钮 162 | 思路很简单,只需要记录RecycleView向下滑动(手指向上滑动)时移动的距离,超过一定范围时就会调用Toolbar以及Floating Action Button的animate().translationY方法,令其在Y轴方向上移动,当RecycleView向上滑动(手指向下滑动)时又会反过来回到初始状态,并且当滑动到RecycleView底部时会强制Toolbar和FAB回到初始状态,上代码。 163 | * HidingScrollListener 164 | 165 | ```java 166 | public abstract class HidingScrollListener extends RecyclerView.OnScrollListener { 167 | 168 | private static final int HIDE_THRESHOLD = 20; 169 | private int scrolledDistance = 0; 170 | private boolean controlsVisible = true; 171 | private int mItemSize=0; 172 | 173 | public HidingScrollListener(int itemSize) { 174 | this.mItemSize = itemSize - 1; 175 | } 176 | 177 | /** 178 | * 179 | * @param recyclerView 180 | * @param dx 横向的滚动距离 181 | * @param dy 纵向的滚动距离 182 | * 记录的是两个滚动事件之间的偏移量,而不是总的滚动距离。 183 | */ 184 | @Override 185 | public void onScrolled(RecyclerView recyclerView, int dx, int dy) { 186 | super.onScrolled(recyclerView, dx, dy); 187 | int firstVisibleItem = ((LinearLayoutManager) recyclerView.getLayoutManager()).findFirstVisibleItemPosition(); 188 | int lastVisibleItem = ((LinearLayoutManager) recyclerView.getLayoutManager()).findLastVisibleItemPosition(); 189 | 190 | if (firstVisibleItem == 0||lastVisibleItem==mItemSize) { 191 | if (!controlsVisible) { 192 | onShow(); 193 | controlsVisible = true; 194 | } 195 | }else{ 196 | //如果总的滚动距离超多了一定值 197 | // (这个值取决于你自己的设定,越大,需要滑动的距离越长才能显示或者隐藏), 198 | // 我们就根据其方向显示或者隐藏Toolbar(dy>0意味着下滚,dy<0意味着上滚)。 199 | if (scrolledDistance > HIDE_THRESHOLD && controlsVisible) { 200 | onHide(); 201 | controlsVisible = false; 202 | scrolledDistance = 0; 203 | } else if (scrolledDistance < -HIDE_THRESHOLD && !controlsVisible) { 204 | onShow(); 205 | scrolledDistance = 0; 206 | controlsVisible = true; 207 | } 208 | } 209 | //计算出滚动的总距离(deltas相加), 210 | // 但是只在Toolbar隐藏且上滚或者Toolbar未隐藏且下滚的时候 211 | if ((controlsVisible && dy > 0) || (!controlsVisible && dy < 0)) { 212 | scrolledDistance += dy; 213 | } 214 | } 215 | 216 | public abstract void onHide(); 217 | public abstract void onShow(); 218 | } 219 | ``` 220 | 在Avtivity中使用回掉方法 221 | ```java 222 | //为recycleview设置滚动监听器 223 | mRecyclerView.setOnScrollListener(new HidingScrollListener(mCursor.getCount()) { 224 | @Override 225 | public void onHide() { 226 | hideView(); 227 | } 228 | 229 | @Override 230 | public void onShow() { 231 | showView(); 232 | } 233 | }); 234 | 235 | private void hideView() { 236 | mToolbar.animate().translationY( 237 | -mToolbar.getHeight()).setInterpolator(new AccelerateInterpolator(2)); 238 | FrameLayout.LayoutParams ip = (FrameLayout.LayoutParams) mFloatingActionButton.getLayoutParams(); 239 | int fabButtonMargin = ip.bottomMargin; 240 | mFloatingActionButton.animate().translationY( 241 | mFloatingActionButton.getHeight() + fabButtonMargin).setInterpolator(new AccelerateInterpolator(2)).start(); 242 | } 243 | 244 | private void showView() { 245 | mToolbar.animate().translationY(0).setInterpolator(new DecelerateInterpolator(2)); 246 | mFloatingActionButton.animate().translationY(0).setInterpolator(new DecelerateInterpolator(2)).start(); 247 | } 248 | ``` 249 | **特别注意布局文件** 250 | 如果你发现你运行的效果像下面的截图一样的话,那你肯定是因为布局文件上少写了这两句 251 | ``` 252 | android:clipToPadding="false" 253 | android:paddingTop="?attr/actionBarSize" 254 | ``` 255 | ![bug截图](/screenshot/2.png) 256 | 完整的布局代码如下: 257 | * main_activity.xml 258 | 259 | ```xml 260 | 261 | 265 | 266 | 273 | 274 | 282 | 283 | 296 | 297 | 298 | ``` 299 | ## 最后来讲讲如何实现仿QQ的侧滑出现删除、指定操作 300 | 首先得谢谢[张旭童](http://blog.csdn.net/zxt0601/article/details/53157090) ,他的一个库帮我解决了这个问题,[点击这里](https://github.com/mcxtzhang/SwipeDelMenuLayout)可以访问他的项目。 301 | **1、在布局文件中使用`com.mcxtzhang.swipemenulib.SwipeMenuLayout`布局,在ItemView后添加button表示删除置顶按钮。** 302 | **2、在Adapter中设置打开侧滑菜单,并且可以设置菜单在左还是在右** 303 | ```java 304 | ((SwipeMenuLayout) holder.root.findViewById(R.id.swipeMenuLayout)).setIos(false).setLeftSwipe(false).setSwipeEnable(true); 305 | ``` 306 | **3、在Activity中设置动作事件** 307 | ```java 308 | mNoteAdapter.setOnSwipeListener(new NoteAdapter.onSwipeListener() { 309 | @Override 310 | public void onDel(int pos) { 311 | Toast.makeText(NoteActivity.this, "点击了第" + (pos+1) + "条item的删除按钮", Toast.LENGTH_SHORT).show(); 312 | mCursor.moveToPosition(pos); 313 | int id = mCursor.getInt(mCursor.getColumnIndex(NoteDbAdapter.COL_ID)); 314 | mNoteDbAdapter.deleteNoteById(id); 315 | mCursor = mNoteDbAdapter.fetchAllNotes(); 316 | mNoteAdapter.changeCursor(mCursor); 317 | } 318 | 319 | @Override 320 | public void onTop(int pos) { 321 | Toast.makeText(NoteActivity.this, "点击了第" + (pos+1) + "条item的Top按钮", Toast.LENGTH_SHORT).show(); 322 | mCursor.moveToPosition(pos); 323 | int id = mCursor.getInt(mCursor.getColumnIndex(NoteDbAdapter.COL_ID)); 324 | Note editNote = mNoteDbAdapter.fetchNoteById(id); 325 | editNote.setDateTime(DateUtil.formatDateTime()); 326 | mNoteDbAdapter.updateNote(editNote); 327 | mCursor = mNoteDbAdapter.fetchAllNotes(); 328 | mNoteAdapter.changeCursor(mCursor); 329 | } 330 | }); 331 | ``` 332 | 333 | **大功告成,如果想要看详细代码,或者有什么建议可以到[Github](https://github.com/Wensibob/Note)上给我发Issue或者直接在站内给我留言哦,记得star哦** 334 | 335 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 24 5 | buildToolsVersion "24.0.1" 6 | defaultConfig { 7 | applicationId "bob.com.note" 8 | minSdkVersion 14 9 | targetSdkVersion 24 10 | versionCode 1 11 | versionName "1.0" 12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(dir: 'libs', include: ['*.jar']) 24 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 25 | exclude group: 'com.android.support', module: 'support-annotations' 26 | }) 27 | compile 'com.android.support:appcompat-v7:24.2.0' 28 | compile 'com.android.support:design:24.2.0' 29 | compile "com.android.support:recyclerview-v7:24.2.0" 30 | compile 'com.android.support:cardview-v7:24.0.0' 31 | compile 'com.github.mcxtzhang:SwipeDelMenuLayout:V1.2.1' 32 | testCompile 'junit:junit:4.12' 33 | } 34 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in D:\android-sdk-windows/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/androidTest/java/bob/com/note/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package bob.com.note; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumentation test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("bob.com.note", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/java/bob/com/note/NoteActivity.java: -------------------------------------------------------------------------------- 1 | package bob.com.note; 2 | 3 | import android.content.Intent; 4 | import android.database.Cursor; 5 | import android.os.Bundle; 6 | import android.support.design.widget.FloatingActionButton; 7 | import android.support.v7.app.AppCompatActivity; 8 | import android.support.v7.widget.LinearLayoutManager; 9 | import android.support.v7.widget.RecyclerView; 10 | import android.support.v7.widget.Toolbar; 11 | import android.util.Log; 12 | import android.view.MotionEvent; 13 | import android.view.View; 14 | import android.view.animation.AccelerateInterpolator; 15 | import android.view.animation.DecelerateInterpolator; 16 | import android.widget.FrameLayout; 17 | import android.widget.Toast; 18 | 19 | import com.mcxtzhang.swipemenulib.SwipeMenuLayout; 20 | 21 | import bob.com.note.adapter.NoteAdapter; 22 | import bob.com.note.bean.Note; 23 | import bob.com.note.adapter.NoteDbAdapter; 24 | import bob.com.note.listener.HidingScrollListener; 25 | import bob.com.note.util.DateUtil; 26 | import bob.com.note.util.SharedPreferencesUtil; 27 | 28 | /** 29 | * bob.com.note 30 | * Created by BOB on 2017/3/1. 31 | * 描述:MainActivity ,当用户首次安装app的时候会自动创建一些示例便签,用户点击每条便签可以来到 NoteContentActivity 32 | * 博客园:http://www.cnblogs.com/ghylzwsb/ 33 | * 个人网站:www.wensibo.top 34 | */ 35 | 36 | public class NoteActivity extends AppCompatActivity { 37 | 38 | private RecyclerView mRecyclerView; 39 | private Toolbar mToolbar; 40 | private FloatingActionButton mFloatingActionButton; 41 | public static NoteDbAdapter mNoteDbAdapter; 42 | public static NoteAdapter mNoteAdapter; 43 | private Cursor mCursor; 44 | private static final String TAG = "NoteActivity"; 45 | private boolean isFirstStart; 46 | 47 | 48 | @Override 49 | protected void onCreate(Bundle savedInstanceState) { 50 | super.onCreate(savedInstanceState); 51 | setContentView(R.layout.activity_main); 52 | initToolbar(); 53 | mFloatingActionButton = (FloatingActionButton) findViewById(R.id.button_add_note); 54 | mFloatingActionButton.setOnClickListener(new View.OnClickListener() { 55 | @Override 56 | public void onClick(View view) { 57 | startActivity(new Intent(NoteActivity.this, NoteContentActivity.class)); 58 | } 59 | }); 60 | mNoteDbAdapter = new NoteDbAdapter(this); 61 | mNoteDbAdapter.open(); 62 | 63 | SharedPreferencesUtil shared=new SharedPreferencesUtil(NoteActivity.this,NoteDbAdapter.CONFIG); 64 | isFirstStart=shared.getBoolean(NoteDbAdapter.IS_FIRST_START); 65 | if(!isFirstStart){ 66 | //如果是首次安装app,则需要创建数据库 67 | mNoteDbAdapter.deleteAllNotes();//首先先清除数据库 68 | insertSomeReminders();//加入一些示例便签 69 | shared.putBoolean(NoteDbAdapter.IS_FIRST_START,true);//最后还需要将boolean设置为true 70 | } 71 | } 72 | 73 | @Override 74 | protected void onResume() { 75 | super.onResume(); 76 | initRecycleView(); 77 | Log.d(TAG, "onResume()"); 78 | } 79 | 80 | /** 81 | * 初始化toolbar 82 | */ 83 | private void initToolbar() { 84 | mToolbar = (Toolbar) findViewById(R.id.toolbar); 85 | setSupportActionBar(mToolbar); 86 | setTitle(R.string.app_name); 87 | mToolbar.setTitleTextColor(getResources().getColor(android.R.color.white)); 88 | } 89 | 90 | /** 91 | * 初始化recycleview 92 | */ 93 | private void initRecycleView() { 94 | mRecyclerView = (RecyclerView) findViewById(R.id.recycle_notes); 95 | mRecyclerView.setLayoutManager(new LinearLayoutManager(this)); 96 | mCursor = mNoteDbAdapter.fetchAllNotes(); 97 | mNoteAdapter = new NoteAdapter(this, mCursor, 0); 98 | Log.d(TAG, "mCursor的大小为:" + mCursor.getCount()); 99 | 100 | //设置点击事件 101 | mNoteAdapter.setRecyclerViewOnItemClickListener(new NoteAdapter.RecyclerViewOnItemClickListener() { 102 | @Override 103 | public void onItemClickListener(View view, int position) { 104 | if (mCursor == null || mCursor.isClosed()) { 105 | if (mCursor == null) { 106 | Log.d("NoteActivity", "newCursor is null"); 107 | Toast.makeText(NoteActivity.this, "newCursor is null", Toast.LENGTH_SHORT).show(); 108 | } else if (mCursor.isClosed()){ 109 | Log.d("NoteActivity", "newCursor is closed"); 110 | Toast.makeText(NoteActivity.this, "newCursor is null", Toast.LENGTH_SHORT).show(); 111 | } 112 | } else { 113 | mCursor.moveToPosition(position); 114 | String content = mCursor.getString(mCursor.getColumnIndex(NoteDbAdapter.COL_CONTENT)); 115 | int importtant = mCursor.getInt(mCursor.getColumnIndex(NoteDbAdapter.COL_IMPORTANT)); 116 | int id = mCursor.getInt(mCursor.getColumnIndex(NoteDbAdapter.COL_ID)); 117 | Log.d("NoteActivity", content + importtant); 118 | Note clickNote = new Note(id, content, importtant); 119 | Intent intent = new Intent(); 120 | intent.setClass(NoteActivity.this, NoteContentActivity.class); 121 | Bundle bundle = new Bundle(); 122 | bundle.putSerializable("note", clickNote); 123 | intent.putExtras(bundle); 124 | startActivity(intent); 125 | } 126 | 127 | } 128 | }); 129 | 130 | //设置侧滑事件 131 | mNoteAdapter.setOnSwipeListener(new NoteAdapter.onSwipeListener() { 132 | @Override 133 | public void onDel(int pos) { 134 | Toast.makeText(NoteActivity.this, "点击了第" + (pos+1) + "条item的删除按钮", Toast.LENGTH_SHORT).show(); 135 | mCursor.moveToPosition(pos); 136 | int id = mCursor.getInt(mCursor.getColumnIndex(NoteDbAdapter.COL_ID)); 137 | mNoteDbAdapter.deleteNoteById(id); 138 | mCursor = mNoteDbAdapter.fetchAllNotes(); 139 | mNoteAdapter.changeCursor(mCursor); 140 | } 141 | 142 | @Override 143 | public void onTop(int pos) { 144 | Toast.makeText(NoteActivity.this, "点击了第" + (pos+1) + "条item的Top按钮", Toast.LENGTH_SHORT).show(); 145 | mCursor.moveToPosition(pos); 146 | int id = mCursor.getInt(mCursor.getColumnIndex(NoteDbAdapter.COL_ID)); 147 | Note editNote = mNoteDbAdapter.fetchNoteById(id); 148 | editNote.setDateTime(DateUtil.formatDateTime()); 149 | mNoteDbAdapter.updateNote(editNote); 150 | mCursor = mNoteDbAdapter.fetchAllNotes(); 151 | mNoteAdapter.changeCursor(mCursor); 152 | } 153 | }); 154 | 155 | 156 | //设置适配器 157 | mRecyclerView.setAdapter(mNoteAdapter); 158 | //为recycleview设置滚动监听器 159 | mRecyclerView.setOnScrollListener(new HidingScrollListener(mCursor.getCount()) { 160 | @Override 161 | public void onHide() { 162 | hideView(); 163 | } 164 | 165 | @Override 166 | public void onShow() { 167 | showView(); 168 | } 169 | }); 170 | 171 | // 可以用在:当点击外部空白处时,关闭正在展开的侧滑菜单。我个人觉得意义不大, 172 | mRecyclerView.setOnTouchListener(new View.OnTouchListener() { 173 | @Override 174 | public boolean onTouch(View view, MotionEvent event) { 175 | if (event.getAction() == MotionEvent.ACTION_UP) { 176 | SwipeMenuLayout viewCache = SwipeMenuLayout.getViewCache(); 177 | if (null != viewCache) { 178 | viewCache.smoothClose(); 179 | } 180 | } 181 | return false; 182 | } 183 | }); 184 | } 185 | 186 | 187 | /** 188 | * 初始化数据库,向数据库插入一些数据 189 | */ 190 | private void insertSomeReminders() { 191 | mNoteDbAdapter.createNote("Buy Learn Android Studio", true,DateUtil.formatDateTime()); 192 | mNoteDbAdapter.createNote("Send Dad birthday gift", false,DateUtil.formatDateTime()); 193 | mNoteDbAdapter.createNote("Dinner at the Gage on Friday", true,DateUtil.formatDateTime()); 194 | mNoteDbAdapter.createNote("String squash racket", false,DateUtil.formatDateTime()); 195 | mNoteDbAdapter.createNote("Shovel and salt walkways", false,DateUtil.formatDateTime()); 196 | mNoteDbAdapter.createNote("Prepare Advanced Android syllabus", false,DateUtil.formatDateTime()); 197 | mNoteDbAdapter.createNote("Buy new office chair", true,DateUtil.formatDateTime()); 198 | mNoteDbAdapter.createNote("Call Auto-body shop for quote", false,DateUtil.formatDateTime()); 199 | mNoteDbAdapter.createNote("Renew membership to club", false,DateUtil.formatDateTime()); 200 | mNoteDbAdapter.createNote("Buy new Galaxy Android phone", true,DateUtil.formatDateTime()); 201 | mNoteDbAdapter.createNote("Sell old Android phone - auction", false,DateUtil.formatDateTime()); 202 | mNoteDbAdapter.createNote("Buy new paddles for kayaks", true,DateUtil.formatDateTime()); 203 | mNoteDbAdapter.createNote("Call accountant about tax returns", false,DateUtil.formatDateTime()); 204 | mNoteDbAdapter.createNote("Buy 300,000 shares of Google", false,DateUtil.formatDateTime()); 205 | mNoteDbAdapter.createNote("Call the Dalai Lama back", true,DateUtil.formatDateTime()); 206 | } 207 | 208 | private void hideView() { 209 | mToolbar.animate().translationY( 210 | -mToolbar.getHeight()).setInterpolator(new AccelerateInterpolator(2)); 211 | FrameLayout.LayoutParams ip = (FrameLayout.LayoutParams) mFloatingActionButton.getLayoutParams(); 212 | int fabButtonMargin = ip.bottomMargin; 213 | mFloatingActionButton.animate().translationY( 214 | mFloatingActionButton.getHeight() + fabButtonMargin).setInterpolator(new AccelerateInterpolator(2)).start(); 215 | } 216 | 217 | private void showView() { 218 | mToolbar.animate().translationY(0).setInterpolator(new DecelerateInterpolator(2)); 219 | mFloatingActionButton.animate().translationY(0).setInterpolator(new DecelerateInterpolator(2)).start(); 220 | } 221 | } 222 | -------------------------------------------------------------------------------- /app/src/main/java/bob/com/note/NoteContentActivity.java: -------------------------------------------------------------------------------- 1 | package bob.com.note; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.support.design.widget.Snackbar; 6 | import android.support.v7.app.AppCompatActivity; 7 | import android.support.v7.widget.Toolbar; 8 | import android.text.TextUtils; 9 | import android.util.Log; 10 | import android.view.Menu; 11 | import android.view.MenuItem; 12 | import android.view.View; 13 | import android.widget.EditText; 14 | import android.widget.ScrollView; 15 | import android.widget.Toast; 16 | 17 | import bob.com.note.bean.Note; 18 | import bob.com.note.util.DateUtil; 19 | 20 | /** 21 | * bob.com.note 22 | * Created by BOB on 2017/3/3. 23 | * 描述:便签内容页面,可以对便签进行简单的编辑,保存便签之后可以存储在数据库中 24 | * 博客园:http://www.cnblogs.com/ghylzwsb/ 25 | * 个人网站:www.wensibo.top 26 | */ 27 | 28 | public class NoteContentActivity extends AppCompatActivity { 29 | 30 | private Toolbar mToolbar; 31 | private EditText mEtNoteContent; 32 | private ScrollView mScrollView; 33 | private Note mNote; 34 | private boolean isImportant = true; 35 | private Intent mIntent; 36 | private int mNoteID; 37 | 38 | 39 | @Override 40 | protected void onCreate(Bundle savedInstanceState) { 41 | super.onCreate(savedInstanceState); 42 | setContentView(R.layout.note_content); 43 | 44 | mEtNoteContent = (EditText) findViewById(R.id.et_note_content); 45 | 46 | mScrollView = (ScrollView) findViewById(R.id.scrollview_note_content); 47 | mIntent = this.getIntent(); 48 | 49 | if ((mNote = (Note) mIntent.getSerializableExtra("note")) != null) { 50 | mNoteID = mNote.getId(); 51 | mEtNoteContent.setText(mNote.getContent()); 52 | mEtNoteContent.setSelection(mEtNoteContent.getText().length()); 53 | isImportant = mNote.getInportant() == 1 ? true : false; 54 | Log.d("NoteContentActivity", mNote.getContent() + isImportant); 55 | } 56 | initToolbar(isImportant); 57 | } 58 | 59 | @Override 60 | public boolean onCreateOptionsMenu(Menu menu) { 61 | getMenuInflater().inflate(R.menu.menu_main, menu); 62 | return super.onCreateOptionsMenu(menu); 63 | } 64 | 65 | 66 | /** 67 | * 初始化toolbar 68 | */ 69 | private void initToolbar(boolean isImportant) { 70 | mToolbar = (Toolbar) findViewById(R.id.toolbar_note_content); 71 | setSupportActionBar(mToolbar); 72 | getSupportActionBar().setDisplayHomeAsUpEnabled(true); 73 | mToolbar.setNavigationOnClickListener(new View.OnClickListener() { 74 | @Override 75 | public void onClick(View v) { 76 | finish(); 77 | } 78 | }); 79 | } 80 | 81 | @Override 82 | public boolean onPrepareOptionsMenu(Menu menu) { 83 | menu.findItem(R.id.action_star).setChecked(isImportant); 84 | setItemIcon(menu.findItem(R.id.action_star), isImportant); 85 | 86 | return super.onPrepareOptionsMenu(menu); 87 | } 88 | 89 | @Override 90 | public boolean onOptionsItemSelected(MenuItem item) { 91 | switch (item.getItemId()) { 92 | case R.id.action_star://星型按钮 93 | if (item.isChecked()) { 94 | item.setChecked(false); 95 | setItemIcon(item, false); 96 | Snackbar.make(mScrollView,R.string.not_important,Snackbar.LENGTH_SHORT).show(); 97 | } else { 98 | item.setChecked(true); 99 | setItemIcon(item, true); 100 | Snackbar.make(mScrollView,R.string.important,Snackbar.LENGTH_SHORT).show(); 101 | } 102 | break; 103 | case R.id.action_save://保存按钮 104 | if (mNote != null) {//编辑之后的便签,需要保存 105 | String content = mEtNoteContent.getText().toString(); 106 | Note editNote = new Note(mNote.getId(), content, isImportant ? 1 : 0,DateUtil.formatDateTime()); 107 | int result=NoteActivity.mNoteDbAdapter.updateNote(editNote); 108 | Log.d("NoteContentActivity", "在数据库更新数据结果为:" + result); 109 | 110 | finish(); 111 | break; 112 | } else {//新建便签 113 | if (TextUtils.isEmpty(mEtNoteContent.getText().toString())) { 114 | Toast.makeText(NoteContentActivity.this, R.string.not_empty, Toast.LENGTH_SHORT).show(); 115 | Snackbar.make(mScrollView,R.string.not_empty,Snackbar.LENGTH_SHORT).show(); 116 | break; 117 | } else { 118 | String content = mEtNoteContent.getText().toString(); 119 | long result=NoteActivity.mNoteDbAdapter.createNote(content,isImportant, DateUtil.formatDateTime()); 120 | Log.d("NoteContentActivity", "向数据库中插入数据结果为:" + result); 121 | finish(); 122 | break; 123 | } 124 | } 125 | 126 | 127 | } 128 | return super.onOptionsItemSelected(item); 129 | } 130 | 131 | //设置星型的图片 132 | private void setItemIcon(MenuItem item, boolean isImportant) { 133 | item.setIcon(isImportant ? android.R.drawable.btn_star_big_on : android.R.drawable.btn_star_big_off); 134 | this.isImportant = isImportant; 135 | } 136 | 137 | 138 | } 139 | -------------------------------------------------------------------------------- /app/src/main/java/bob/com/note/adapter/NoteAdapter.java: -------------------------------------------------------------------------------- 1 | package bob.com.note.adapter; 2 | 3 | import android.content.Context; 4 | import android.database.Cursor; 5 | import android.support.v7.widget.RecyclerView; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | import android.widget.Button; 10 | import android.widget.TextView; 11 | 12 | import com.mcxtzhang.swipemenulib.SwipeMenuLayout; 13 | 14 | import bob.com.note.R; 15 | import bob.com.note.util.RecyclerViewCursorAdapter; 16 | 17 | /** 18 | * bob.com.note.adapter 19 | * Created by BOB on 2017/3/1. 20 | * 描述:Recycle View的适配器,由于用到了Cursor,所以这里只能是继承RecyclerViewCursorAdapter, 21 | * 同时这里还适配了recycle View的item的侧滑事件,监听删除、置顶操作 22 | * 23 | * 博客园:http://www.cnblogs.com/ghylzwsb/ 24 | * 个人网站:www.wensibo.top 25 | */ 26 | 27 | public class NoteAdapter extends RecyclerViewCursorAdapter { 28 | 29 | private Context mContext; 30 | private RecyclerViewOnItemClickListener mOnItemClickListener; 31 | private onSwipeListener mOnSwipeListener; 32 | 33 | 34 | public NoteAdapter(Context context,Cursor cursor,int flags) { 35 | super(context,cursor,flags); 36 | this.mContext = context; 37 | } 38 | 39 | @Override 40 | public MyNoteViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 41 | View root = LayoutInflater.from(parent.getContext()).inflate(R.layout.note_row, parent, false); 42 | MyNoteViewHolder holder = new MyNoteViewHolder(root); 43 | return holder; 44 | } 45 | 46 | @Override 47 | public void onBindViewHolder(final MyNoteViewHolder holder, Cursor cursor) { 48 | int position = cursor.getPosition(); 49 | holder.tv.setText(cursor.getString(cursor.getColumnIndex(NoteDbAdapter.COL_CONTENT))); 50 | holder.tv_dateTime.setText(cursor.getString(cursor.getColumnIndex(NoteDbAdapter.COL_DATETIME))); 51 | holder.mRowtab.setBackgroundColor(cursor.getInt(cursor.getColumnIndex(NoteDbAdapter.COL_IMPORTANT)) == 1? 52 | mContext.getResources().getColor(R.color.colorAccent):mContext.getResources().getColor(android.R.color.white) 53 | ); 54 | holder.root.setTag(position); 55 | ((SwipeMenuLayout) holder.root.findViewById(R.id.swipeMenuLayout)).setIos(false).setLeftSwipe(false).setSwipeEnable(true); 56 | 57 | holder.btnTop.setOnClickListener(new View.OnClickListener() { 58 | @Override 59 | public void onClick(View view) { 60 | if (mOnSwipeListener != null) { 61 | mOnSwipeListener.onTop(holder.getAdapterPosition()); 62 | } 63 | } 64 | }); 65 | 66 | holder.btnDelete.setOnClickListener(new View.OnClickListener() { 67 | @Override 68 | public void onClick(View view) { 69 | if (mOnSwipeListener != null) { 70 | mOnSwipeListener.onDel(holder.getAdapterPosition()); 71 | } 72 | } 73 | }); 74 | 75 | holder.tv.setOnClickListener(new View.OnClickListener() { 76 | @Override 77 | public void onClick(View view) { 78 | if (mOnItemClickListener != null) { 79 | mOnItemClickListener.onItemClickListener(view, holder.getAdapterPosition()); 80 | } 81 | } 82 | }); 83 | 84 | 85 | } 86 | 87 | @Override 88 | protected void onContentChanged() { 89 | 90 | } 91 | 92 | 93 | 94 | 95 | /** 设置点击事件 */ 96 | public void setRecyclerViewOnItemClickListener(RecyclerViewOnItemClickListener onItemClickListener) { 97 | this.mOnItemClickListener = onItemClickListener; 98 | } 99 | 100 | public RecyclerViewOnItemClickListener getOnItemClickListener() { 101 | return mOnItemClickListener; 102 | } 103 | 104 | 105 | /** 获取侧滑时间监听器*/ 106 | public onSwipeListener getOnSwipeListener() { 107 | return mOnSwipeListener; 108 | } 109 | 110 | /** 设置侧滑时间监听器*/ 111 | public void setOnSwipeListener(onSwipeListener mOnSwipeListener) { 112 | this.mOnSwipeListener = mOnSwipeListener; 113 | } 114 | 115 | /** 点击事件接口 */ 116 | public interface RecyclerViewOnItemClickListener { 117 | void onItemClickListener(View view, int position); 118 | } 119 | 120 | /** 侧滑事件接口 */ 121 | public interface onSwipeListener { 122 | void onDel(int pos); 123 | void onTop(int pos); 124 | } 125 | 126 | 127 | /** 128 | * 内部类Holder 129 | */ 130 | class MyNoteViewHolder extends RecyclerView.ViewHolder { 131 | private TextView tv; 132 | private TextView tv_dateTime; 133 | private View mRowtab; 134 | private Button btnTop; 135 | private Button btnDelete; 136 | private View root; 137 | 138 | public MyNoteViewHolder(View root) { 139 | super(root); 140 | this.root = root; 141 | tv = (TextView) root.findViewById(R.id.row_text); 142 | tv_dateTime = (TextView) root.findViewById(R.id.tv_note_time); 143 | mRowtab = root.findViewById(R.id.row_tab); 144 | btnTop = (Button) root.findViewById(R.id.btnTop); 145 | btnDelete = (Button) root.findViewById(R.id.btnDelete); 146 | } 147 | } 148 | 149 | } 150 | -------------------------------------------------------------------------------- /app/src/main/java/bob/com/note/adapter/NoteDbAdapter.java: -------------------------------------------------------------------------------- 1 | package bob.com.note.adapter; 2 | 3 | import android.content.ContentValues; 4 | import android.content.Context; 5 | import android.database.Cursor; 6 | import android.database.SQLException; 7 | import android.database.sqlite.SQLiteDatabase; 8 | import android.database.sqlite.SQLiteOpenHelper; 9 | import android.util.Log; 10 | 11 | import bob.com.note.bean.Note; 12 | 13 | /** 14 | * bob.com.note.database 15 | * Created by BOB on 2017/3/1. 16 | * 描述:数据库的操作类,用于对数据的增删改查操作 17 | * 博客园:http://www.cnblogs.com/ghylzwsb/ 18 | * 个人网站:www.wensibo.top 19 | */ 20 | 21 | public class NoteDbAdapter { 22 | 23 | //判断是否首次打开app 24 | public static final String CONFIG = "config";//存放用户信息 25 | public static final String IS_FIRST_START="is_first_start";//app是否首次安装 26 | 27 | //数据库表的列名 28 | public static final String COL_ID = "_id"; 29 | public static final String COL_CONTENT = "content"; 30 | public static final String COL_IMPORTANT = "important"; 31 | public static final String COL_DATETIME = "last_motidied_time"; 32 | //相关的索引 33 | public static final int INDEX_ID = 0; 34 | public static final int INDEX_CONTENT = INDEX_ID + 1; 35 | public static final int INDEX_IMPORTANT = INDEX_ID + 2; 36 | public static final int INDEX_DATETIME = INDEX_ID + 3; 37 | //数据库名称,表名称,数据库版本 38 | private static final String DATABASE_NAME = "dba_note"; 39 | private static final String TABLE_NAME = "tb1_note"; 40 | private static final int DATABASE_VERSION = 1; 41 | 42 | //打印日志 43 | private static final String TAG = "NoteBdAdapter"; 44 | private DataBaseHelper mDataBaseHelper; 45 | private SQLiteDatabase mDb; 46 | private final Context mContext; 47 | 48 | //创建数据库表的语句 49 | private static final String DATABASE_CREATE = 50 | "CREATE TABLE if not exists " + TABLE_NAME + " ( " + 51 | COL_ID + " INTEGER PRIMARY KEY autoincrement, " + 52 | COL_CONTENT + " TEXT," + 53 | COL_IMPORTANT + " INTEGER ," + 54 | COL_DATETIME + " TEXT "+" );"; 55 | private static final String UPGRADING_DATABASE = "DROP TABLE IF EXISTS " + TABLE_NAME; 56 | 57 | 58 | public NoteDbAdapter(Context context) { 59 | mContext = context; 60 | } 61 | 62 | /** 63 | * open database 64 | * @throws SQLException 65 | */ 66 | public void open() throws SQLException { 67 | mDataBaseHelper = new DataBaseHelper(mContext); 68 | mDb = mDataBaseHelper.getWritableDatabase(); 69 | } 70 | 71 | /** 72 | * close database 73 | */ 74 | public void close(){ 75 | if (mDataBaseHelper != null) { 76 | mDataBaseHelper.close(); 77 | } 78 | } 79 | 80 | //创建便签 81 | public long createNote(String name, boolean important,String dateTime) { 82 | ContentValues contentValues = new ContentValues(); 83 | contentValues.put(COL_CONTENT, name); 84 | contentValues.put(COL_IMPORTANT, important ? 1 : 0); 85 | contentValues.put(COL_DATETIME,dateTime); 86 | return mDb.insert(TABLE_NAME, null, contentValues); 87 | } 88 | 89 | //创建便签的重载 90 | public long createNote(Note note) { 91 | ContentValues contentValues = new ContentValues(); 92 | contentValues.put(COL_CONTENT, note.getContent()); 93 | contentValues.put(COL_IMPORTANT, note.getInportant()); 94 | contentValues.put(COL_DATETIME,note.getDateTime()); 95 | return mDb.insert(TABLE_NAME, null, contentValues); 96 | } 97 | 98 | //根据ID取出便签 99 | public Note fetchNoteById(int id) { 100 | Cursor cursor=mDb.query(TABLE_NAME,new String[]{COL_ID,COL_CONTENT,COL_IMPORTANT,COL_DATETIME}, 101 | COL_ID+"=?",new String[]{String.valueOf(id)},null,null,null,null); 102 | if (cursor != null) { 103 | cursor.moveToFirst(); 104 | } 105 | return new Note( 106 | cursor.getInt(INDEX_ID), 107 | cursor.getString(INDEX_CONTENT), 108 | cursor.getInt(INDEX_IMPORTANT), 109 | cursor.getString(INDEX_DATETIME)); 110 | } 111 | 112 | //取出全部便签 113 | public Cursor fetchAllNotes() { 114 | Cursor mCursor = mDb.query(TABLE_NAME, new String[]{COL_ID, COL_CONTENT, COL_IMPORTANT,COL_DATETIME}, 115 | null, null, null, null,"last_motidied_time desc"); 116 | if (mCursor != null) { 117 | mCursor.moveToFirst(); 118 | } 119 | return mCursor; 120 | } 121 | 122 | //根据便签对象更新便签 123 | public int updateNote(Note note) { 124 | ContentValues contentValues = new ContentValues(); 125 | contentValues.put(COL_CONTENT, note.getContent()); 126 | contentValues.put(COL_IMPORTANT, note.getInportant()); 127 | contentValues.put(COL_DATETIME,note.getDateTime()); 128 | return mDb.update(TABLE_NAME, contentValues, 129 | COL_ID + "=?", new String[]{String.valueOf(note.getId())}); 130 | } 131 | 132 | //通过便签ID删除 133 | public void deleteNoteById(int id) { 134 | mDb.delete(TABLE_NAME, COL_ID + "=?", new String[]{String.valueOf(id)}); 135 | } 136 | 137 | //删除所有便签 138 | public void deleteAllNotes() { 139 | mDb.delete(TABLE_NAME, null, null); 140 | } 141 | 142 | private static class DataBaseHelper extends SQLiteOpenHelper { 143 | 144 | public DataBaseHelper(Context context) { 145 | super(context, DATABASE_NAME, null, DATABASE_VERSION); 146 | } 147 | 148 | @Override 149 | public void onCreate(SQLiteDatabase database) { 150 | Log.w(TAG, DATABASE_CREATE); 151 | database.execSQL(DATABASE_CREATE); 152 | } 153 | 154 | @Override 155 | public void onUpgrade(SQLiteDatabase database, int oldVersion, int newVersion) { 156 | Log.w(TAG, "Upgrading database from version " + oldVersion + " to " + newVersion + ", which will destroy all old data"); 157 | database.execSQL(UPGRADING_DATABASE); 158 | onCreate(database); 159 | } 160 | } 161 | 162 | } 163 | -------------------------------------------------------------------------------- /app/src/main/java/bob/com/note/bean/Note.java: -------------------------------------------------------------------------------- 1 | package bob.com.note.bean; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * bob.com.note.bean 7 | * Created by BOB on 2017/3/1. 8 | * 描述:便签bean类 9 | * 博客园:http://www.cnblogs.com/ghylzwsb/ 10 | * 个人网站:www.wensibo.top 11 | */ 12 | 13 | public class Note implements Serializable { 14 | 15 | private int mId;//便签ID 16 | private String mContent;//便签内容 17 | private int mInportant;//便签是否为星,0代表不为星,1代表为星 18 | private String mDateTime; 19 | 20 | public Note(int id, String content, int inportant,String dateTime) { 21 | mId = id; 22 | mContent = content; 23 | mInportant = inportant; 24 | mDateTime = dateTime; 25 | } 26 | 27 | public Note(int id, String content, int inportant) { 28 | mId = id; 29 | mContent = content; 30 | mInportant = inportant; 31 | } 32 | 33 | public int getId() { 34 | return mId; 35 | } 36 | 37 | public void setId(int id) { 38 | mId = id; 39 | } 40 | 41 | public String getContent() { 42 | return mContent; 43 | } 44 | 45 | public void setContent(String content) { 46 | mContent = content; 47 | } 48 | 49 | public int getInportant() { 50 | return mInportant; 51 | } 52 | 53 | public void setInportant(int inportant) { 54 | mInportant = inportant; 55 | } 56 | 57 | public String getDateTime() { 58 | return mDateTime; 59 | } 60 | 61 | public void setDateTime(String dateTime) { 62 | mDateTime = dateTime; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /app/src/main/java/bob/com/note/listener/HidingScrollListener.java: -------------------------------------------------------------------------------- 1 | package bob.com.note.listener; 2 | 3 | import android.support.v7.widget.LinearLayoutManager; 4 | import android.support.v7.widget.RecyclerView; 5 | 6 | /** 7 | * bob.com.note.listener 8 | * Created by BOB on 2017/3/1. 9 | * 描述:用于实现主界面上下滑动recycle View的时候Toolbar以及FAB的隐藏效果 10 | * 博客园:http://www.cnblogs.com/ghylzwsb/ 11 | * 个人网站:www.wensibo.top 12 | */ 13 | 14 | public abstract class HidingScrollListener extends RecyclerView.OnScrollListener { 15 | 16 | private static final int HIDE_THRESHOLD = 20; 17 | private int scrolledDistance = 0; 18 | private boolean controlsVisible = true; 19 | private int mItemSize=0; 20 | 21 | public HidingScrollListener(int itemSize) { 22 | this.mItemSize = itemSize - 1; 23 | } 24 | 25 | /** 26 | * 27 | * @param recyclerView 28 | * @param dx 横向的滚动距离 29 | * @param dy 纵向的滚动距离 30 | * 记录的是两个滚动事件之间的偏移量,而不是总的滚动距离。 31 | */ 32 | @Override 33 | public void onScrolled(RecyclerView recyclerView, int dx, int dy) { 34 | super.onScrolled(recyclerView, dx, dy); 35 | int firstVisibleItem = ((LinearLayoutManager) recyclerView.getLayoutManager()).findFirstVisibleItemPosition(); 36 | int lastVisibleItem = ((LinearLayoutManager) recyclerView.getLayoutManager()).findLastVisibleItemPosition(); 37 | 38 | if (firstVisibleItem == 0||lastVisibleItem==mItemSize) { 39 | if (!controlsVisible) { 40 | onShow(); 41 | controlsVisible = true; 42 | } 43 | }else{ 44 | //如果总的滚动距离超多了一定值 45 | // (这个值取决于你自己的设定,越大,需要滑动的距离越长才能显示或者隐藏), 46 | // 我们就根据其方向显示或者隐藏Toolbar(dy>0意味着下滚,dy<0意味着上滚)。 47 | if (scrolledDistance > HIDE_THRESHOLD && controlsVisible) { 48 | onHide(); 49 | controlsVisible = false; 50 | scrolledDistance = 0; 51 | } else if (scrolledDistance < -HIDE_THRESHOLD && !controlsVisible) { 52 | onShow(); 53 | scrolledDistance = 0; 54 | controlsVisible = true; 55 | } 56 | } 57 | //计算出滚动的总距离(deltas相加), 58 | // 但是只在Toolbar隐藏且上滚或者Toolbar未隐藏且下滚的时候 59 | if ((controlsVisible && dy > 0) || (!controlsVisible && dy < 0)) { 60 | scrolledDistance += dy; 61 | } 62 | } 63 | 64 | public abstract void onHide(); 65 | public abstract void onShow(); 66 | } 67 | -------------------------------------------------------------------------------- /app/src/main/java/bob/com/note/util/CursorFilter.java: -------------------------------------------------------------------------------- 1 | package bob.com.note.util; 2 | 3 | import android.widget.Filter; 4 | import android.database.Cursor; 5 | 6 | /** 7 | * bob.com.note.util 8 | * Created by BOB on 2017/3/2. 9 | * 描述: 10 | * The CursorFilter delegates most of the work to the 11 | * {@link android.widget.CursorAdapter}. Subclasses should override these 12 | * delegate methods to run the queries and convert the results into String 13 | * that can be used by auto-completion widgets. 14 | * 博客园:http://www.cnblogs.com/ghylzwsb/ 15 | * 个人网站:www.wensibo.top 16 | */ 17 | 18 | class CursorFilter extends Filter { 19 | 20 | CursorFilterClient mClient; 21 | 22 | interface CursorFilterClient { 23 | CharSequence convertToString(Cursor cursor); 24 | Cursor runQueryOnBackgroundThread(CharSequence constraint); 25 | Cursor getCursor(); 26 | void changeCursor(Cursor cursor); 27 | } 28 | 29 | CursorFilter(CursorFilterClient client) { 30 | mClient = client; 31 | } 32 | 33 | @Override 34 | public CharSequence convertResultToString(Object resultValue) { 35 | return mClient.convertToString((Cursor) resultValue); 36 | } 37 | 38 | @Override 39 | protected Filter.FilterResults performFiltering(CharSequence constraint) { 40 | Cursor cursor = mClient.runQueryOnBackgroundThread(constraint); 41 | 42 | FilterResults results = new FilterResults(); 43 | if (cursor != null) { 44 | results.count = cursor.getCount(); 45 | results.values = cursor; 46 | } else { 47 | results.count = 0; 48 | results.values = null; 49 | } 50 | return results; 51 | } 52 | 53 | @Override 54 | protected void publishResults(CharSequence constraint, FilterResults results) { 55 | Cursor oldCursor = mClient.getCursor(); 56 | 57 | if (results.values != null && results.values != oldCursor) { 58 | mClient.changeCursor((Cursor) results.values); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /app/src/main/java/bob/com/note/util/DateUtil.java: -------------------------------------------------------------------------------- 1 | package bob.com.note.util; 2 | 3 | import java.text.SimpleDateFormat; 4 | import java.util.Date; 5 | 6 | /** 7 | * bob.com.note.util 8 | * Created by BOB on 2017/3/6. 9 | * 描述:时间工具类,用于获取当前时间写入数据库 10 | * 博客园:http://www.cnblogs.com/ghylzwsb/ 11 | * 个人网站:www.wensibo.top 12 | */ 13 | 14 | public class DateUtil { 15 | 16 | public static String formatDateTime() { 17 | return new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss").format(new Date(System.currentTimeMillis())); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /app/src/main/java/bob/com/note/util/RecyclerViewCursorAdapter.java: -------------------------------------------------------------------------------- 1 | package bob.com.note.util; 2 | 3 | import android.content.Context; 4 | import android.database.ContentObserver; 5 | import android.database.Cursor; 6 | import android.database.DataSetObserver; 7 | import android.os.Handler; 8 | import android.support.v7.widget.RecyclerView; 9 | import android.widget.Filter; 10 | import android.widget.FilterQueryProvider; 11 | import android.widget.Filterable; 12 | 13 | /** 14 | * bob.com.note.util 15 | * Created by BOB on 2017/3/2. 16 | * 描述: 17 | * 博客园:http://www.cnblogs.com/ghylzwsb/ 18 | * 个人网站:www.wensibo.top 19 | */ 20 | 21 | public abstract class RecyclerViewCursorAdapter extends RecyclerView.Adapter implements Filterable, 22 | CursorFilter.CursorFilterClient { 23 | 24 | /** 25 | * Call when bind view with the cursor 26 | * @param holder 27 | * @param cursor 28 | */ 29 | public abstract void onBindViewHolder(VH holder, Cursor cursor); 30 | 31 | /** 32 | * This field should be made private, so it is hidden from the SDK. 33 | * {@hide} 34 | */ 35 | protected boolean mDataValid; 36 | 37 | /** 38 | * The current cursor 39 | */ 40 | protected Cursor mCursor; 41 | 42 | /** 43 | * This field should be made private, so it is hidden from the SDK. 44 | * {@hide} 45 | */ 46 | protected Context mContext; 47 | 48 | /** 49 | * The row id column 50 | */ 51 | protected int mRowIDColumn; 52 | 53 | /** 54 | * This field should be made private, so it is hidden from the SDK. 55 | * {@hide} 56 | */ 57 | protected ChangeObserver mChangeObserver; 58 | /** 59 | * This field should be made private, so it is hidden from the SDK. 60 | * {@hide} 61 | */ 62 | protected DataSetObserver mDataSetObserver; 63 | 64 | /** 65 | * This field should be made private, so it is hidden from the SDK. 66 | * {@hide} 67 | */ 68 | protected CursorFilter mCursorFilter; 69 | 70 | /** 71 | * This field should be made private, so it is hidden from the SDK. 72 | * {@hide} 73 | */ 74 | protected FilterQueryProvider mFilterQueryProvider; 75 | 76 | /** 77 | * If set the adapter will register a content observer on the cursor and will call 78 | * {@link #onContentChanged()} when a notification comes in. Be careful when 79 | * using this flag: you will need to unset the current Cursor from the adapter 80 | * to avoid leaks due to its registered observers. This flag is not needed 81 | * when using a CursorAdapter with a 82 | * {@link android.content.CursorLoader}. 83 | */ 84 | public static final int FLAG_REGISTER_CONTENT_OBSERVER = 0x02; 85 | 86 | /** 87 | * Recommended constructor. 88 | * 89 | * @param c The cursor from which to get the data. 90 | * @param context The context 91 | * @param flags Flags used to determine the behavior of the adapter; 92 | * Currently it accept {@link #FLAG_REGISTER_CONTENT_OBSERVER}. 93 | */ 94 | public RecyclerViewCursorAdapter(Context context, Cursor c, int flags) { 95 | init(context, c, flags); 96 | } 97 | 98 | void init(Context context, Cursor c, int flags) { 99 | 100 | boolean cursorPresent = c != null; 101 | mCursor = c; 102 | mDataValid = cursorPresent; 103 | mContext = context; 104 | mRowIDColumn = cursorPresent ? c.getColumnIndexOrThrow("_id") : -1; 105 | if ((flags & FLAG_REGISTER_CONTENT_OBSERVER) == FLAG_REGISTER_CONTENT_OBSERVER) { 106 | mChangeObserver = new ChangeObserver(); 107 | mDataSetObserver = new MyDataSetObserver(); 108 | } else { 109 | mChangeObserver = null; 110 | mDataSetObserver = null; 111 | } 112 | 113 | if (cursorPresent) { 114 | if (mChangeObserver != null) c.registerContentObserver(mChangeObserver); 115 | if (mDataSetObserver != null) c.registerDataSetObserver(mDataSetObserver); 116 | } 117 | setHasStableIds(true); 118 | } 119 | 120 | /** 121 | * Returns the cursor. 122 | * @return the cursor. 123 | */ 124 | @Override 125 | public Cursor getCursor() { 126 | return mCursor; 127 | } 128 | 129 | /** 130 | * @see android.support.v7.widget.RecyclerView.Adapter#getItemCount() 131 | */ 132 | @Override 133 | public int getItemCount() { 134 | if (mDataValid && mCursor != null) { 135 | return mCursor.getCount(); 136 | } else { 137 | return 0; 138 | } 139 | } 140 | 141 | /** 142 | * @see android.support.v7.widget.RecyclerView.Adapter#getItemId(int) 143 | * 144 | * @param position Adapter position to query 145 | * @return 146 | */ 147 | @Override 148 | public long getItemId(int position) { 149 | if (mDataValid && mCursor != null) { 150 | if (mCursor.moveToPosition(position)) { 151 | return mCursor.getLong(mRowIDColumn); 152 | } else { 153 | return 0; 154 | } 155 | } else { 156 | return 0; 157 | } 158 | } 159 | 160 | @Override 161 | public void onBindViewHolder(VH holder, int position) { 162 | if (!mDataValid) { 163 | throw new IllegalStateException("this should only be called when the cursor is valid"); 164 | } 165 | if (!mCursor.moveToPosition(position)) { 166 | throw new IllegalStateException("couldn't move cursor to position " + position); 167 | } 168 | onBindViewHolder(holder, mCursor); 169 | } 170 | 171 | /** 172 | * Change the underlying cursor to a new cursor. If there is an existing cursor it will be 173 | * closed. 174 | * 175 | * @param cursor The new cursor to be used 176 | */ 177 | public void changeCursor(Cursor cursor) { 178 | Cursor old = swapCursor(cursor); 179 | if (old != null) { 180 | old.close(); 181 | } 182 | } 183 | 184 | /** 185 | * Swap in a new Cursor, returning the old Cursor. Unlike 186 | * {@link #changeCursor(Cursor)}, the returned old Cursor is not 187 | * closed. 188 | * 189 | * @param newCursor The new cursor to be used. 190 | * @return Returns the previously set Cursor, or null if there wasa not one. 191 | * If the given new Cursor is the same instance is the previously set 192 | * Cursor, null is also returned. 193 | */ 194 | public Cursor swapCursor(Cursor newCursor) { 195 | if (newCursor == mCursor) { 196 | return null; 197 | } 198 | Cursor oldCursor = mCursor; 199 | if (oldCursor != null) { 200 | if (mChangeObserver != null) oldCursor.unregisterContentObserver(mChangeObserver); 201 | if (mDataSetObserver != null) oldCursor.unregisterDataSetObserver(mDataSetObserver); 202 | } 203 | mCursor = newCursor; 204 | if (newCursor != null) { 205 | if (mChangeObserver != null) newCursor.registerContentObserver(mChangeObserver); 206 | if (mDataSetObserver != null) newCursor.registerDataSetObserver(mDataSetObserver); 207 | mRowIDColumn = newCursor.getColumnIndexOrThrow("_id"); 208 | mDataValid = true; 209 | // notify the observers about the new cursor 210 | notifyDataSetChanged(); 211 | } else { 212 | mRowIDColumn = -1; 213 | mDataValid = false; 214 | // notify the observers about the lack of a data set 215 | notifyDataSetChanged(); 216 | // notifyDataSetInvalidated(); 217 | } 218 | return oldCursor; 219 | } 220 | 221 | /** 222 | *

Converts the cursor into a CharSequence. Subclasses should override this 223 | * method to convert their results. The default implementation returns an 224 | * empty String for null values or the default String representation of 225 | * the value.

226 | * 227 | * @param cursor the cursor to convert to a CharSequence 228 | * @return a CharSequence representing the value 229 | */ 230 | public CharSequence convertToString(Cursor cursor) { 231 | return cursor == null ? "" : cursor.toString(); 232 | } 233 | 234 | /** 235 | * Runs a query with the specified constraint. This query is requested 236 | * by the filter attached to this adapter. 237 | * 238 | * The query is provided by a 239 | * {@link android.widget.FilterQueryProvider}. 240 | * If no provider is specified, the current cursor is not filtered and returned. 241 | * 242 | * After this method returns the resulting cursor is passed to {@link #changeCursor(Cursor)} 243 | * and the previous cursor is closed. 244 | * 245 | * This method is always executed on a background thread, not on the 246 | * application's main thread (or UI thread.) 247 | * 248 | * Contract: when constraint is null or empty, the original results, 249 | * prior to any filtering, must be returned. 250 | * 251 | * @param constraint the constraint with which the query must be filtered 252 | * 253 | * @return a Cursor representing the results of the new query 254 | * 255 | * @see #getFilter() 256 | * @see #getFilterQueryProvider() 257 | * @see #setFilterQueryProvider(android.widget.FilterQueryProvider) 258 | */ 259 | public Cursor runQueryOnBackgroundThread(CharSequence constraint) { 260 | if (mFilterQueryProvider != null) { 261 | return mFilterQueryProvider.runQuery(constraint); 262 | } 263 | 264 | return mCursor; 265 | } 266 | 267 | public Filter getFilter() { 268 | if (mCursorFilter == null) { 269 | mCursorFilter = new CursorFilter(this); 270 | } 271 | return mCursorFilter; 272 | } 273 | 274 | /** 275 | * Returns the query filter provider used for filtering. When the 276 | * provider is null, no filtering occurs. 277 | * 278 | * @return the current filter query provider or null if it does not exist 279 | * 280 | * @see #setFilterQueryProvider(android.widget.FilterQueryProvider) 281 | * @see #runQueryOnBackgroundThread(CharSequence) 282 | */ 283 | public FilterQueryProvider getFilterQueryProvider() { 284 | return mFilterQueryProvider; 285 | } 286 | 287 | /** 288 | * Sets the query filter provider used to filter the current Cursor. 289 | * The provider's 290 | * {@link android.widget.FilterQueryProvider#runQuery(CharSequence)} 291 | * method is invoked when filtering is requested by a client of 292 | * this adapter. 293 | * 294 | * @param filterQueryProvider the filter query provider or null to remove it 295 | * 296 | * @see #getFilterQueryProvider() 297 | * @see #runQueryOnBackgroundThread(CharSequence) 298 | */ 299 | public void setFilterQueryProvider(FilterQueryProvider filterQueryProvider) { 300 | mFilterQueryProvider = filterQueryProvider; 301 | } 302 | 303 | /** 304 | * Called when the {@link ContentObserver} on the cursor receives a change notification. 305 | * The default implementation provides the auto-requery logic, but may be overridden by 306 | * sub classes. 307 | * 308 | * @see ContentObserver#onChange(boolean) 309 | */ 310 | protected abstract void onContentChanged(); 311 | 312 | private class ChangeObserver extends ContentObserver { 313 | public ChangeObserver() { 314 | super(new Handler()); 315 | } 316 | 317 | @Override 318 | public boolean deliverSelfNotifications() { 319 | return true; 320 | } 321 | 322 | @Override 323 | public void onChange(boolean selfChange) { 324 | onContentChanged(); 325 | } 326 | } 327 | 328 | private class MyDataSetObserver extends DataSetObserver { 329 | @Override 330 | public void onChanged() { 331 | mDataValid = true; 332 | notifyDataSetChanged(); 333 | } 334 | 335 | @Override 336 | public void onInvalidated() { 337 | mDataValid = false; 338 | notifyDataSetChanged(); 339 | // notifyDataSetInvalidated(); 340 | } 341 | } 342 | } 343 | -------------------------------------------------------------------------------- /app/src/main/java/bob/com/note/util/SharedPreferencesUtil.java: -------------------------------------------------------------------------------- 1 | package bob.com.note.util; 2 | 3 | import android.content.Context; 4 | import android.content.SharedPreferences; 5 | import android.content.SharedPreferences.Editor; 6 | import android.util.Log; 7 | 8 | 9 | /** 10 | * bob.com.note.util 11 | * Created by BOB on 2017/3/7. 12 | * 描述:SharedPreferences工具类,用于判断用户是否首次安装app,如果首次安装,则会创建示例便签 13 | * 博客园:http://www.cnblogs.com/ghylzwsb/ 14 | * 个人网站:www.wensibo.top 15 | */ 16 | 17 | public class SharedPreferencesUtil { 18 | 19 | private Context mContext; 20 | private Editor mEditor; 21 | private SharedPreferences mPreferences; 22 | private String mFileName = ""; 23 | private int mMode = 0; 24 | private static final String TAG = SharedPreferencesUtil.class.getSimpleName(); 25 | 26 | 27 | public SharedPreferencesUtil(Context context, String fileName){ 28 | this.mContext = context; 29 | this.mPreferences = context.getSharedPreferences(fileName, Context.MODE_PRIVATE); 30 | this.mEditor = this.mPreferences.edit(); 31 | mFileName = fileName; 32 | mMode = Context.MODE_PRIVATE; 33 | Log.d(TAG," create SharedPreferencesUtil; name : " + mFileName + "; mode : " + mMode); 34 | } 35 | 36 | public SharedPreferencesUtil(Context context, String fileName, int mode){ 37 | this.mContext = context; 38 | this.mPreferences = context.getSharedPreferences(fileName, mode); 39 | this.mEditor = this.mPreferences.edit(); 40 | mFileName = fileName; 41 | mMode = mode; 42 | Log.d(TAG," create SharedPreferencesUtil; name : " + mFileName + "; mode : " + mMode); 43 | } 44 | 45 | // 读写配置文件 46 | public boolean putString(String name, String value) { 47 | mEditor.putString(name, value); 48 | boolean result = mEditor.commit(); 49 | 50 | Log.d(TAG, " put key : "+name+", value : "+value+" to file : "+mFileName+" result: "+result); 51 | return result; 52 | } 53 | 54 | public boolean putLong(String name, Long value) { 55 | mEditor.putLong(name, value); 56 | boolean result = mEditor.commit(); 57 | 58 | Log.d(TAG, " put key : "+name+", value : "+value+" to file : "+mFileName+" result: "+result); 59 | return result; 60 | } 61 | 62 | public boolean putInt(String name, int value) { 63 | mEditor.putInt(name, value); 64 | boolean result = mEditor.commit(); 65 | 66 | Log.d(TAG, " put key : "+name+", value : "+value+" to file : "+mFileName+" result: "+result); 67 | return result; 68 | } 69 | 70 | public boolean putBoolean(String name, Boolean value) { 71 | mEditor.putBoolean(name, value); 72 | boolean result = mEditor.commit(); 73 | 74 | Log.d(TAG, " put key : "+name+", value : "+value+" to file : "+mFileName+" result: "+result); 75 | return result; 76 | } 77 | 78 | public boolean remove(String name) { 79 | mEditor.remove(name); 80 | boolean result = mEditor.commit(); 81 | 82 | Log.d(TAG, " remove key : "+name+" from file : "+mFileName+" result: "+result); 83 | return result; 84 | } 85 | 86 | public boolean clear(){ 87 | mEditor.clear(); 88 | boolean result = mEditor.commit(); 89 | 90 | Log.d(TAG, " clear file : "+mFileName+" result: "+result); 91 | return result; 92 | } 93 | 94 | public long getLong(String key) { 95 | return mPreferences.getLong(key, 0); 96 | } 97 | 98 | public int getInt(String key) { 99 | return mPreferences.getInt(key, 0); 100 | } 101 | 102 | public Boolean getBoolean(String key) { 103 | return mPreferences.getBoolean(key, false); 104 | } 105 | 106 | public String getString(String key) { 107 | return mPreferences.getString(key, ""); 108 | } 109 | 110 | public long getLong(String key, long defValue) { 111 | return mPreferences.getLong(key, defValue); 112 | } 113 | 114 | public int getInt(String key, int defValue) { 115 | return mPreferences.getInt(key, defValue); 116 | } 117 | 118 | public Boolean getBoolean(String key, boolean defValue) { 119 | return mPreferences.getBoolean(key, defValue); 120 | } 121 | 122 | public String getString(String key, String defValue) { 123 | return mPreferences.getString(key, defValue); 124 | } 125 | 126 | public Editor getEditor(){ 127 | return mEditor; 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_action_new.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wensibob/Note/af6ca08b1460f3d35466a445f25a14406e45baf0/app/src/main/res/drawable-hdpi/ic_action_new.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_action_new.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wensibob/Note/af6ca08b1460f3d35466a445f25a14406e45baf0/app/src/main/res/drawable-mdpi/ic_action_new.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_action_new.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wensibob/Note/af6ca08b1460f3d35466a445f25a14406e45baf0/app/src/main/res/drawable-xhdpi/ic_action_new.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_action_new.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wensibob/Note/af6ca08b1460f3d35466a445f25a14406e45baf0/app/src/main/res/drawable-xxhdpi/ic_action_new.png -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 14 | 15 | 23 | 24 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /app/src/main/res/layout/note_content.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 18 | 19 | 23 | 24 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /app/src/main/res/layout/note_row.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14 | 20 | 21 | 22 | 23 | 29 | 30 | 35 | 36 | 40 | 41 | 55 | 56 | 65 | 66 | 67 | 68 | 69 | 70 |