美文网首页Android专题
android便签实现(仿小米便签,添加了一些新功能)

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

作者: 小星star | 来源:发表于2019-01-28 17:16 被阅读23次

    一、完成效果

    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

    相关文章

      网友评论

        本文标题:android便签实现(仿小米便签,添加了一些新功能)

        本文链接:https://www.haomeiwen.com/subject/kjbjjqtx.html