**歡迎訪問我的個人博客轉發請注明出處:http://www.wensibo.top/2017/03/08/寫一個小便簽/ **
一直想要寫一個便簽應用,因為我一直在用的是錘子便簽和一加便簽,覺得體驗還是可以的,但是始終覺得自己也是可以做的,這段時間因為有些事情耽誤了,項目前幾天做好了,一直沒有時間上傳到github (各位大爺路過給個star唄😉) ,今天趁着有時間順便寫了這篇文章,介紹一下寫這個便簽時遇到的一些問題,當作是跟大家一起分享吧!
功能
- 實現最基本的增加、刪除、修改便簽
- 便簽能夠保存到本地
- 主界面采用Material Design設計風格,相對美觀(勿噴)
- Recycle View上下滑動可以自動隱藏Toolbar,以及Floating Action Button
- Recycle View的Item可以實現如QQ的側滑效果,可以通過點擊刪除、置頂進行編輯
- 可以設置便簽為星✨,那么將會在便簽界面左邊增加一個紅色的標志,以提醒用戶此便簽為重要便簽
- 變遷主界面標有時間,並且按照編輯時間從新到舊進行排列
效果
Talk is cheap,show me your code
如何在RecycleView中使用本地Sqlite數據庫數據
RecycleView是google在推出Material Design時着重介紹的一個組件,它對傳統的ListView已經可以說是完全代替了,功能強大是他的一個最大優點,但是有一點局限的就是我們自定義的RecycleView必須繼承重寫 RecyclerView.Adapter 和 RecyclerView.ViewHolder,雖然在里面我們可以隨意重寫方法,但是可以發現如果我們使用數據庫作為數據源,RecyclerView.Adapter是無法支持讀取Cursor的,但是開源的力量又再次顯現了,直接給上github地址,但是我們這里只需要復用其中的兩個文件就行了,容我娓娓道來。
1、添加下面的RecyclerViewCursorAdapter 和 CursorFilter到工程中
由於代碼太長影響排版,我就直接附上下載鏈接
RecyclerViewCursorAdapter
CursorFilter
2、新建自定義的Adapter並且繼承RecyclerViewCursorAdapter
- NoteAdapter
public class NoteAdapter extends RecyclerViewCursorAdapter<NoteAdapter.MyNoteViewHolder> {
private Context mContext;
private RecyclerViewOnItemClickListener mOnItemClickListener;
private onSwipeListener mOnSwipeListener;
public NoteAdapter(Context context,Cursor cursor,int flags) {
super(context,cursor,flags);
this.mContext = context;
}
@Override
public MyNoteViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View root = LayoutInflater.from(parent.getContext()).inflate(R.layout.note_row, parent, false);
MyNoteViewHolder holder = new MyNoteViewHolder(root);
return holder;
}
@Override
public void onBindViewHolder(final MyNoteViewHolder holder, Cursor cursor) {
int position = cursor.getPosition();
holder.tv.setText(cursor.getString(cursor.getColumnIndex(NoteDbAdapter.COL_CONTENT)));
holder.tv_dateTime.setText(cursor.getString(cursor.getColumnIndex(NoteDbAdapter.COL_DATETIME)));
holder.mRowtab.setBackgroundColor(cursor.getInt(cursor.getColumnIndex(NoteDbAdapter.COL_IMPORTANT)) == 1?
mContext.getResources().getColor(R.color.colorAccent):mContext.getResources().getColor(android.R.color.white)
);
holder.root.setTag(position);
holder.tv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (mOnItemClickListener != null) {
mOnItemClickListener.onItemClickListener(view, holder.getAdapterPosition());
}
}
});
}
@Override
protected void onContentChanged() {
}
/** 設置點擊事件 */
public void setRecyclerViewOnItemClickListener(RecyclerViewOnItemClickListener onItemClickListener) {
this.mOnItemClickListener = onItemClickListener;
}
public RecyclerViewOnItemClickListener getOnItemClickListener() {
return mOnItemClickListener;
}
/** 點擊事件接口 */
public interface RecyclerViewOnItemClickListener {
void onItemClickListener(View view, int position);
}
/**
* 內部類Holder
*/
class MyNoteViewHolder extends RecyclerView.ViewHolder {
private TextView tv;
private TextView tv_dateTime;
private View mRowtab;
private Button btnTop;
private Button btnDelete;
private View root;
public MyNoteViewHolder(View root) {
super(root);
this.root = root;
tv = (TextView) root.findViewById(R.id.row_text);
tv_dateTime = (TextView) root.findViewById(R.id.tv_note_time);
mRowtab = root.findViewById(R.id.row_tab);
btnTop = (Button) root.findViewById(R.id.btnTop);
btnDelete = (Button) root.findViewById(R.id.btnDelete);
}
}
}
3、在Activity中這樣用
mRecyclerView = (RecyclerView) findViewById(R.id.recycle_notes);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
mCursor = mNoteDbAdapter.fetchAllNotes();
mNoteAdapter = new NoteAdapter(this, mCursor, 0);
Log.d(TAG, "mCursor的大小為:" + mCursor.getCount());
//設置點擊事件
mNoteAdapter.setRecyclerViewOnItemClickListener(new NoteAdapter.RecyclerViewOnItemClickListener() {
@Override
public void onItemClickListener(View view, int position) {
if (mCursor == null || mCursor.isClosed()) {
if (mCursor == null) {
Log.d("NoteActivity", "newCursor is null");
Toast.makeText(NoteActivity.this, "newCursor is null", Toast.LENGTH_SHORT).show();
} else if (mCursor.isClosed()){
Log.d("NoteActivity", "newCursor is closed");
Toast.makeText(NoteActivity.this, "newCursor is null", Toast.LENGTH_SHORT).show();
}
} else {
mCursor.moveToPosition(position);
String content = mCursor.getString(mCursor.getColumnIndex(NoteDbAdapter.COL_CONTENT));
int importtant = mCursor.getInt(mCursor.getColumnIndex(NoteDbAdapter.COL_IMPORTANT));
int id = mCursor.getInt(mCursor.getColumnIndex(NoteDbAdapter.COL_ID));
Log.d("NoteActivity", content + importtant);
Note clickNote = new Note(id, content, importtant);
Intent intent = new Intent();
intent.setClass(NoteActivity.this, NoteContentActivity.class);
Bundle bundle = new Bundle();
bundle.putSerializable("note", clickNote);
intent.putExtras(bundle);
startActivity(intent);
}
}
});
//設置適配器
mRecyclerView.setAdapter(mNoteAdapter);
如何在RecycleView上下滑動時隱藏Toolbar和FAB按鈕
思路很簡單,只需要記錄RecycleView向下滑動(手指向上滑動)時移動的距離,超過一定范圍時就會調用Toolbar以及Floating Action Button的animate().translationY方法,令其在Y軸方向上移動,當RecycleView向上滑動(手指向下滑動)時又會反過來回到初始狀態,並且當滑動到RecycleView底部時會強制Toolbar和FAB回到初始狀態,上代碼。
- HidingScrollListener
public abstract class HidingScrollListener extends RecyclerView.OnScrollListener {
private static final int HIDE_THRESHOLD = 20;
private int scrolledDistance = 0;
private boolean controlsVisible = true;
private int mItemSize=0;
public HidingScrollListener(int itemSize) {
this.mItemSize = itemSize - 1;
}
/**
*
* @param recyclerView
* @param dx 橫向的滾動距離
* @param dy 縱向的滾動距離
* 記錄的是兩個滾動事件之間的偏移量,而不是總的滾動距離。
*/
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
int firstVisibleItem = ((LinearLayoutManager) recyclerView.getLayoutManager()).findFirstVisibleItemPosition();
int lastVisibleItem = ((LinearLayoutManager) recyclerView.getLayoutManager()).findLastVisibleItemPosition();
if (firstVisibleItem == 0||lastVisibleItem==mItemSize) {
if (!controlsVisible) {
onShow();
controlsVisible = true;
}
}else{
//如果總的滾動距離超多了一定值
// (這個值取決於你自己的設定,越大,需要滑動的距離越長才能顯示或者隱藏),
// 我們就根據其方向顯示或者隱藏Toolbar(dy>0意味着下滾,dy<0意味着上滾)。
if (scrolledDistance > HIDE_THRESHOLD && controlsVisible) {
onHide();
controlsVisible = false;
scrolledDistance = 0;
} else if (scrolledDistance < -HIDE_THRESHOLD && !controlsVisible) {
onShow();
scrolledDistance = 0;
controlsVisible = true;
}
}
//計算出滾動的總距離(deltas相加),
// 但是只在Toolbar隱藏且上滾或者Toolbar未隱藏且下滾的時候
if ((controlsVisible && dy > 0) || (!controlsVisible && dy < 0)) {
scrolledDistance += dy;
}
}
public abstract void onHide();
public abstract void onShow();
}
在Avtivity中使用回掉方法
//為recycleview設置滾動監聽器
mRecyclerView.setOnScrollListener(new HidingScrollListener(mCursor.getCount()) {
@Override
public void onHide() {
hideView();
}
@Override
public void onShow() {
showView();
}
});
private void hideView() {
mToolbar.animate().translationY(
-mToolbar.getHeight()).setInterpolator(new AccelerateInterpolator(2));
FrameLayout.LayoutParams ip = (FrameLayout.LayoutParams) mFloatingActionButton.getLayoutParams();
int fabButtonMargin = ip.bottomMargin;
mFloatingActionButton.animate().translationY(
mFloatingActionButton.getHeight() + fabButtonMargin).setInterpolator(new AccelerateInterpolator(2)).start();
}
private void showView() {
mToolbar.animate().translationY(0).setInterpolator(new DecelerateInterpolator(2));
mFloatingActionButton.animate().translationY(0).setInterpolator(new DecelerateInterpolator(2)).start();
}
特別注意布局文件
如果你發現你運行的效果像下面的截圖一樣的話,那你肯定是因為布局文件上少寫了這兩句
android:clipToPadding="false"
android:paddingTop="?attr/actionBarSize"
完整的布局代碼如下:
- main_activity.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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="match_parent">
<android.support.v7.widget.RecyclerView
android:id="@+id/recycle_notes"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:paddingTop="?attr/actionBarSize"
/>
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:clipToPadding="false"
app:titleTextColor="@android:color/white"
/>
<android.support.design.widget.FloatingActionButton
android:id="@+id/button_add_note"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
android:layout_marginBottom="16dp"
android:layout_marginRight="16dp"
android:src="@drawable/ic_action_new"
android:elevation="15dp"
app:fabSize="normal"
app:pressedTranslationZ="8dp"
app:rippleColor="#ff87eb"
/>
</FrameLayout>
最后來講講如何實現仿QQ的側滑出現刪除、指定操作
首先得謝謝張旭童 ,他的一個庫幫我解決了這個問題,點擊這里可以訪問他的項目。
1、在布局文件中使用com.mcxtzhang.swipemenulib.SwipeMenuLayout
布局,在ItemView后添加button表示刪除置頂按鈕。
2、在Adapter中設置打開側滑菜單,並且可以設置菜單在左還是在右
((SwipeMenuLayout) holder.root.findViewById(R.id.swipeMenuLayout)).setIos(false).setLeftSwipe(false).setSwipeEnable(true);
3、在Activity中設置動作事件
mNoteAdapter.setOnSwipeListener(new NoteAdapter.onSwipeListener() {
@Override
public void onDel(int pos) {
Toast.makeText(NoteActivity.this, "點擊了第" + (pos+1) + "條item的刪除按鈕", Toast.LENGTH_SHORT).show();
mCursor.moveToPosition(pos);
int id = mCursor.getInt(mCursor.getColumnIndex(NoteDbAdapter.COL_ID));
mNoteDbAdapter.deleteNoteById(id);
mCursor = mNoteDbAdapter.fetchAllNotes();
mNoteAdapter.changeCursor(mCursor);
}
@Override
public void onTop(int pos) {
Toast.makeText(NoteActivity.this, "點擊了第" + (pos+1) + "條item的Top按鈕", Toast.LENGTH_SHORT).show();
mCursor.moveToPosition(pos);
int id = mCursor.getInt(mCursor.getColumnIndex(NoteDbAdapter.COL_ID));
Note editNote = mNoteDbAdapter.fetchNoteById(id);
editNote.setDateTime(DateUtil.formatDateTime());
mNoteDbAdapter.updateNote(editNote);
mCursor = mNoteDbAdapter.fetchAllNotes();
mNoteAdapter.changeCursor(mCursor);
}
});
大功告成,如果想要看詳細代碼,或者有什么建議可以到Github上給我發Issue或者直接在站內給我留言哦,記得star哦