寫(xiě)一個(gè)小便簽

**歡迎訪問(wèn)我的個(gè)人博客轉(zhuǎn)發(fā)請(qǐng)注明出處:http://www.wensibo.top/2017/03/08/寫(xiě)一個(gè)小便簽/ **
一直想要寫(xiě)一個(gè)便簽應(yīng)用,因?yàn)槲乙恢痹谟玫氖清N子便簽和一加便簽,覺(jué)得體驗(yàn)還是可以的,但是始終覺(jué)得自己也是可以做的,這段時(shí)間因?yàn)橛行┦虑榈⒄`了,項(xiàng)目前幾天做好了,一直沒(méi)有時(shí)間上傳到github (各位大爺路過(guò)給個(gè)star唄??) ,今天趁著有時(shí)間順便寫(xiě)了這篇文章,介紹一下寫(xiě)這個(gè)便簽時(shí)遇到的一些問(wèn)題,當(dāng)作是跟大家一起分享吧!

功能

  • 實(shí)現(xiàn)最基本的增加、刪除、修改便簽
  • 便簽?zāi)軌虮4娴奖镜?/li>
  • 主界面采用Material Design設(shè)計(jì)風(fēng)格,相對(duì)美觀(勿噴)
  • Recycle View上下滑動(dòng)可以自動(dòng)隱藏Toolbar,以及Floating Action Button
  • Recycle View的Item可以實(shí)現(xiàn)如QQ的側(cè)滑效果,可以通過(guò)點(diǎn)擊刪除、置頂進(jìn)行編輯
  • 可以設(shè)置便簽為星?,那么將會(huì)在便簽界面左邊增加一個(gè)紅色的標(biāo)志,以提醒用戶此便簽為重要便簽
  • 變遷主界面標(biāo)有時(shí)間,并且按照編輯時(shí)間從新到舊進(jìn)行排列

效果

效果截圖

Talk is cheap,show me your code

如何在RecycleView中使用本地Sqlite數(shù)據(jù)庫(kù)數(shù)據(jù)

RecycleView是google在推出Material Design時(shí)著重介紹的一個(gè)組件,它對(duì)傳統(tǒng)的ListView已經(jīng)可以說(shuō)是完全代替了,功能強(qiáng)大是他的一個(gè)最大優(yōu)點(diǎn),但是有一點(diǎn)局限的就是我們自定義的RecycleView必須繼承重寫(xiě) RecyclerView.Adapter 和 RecyclerView.ViewHolder,雖然在里面我們可以隨意重寫(xiě)方法,但是可以發(fā)現(xiàn)如果我們使用數(shù)據(jù)庫(kù)作為數(shù)據(jù)源,RecyclerView.Adapter是無(wú)法支持讀取Cursor的,但是開(kāi)源的力量又再次顯現(xiàn)了,直接給上github地址,但是我們這里只需要復(fù)用其中的兩個(gè)文件就行了,容我娓娓道來(lái)。

1、添加下面的RecyclerViewCursorAdapter 和 CursorFilter到工程中
由于代碼太長(zhǎng)影響排版,我就直接附上下載鏈接
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() {

    }

    /** 設(shè)置點(diǎn)擊事件 */
    public void setRecyclerViewOnItemClickListener(RecyclerViewOnItemClickListener onItemClickListener) {
        this.mOnItemClickListener = onItemClickListener;
    }

    public RecyclerViewOnItemClickListener getOnItemClickListener() {
        return mOnItemClickListener;
    }

    /** 點(diǎn)擊事件接口 */
    public interface RecyclerViewOnItemClickListener {
        void onItemClickListener(View view, int position);
    }


    /**
     * 內(nèi)部類(lèi)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());

        //設(shè)置點(diǎn)擊事件
        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);
                }

            }
        });

        //設(shè)置適配器
        mRecyclerView.setAdapter(mNoteAdapter);

如何在RecycleView上下滑動(dòng)時(shí)隱藏Toolbar和FAB按鈕

思路很簡(jiǎn)單,只需要記錄RecycleView向下滑動(dòng)(手指向上滑動(dòng))時(shí)移動(dòng)的距離,超過(guò)一定范圍時(shí)就會(huì)調(diào)用Toolbar以及Floating Action Button的animate().translationY方法,令其在Y軸方向上移動(dòng),當(dāng)RecycleView向上滑動(dòng)(手指向下滑動(dòng))時(shí)又會(huì)反過(guò)來(lái)回到初始狀態(tài),并且當(dāng)滑動(dòng)到RecycleView底部時(shí)會(huì)強(qiáng)制Toolbar和FAB回到初始狀態(tài),上代碼。

  • 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 橫向的滾動(dòng)距離
     * @param dy 縱向的滾動(dòng)距離
     *           記錄的是兩個(gè)滾動(dòng)事件之間的偏移量,而不是總的滾動(dòng)距離。
     */
    @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{
            //如果總的滾動(dòng)距離超多了一定值
            // (這個(gè)值取決于你自己的設(shè)定,越大,需要滑動(dòng)的距離越長(zhǎng)才能顯示或者隱藏),
            // 我們就根據(jù)其方向顯示或者隱藏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;
            }
        }
        //計(jì)算出滾動(dòng)的總距離(deltas相加),
        // 但是只在Toolbar隱藏且上滾或者Toolbar未隱藏且下滾的時(shí)候
        if ((controlsVisible && dy > 0) || (!controlsVisible && dy < 0)) {
            scrolledDistance += dy;
        }
    }

    public abstract void onHide();
    public abstract void onShow();
}

在Avtivity中使用回掉方法

//為recycleview設(shè)置滾動(dòng)監(jiān)聽(tīng)器
        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();
        }

特別注意布局文件
如果你發(fā)現(xiàn)你運(yùn)行的效果像下面的截圖一樣的話,那你肯定是因?yàn)椴季治募仙賹?xiě)了這兩句

android:clipToPadding="false"
android:paddingTop="?attr/actionBarSize"

bug截圖

完整的布局代碼如下:

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

最后來(lái)講講如何實(shí)現(xiàn)仿QQ的側(cè)滑出現(xiàn)刪除、指定操作

首先得謝謝張旭童 ,他的一個(gè)庫(kù)幫我解決了這個(gè)問(wèn)題,點(diǎn)擊這里可以訪問(wèn)他的項(xiàng)目。
1、在布局文件中使用com.mcxtzhang.swipemenulib.SwipeMenuLayout布局,在ItemView后添加button表示刪除置頂按鈕。
2、在Adapter中設(shè)置打開(kāi)側(cè)滑菜單,并且可以設(shè)置菜單在左還是在右

((SwipeMenuLayout) holder.root.findViewById(R.id.swipeMenuLayout)).setIos(false).setLeftSwipe(false).setSwipeEnable(true);

3、在Activity中設(shè)置動(dòng)作事件

 mNoteAdapter.setOnSwipeListener(new NoteAdapter.onSwipeListener() {
            @Override
            public void onDel(int pos) {
                Toast.makeText(NoteActivity.this, "點(diǎn)擊了第" + (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, "點(diǎn)擊了第" + (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);
            }
        });

大功告成,如果想要看詳細(xì)代碼,或者有什么建議可以到Github上給我發(fā)Issue或者直接在站內(nèi)給我留言哦,記得star哦

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,355評(píng)論 25 708
  • 內(nèi)容抽屜菜單ListViewWebViewSwitchButton按鈕點(diǎn)贊按鈕進(jìn)度條TabLayout圖標(biāo)下拉刷新...
    皇小弟閱讀 47,185評(píng)論 22 665
  • 有人看破紅塵 更多的是在紅塵里飄有的人 而僅有為數(shù)不多的幾個(gè)看透紅塵走上心靈之旅 心靈之旅的最終產(chǎn)物就是孤獨(dú) 人本...
    錢(qián)老師碎碎念閱讀 160評(píng)論 0 2
  • 今天林丹出界,劉愷威終于松了口氣。 不少姑娘都曾面臨謝杏芳一樣的痛楚。 收集了15句真摯動(dòng)容的內(nèi)心獨(dú)白: “我以為...
    Jukie閱讀 3,046評(píng)論 4 1
  • 半夜里睡不著,看著窗外的夜空,晴朗的夜幕上點(diǎn)點(diǎn)星光透過(guò)片片云波似乎在訴說(shuō)著什么,那偶爾經(jīng)過(guò)的飛機(jī)如同流星般從...
    流光蘇影閱讀 295評(píng)論 0 4

友情鏈接更多精彩內(nèi)容