android便签实现(仿小米便签,添加了一些新功能)

一、完成效果

GitHub地址:星便签

  1. 主界面


    星便签
  2. 侧栏菜单


    侧栏菜单
  1. 基本功能(还有语音等)


    便签功能

二、实现:
明天再写吧,leileleile😂
实现功能:

功能 实现方法
界面展示 Android基础控件学习,DrawerLayout整体抽屉,RecyclerView + Adapter显示便签
便签数据存储 存到Sqlite,同时新建一个业务类完成对数据库操作的封装
分享 新建Intent跳转到系统自带的活动
提醒 闹钟组件 + Service
搜索 重写SearchView中的方法
图片和 语音 SpannableString + 正则表达式 实现存储和展示

挺简单的,主要是我对android编程完全不了解,所以花费了不少的时间。

以下步骤是我自己认为的,不知道对不对,毕竟开发经验很少

  1. 确定大概的需求,要实现哪些功能,哪些是必须的,那些是实在不会就算了的(这里显然能写便签是必须的,至于图片啥的,能实现就实现)、大概的UI,软件长啥样,使用的过程,可以使用一些开发软件帮助画出来,这样布局文件 和 活动脉络就容易理清。
  2. 第一次开发最好借鉴一下别人的,稍微看一下也行。完成 数据库的设计,便签的Id,内容,组别,等等想好。
  3. 开始写每一个布局,每一个功能,写的时候建议 准备在手上 新技术原理介绍 + 新技术实现例子 + 新技术的坑

好了,下面是文件的结构

文件结构

遇到的困难

  1. 图片的添加 与 语音的添加
    刚开始在纠结要不要把图片存在数据库,后来想了下如果要把图片存在数据库可能造成一些问题,存图片路径的话也需要,在展示图片的时候进行还原,也就意味着,纪录图片的同时还需要纪录它在文档中的位置。 所以最终选择了 SpannableString来实现。 SpanStr可以达到富文本的效果,只不过我们需要把富文本的标签用正则表达式提取出来,加工一下就可以了。这样存在一个便签的内容存在数据库中还是一个整体,而不会被分割成图片 + 文字 + 声音,在textView和editText中都能正确显示SpanStr。
    但是如果你仅仅是加了图片的话,Html.formHtml()然后重写ImageGetter()会更加方便的(他的原理是当遇到<img >就会回调这个方法,加载图片)。
    这里展示一下将String Note.content 转化成 我自己定义的 标签
    <img src=''/> <voice src=''/>的代码。
    public class ContentToSpannableString {
    
    
    public static SpannableString Content2SpanStr(Context context, String noteContent){
        //这里的fakeNoteContent 是虚假content,是展示给用户的,因为真正的content中包含着的声音src变为可点击spannable之后会很丑
        String fakeNoteContent = noteContent;
        ArrayList<String> voiceSrc = new ArrayList<>();
        Pattern voice = Pattern.compile("<voice src='(.*?)'/>");
        Matcher mVoice = voice.matcher(noteContent);
        while(mVoice.find()){
            String str1 = mVoice.group(0);
            fakeNoteContent = noteContent.replace(str1,"");
            String str2 = mVoice.group(1);
            voiceSrc.add(str2);
        }
    
        Log.d("voiceSrc的大小",Integer.toString(voiceSrc.size()));
    
        Pattern img = Pattern.compile("<img src='(.*?)'/>");
        Matcher mImg = img.matcher(fakeNoteContent);
    
        // "\uD83C\uDFA4", 这是android手机的emoji录音图标
        Pattern voiceLogo = Pattern.compile("\uD83C\uDFA4");
        Matcher mVoiceLogo = voiceLogo.matcher(fakeNoteContent);
    
        SpannableString spanStr = new SpannableString(fakeNoteContent);
    
        while(mImg.find()){
            String str = mImg.group(0);
            int start = mImg.start();   int end = mImg.end();
            Uri imgUri = Uri.parse(mImg.group(1));
            Drawable drawable = null;
            try {
                drawable = Drawable.createFromStream(context.getContentResolver().openInputStream(imgUri),null);
                drawable.setBounds(0,0,2 * drawable.getIntrinsicWidth(),2 * drawable.getIntrinsicHeight());
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
            ImageSpan imageSpan = new ImageSpan(drawable);
    
            spanStr.setSpan(imageSpan,start,end,Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
        }
    
        int i = 0;
        while(mVoiceLogo.find()){
    
            Log.d("下标i",Integer.toString(i));
            int start = mVoiceLogo.start();     int end = mVoiceLogo.end();
            final String voiceFilePath = voiceSrc.get(i);
            i++;
    
            //可点击的SpannableString
            ClickableSpan clickableSpan = new ClickableSpan() {
                @Override
                public void onClick(View view) {
                    //实现点击事件
                    Log.d("voice能否点击","能够点击");
                    MediaPlayer mp = new MediaPlayer();
                    try {
                        mp.setDataSource(voiceFilePath);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    //mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
                    mp.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
                        @Override
                        public void onCompletion(MediaPlayer mediaPlayer) {
                            if(mediaPlayer != null){
                                mediaPlayer.stop();
                                mediaPlayer.release();
                                mediaPlayer = null;
                            }
    
                        }
                    });
    
                    try {
                        mp.prepare();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    mp.start();
                    //etc
                }
            };
            spanStr.setSpan(clickableSpan,start,end,Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
    
        }
    
        return spanStr;
    }
    
    
    }
    
    
  2. 分享到 QQ,微信点了没反应,别慌,这是正常的,因为你需要去注册一下
  3. RecyclerView + CardView ( item布局是cardView)
    RecyclerView的三种LayoutManager
    瀑布会表达出杂乱,碰撞 但是 个体清晰的感觉
    线性 工整
    宫格则是整齐
  4. 用SpiderMan来实现奔溃信息的展示,查看自己的代码哪里出现了问题
    效果如下:


    奔溃界面
```
SpiderMan.getInstance()
            .init(this)
            //设置是否捕获异常,不弹出崩溃框
            .setEnable(true)
            //设置是否显示崩溃信息展示页面
            .showCrashMessage(true)
            //是否回调异常信息,友盟等第三方崩溃信息收集平台会用到,
            .setOnCrashListener(new SpiderMan.OnCrashListener() {
                @Override
                public void onCrash(Thread t, Throwable ex, CrashModel model) {
                    //CrashModel 崩溃信息记录,包含设备信息
                }
            });
```
  1. 在用户添加照片的时候使用到了知乎的Matisse库(图片选择器),Matisse配合Gilde来展示图片的时候,要写一个类实现ImageEngine,然后花了一下午在一个Matisse的bug,醉了,这也提醒我们,在使用第三库的时候要小心,有时候并不是我们代码的问题。

  2. 控制 RecyclerView 中 item的间距的时候
    新建一个类SpacesItemDecoration

    public class SpacesItemDecoration extends RecyclerView.ItemDecoration {
    
        private int space;
    
        public SpacesItemDecoration(int space) {
            this.space = space;
        }
    
        @Override
        public void getItemOffsets(Rect outRect, View view,
                               RecyclerView parent, RecyclerView.State state) {
            outRect.left = space;
            outRect.right = space;
            outRect.bottom = space;
    
            // Add top margin only for the first item to avoid double space between items
            if (parent.getChildPosition(view) == 0)
                outRect.top = space;
        }
    }
    

    然后

     //3是设置的间距
     recyclerView.addItemDecoration(new SpacesItemDecoration(3));
    
  3. 6.0之后需要动态申请权限,请务必小心

  4. 方便的工具类 将 Uri转化为文件的绝对路径

  5. webView

  6. Android中的请求码与结果码
    为什么要在每个activity中都要建立一个TAG?
    这是为了能够方便辨认是从哪一个activity传来的intent


    image.png
  7. textView自动获得焦点问题,并且自动弹出软键盘

  8. fab背景色 颜色叠加

  9. edittext去掉下划线,editText光标颜色

  10. recycleView一页只显示一个item

  11. 回调 委托

  12. Serializable接口是启用其序列化功能的接口

  13. 数据库单例模式,防止生成多个数据库,
    onCreate(database):首次使用软件时生成数据库表

  14. recycleView 的使用 设置间距 设置显示方向

  15. 深刻理解interface

  16. android:fitsSystemWindows="true"

  17. android:windowSoftInputMode="adjustResize|stateVisible"

  18. accentColor

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,142评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,298评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,068评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,081评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,099评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,071评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,990评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,832评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,274评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,488评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,649评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,378评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,979评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,625评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,643评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,545评论 2 352

推荐阅读更多精彩内容