jethro's Blog - Android
type: channel
link: https://jethro.fun/index.php/category/Android/
description: android开发学习记录
items: EditView+SpannableString+ImageSpan实现图文混编
items: Android快速实现倒计时按钮
items: recyclerView多层嵌套最里层内容把外层内容挤出
items: LitePal数据库框架的简单介绍及使用
items: 论如何优雅的使用ExpandableListView（二）
items: 论如何优雅的使用ExpandableListView
items: BottomNavigationView 进阶使用之切换Fragment
items: BottomNavigationView 的简单使用
items: 一行代码解决RecyclerView在ScrollView中滑动惯性失效
items: 解决 Android Studio 新建项目卡死（超详细图文教程）
items: android6.0运行时权限的使用与封装
items: 使用menu菜单
items: TabLayout与ViewPager的辛酸往事
items: 使用openFileInput和openFileOutput读写文本文件

EditView+SpannableString+ImageSpan实现图文混编
type: item
date: 2019-03-10T14:30:00+08:00
description: 网上有很多图文混编的方案,无非两种webviewSpannableString+ImageSpan他们之间的优势比较什么的,网上很多,我也不再赘述,我这里直接选择第二种,因为简单,需求也只是可以插入图片.然后各种搜索如何实现,找到一篇博文https://blog.csdn.net/ljzdyh/article/details/82497625我的代码就是参考这篇文章来的,因为文章中有些地方使用的api已经被标记为弃用了,所以只好改一下,加点注释,试图让它更加通俗易懂.那么先上第一段代码/** * 图片上传到本地磁盘成功 * * @param path 所在本地磁盘路径 */ private void imgUploadSuccess(final String path) { Glide.with(this) .load(path) .into(new CustomTarget<Drawable>() { @Override public void onResourceReady(@NonNull Drawable resource, @Nullable Transition<? super Drawable> transition) { image(resource, path); } @Override public void onLoadCleared(@Nullable Drawable placeholder) { } }); } 这个方法是我选择图片后的回调, String path是图片储存的路径,我给放sd卡了.路径类似于/storage/emulated/0/Android/data/com.xxx.xxx/files/imgs/strategy3121064518359412361.png这里选择用 Glide 加载管理图片,以防止 OOM ,这里 Glide 的版本是 4.9.0 ,所以回调跟 glide3 的不一样,没了 .asBitmap 用的是 CustomTarget 回调.图片准备好以后会回调 onResourceReady ,我们在这里调用 image(resource, path);这个方法是重点,看下一段代码/** * 把图片插入到 EditView * @param resource 图片资源 * @param path 图片所在位置 可以是url 也可是本地储存路径,我这里是放在本地了 */ private void image(Drawable resource, String path) { String img_str = String.format("![插图](%s)", path);// 插入markdown格式的插图 imgReSize(resource);// 缩放图片的宽度到EditView的宽度 int start = etContent.getSelectionStart();// 获取当前光标所在位置 // 创建一个SpannableString对象,前后都写上换行,然后把 img_str 给放进去 SpannableString ss = new SpannableString("\n\n" + img_str + "\n\n"); // 新建 ImageSpan 把图片传进去 ImageSpan imageSpan = new ImageSpan(resource); // 这一行的意思是给SpannableString对象修饰一下,用刚才创建的 ImageSpan 修饰, // 第二个参数是从那个位置开始,ss 前面有两个换行符,换行符不要修饰,所以跳过换行符,从下标2开始, // 换行符虽然写的\n,但是只算一个字符.第三个参数之所以要+2是因为前面第二个参数往后挪了两位, // 后面也要挪两位,不要就会把 img_str 的最后两个字符串给丢掉,导致图片后面会有`g)`这种字符串, // 一般图片的后缀是 png jpg 所以最后的两个字就是 g),这两个字一定要写 img_str.length() + 2 ss.setSpan(imageSpan, 2, img_str.length() + 2, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); // 最后把 SpannableString 对象给插入到我们的EditText中,start就是当前光标所在位置 // 或者说光标最后所在位置 etContent.getEditableText().insert(start, ss);// 设置ss要添加的位置 // 最后更新一下view 请求一下焦点.完事 etContent.requestLayout(); etContent.re…

Android快速实现倒计时按钮
type: item
date: 2018-08-11T14:37:00+08:00
description: 一开始想过用子线程来实现,还好搜索了一下,发现 Android 自带了一个倒计时类,正好适合,这个类就是CountDownTimer,创建它需要提供两个参数,一个是总计时时间(单位是毫秒),一个是步长,也就是一次走多少毫秒.然后还需要实现它的两个接口. void onTick(long millisUntilFinished)当时间每走一次就会调用这个方法,参数是还剩下多少时间没走,在这里禁用按钮,然后显示还剩多少时间void onFinish()这个一看就知道是倒计时完成了,会回调它,可以在这里启用按钮上示例代码//倒计时60秒,这里不直接写60000,而用1000*60是因为后者看起来更直观,每走一步是1000毫秒也就是1秒 CountDownTimer timer = new CountDownTimer(1000 * 60, 1000) { @SuppressLint("DefaultLocale") @Override public void onTick(long millisUntilFinished) { button.setEnabled(false); button.setText(String.format("已发送(%d)",millisUntilFinished/1000)); } @Override public void onFinish() { button.setEnabled(true); button.setText("重新获取"); } }; timer.start();最后一定要记得 start()

recyclerView多层嵌套最里层内容把外层内容挤出
type: item
date: 2018-08-09T20:59:00+08:00
description: 在渲染订单列表的时候,用到了三层recyclerView嵌套,结果进入订单列表页,当最里层的数据一个屏幕装不下的时候,就会把外层的view挤出去.比如店铺名.后来查各种资料发现是焦点问题:当新出现一个recyclerView的时候,这个recyclerView就会获得焦点,它的第一条数据就会出现在屏幕顶部,解决方案是:<LinearLayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="wrap_content"> <View android:focusable="true" android:focusableInTouchMode="true" android:layout_width="match_parent" android:layout_height="4dp"/> <android.support.v7.widget.RecyclerView android:id="@+id/rv_goods_cart" android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout>在最外层的RecyclerView前面新建一个View,然后给他焦点,这样的话,进入订单页初次渲染的时候,最外层的recyclerView就不会被挤出去了.

LitePal数据库框架的简单介绍及使用
type: item
date: 2017-06-07T22:32:00+08:00
description: 相信我，当你用上 LitePal 之后，你将再也不想去碰 SQLiteDatabase 了。LitePal是一款开源的 Android 数据库框架，它采用了对象关系映射（ORM）的模式，并将我们平时开发最常用到的一些数据库功能进行了封装，使得不用编写一行SQL语句就可以完成各种建表和增删改查的操作。LitePal 的项目主页上也有详细的使用文档，地址是https://github.com/LitePalFramework/LitePalOK，如果看不懂，没关系，总之它很好用就是了首先，新建一个项目 LitePalTest ，然后开启 LitePal 之旅，车速有点慢哈，耐心看下去。如果你看过郭霖大神的《第一行代码》第二版，可能就没必要看下去了，因为内容基本一致。1. 配置 LitePal首先，修改项目的 app/build.gradle 文件，在 dependencies 闭包中申明LitePal库的引用compile 'org.litepal.android:core:1.5.1'开发者肯定会对框架的更新的，所以下载最新版的话请前往项目主页查看https://github.com/LitePalFramework/LitePal接下来需要配置 litepal.xml 文件。右击 app/src/main 目录 ——>New——>Directory，创建一个 assets 目录然后在 assets 目录下新建一个 litepal.xml 文件，接着编辑该文件，如下所示最后还需要修改一下 AndroidManifest.xml 中的代码加上我选中的哪一行，只有加上这个才能让 LitePal 的所有功能正常运行，关于 application 的作用，还请看官自行 Google 。现在 Litepal 的配置工作已经做完了，让我们开始正式使用它吧！2. 创建和升级数据库先分析一下数据表，需要哪些数据，我们是一个书店应用，呢么它是不是应该存储书的数据，那么一本书就是一条数据，这条数据有书名、书的作者、书的价格、书的页数，还有数据的ID这些字段LitePal 是采取的对象关系映射（ORM），我们的编程语言是面向对象的，而使用的数据库则是关系型数据库，那么将面向对象的语言和面向关系的数据库之间建立一种映射关系，这就是对象关系映射了，所以我们可以用面向对象的思维来操作数据库了。所以根据刚才的分析结果，我们来定义一个 Book 类，一定要继承 DataSupport 类，代码如下所示：package com.example.litepaltest; import org.litepal.crud.DataSupport; /** * Created by jethro on 2017/6/8. */ public class Book extends DataSupport{ private int id ; private String name; //书名 private double price; //价格 private int pages; //页数 private String author; //作者 public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public double getPrice() { return price; } public void setPrice(double price) { this.price = price; } public int getPages() { return pages; } public void setPages(int pages) { this.pages = pages; } public String getAuthor() { return author; } public void setAuthor(String author) { this.author = author; } } 这是一个典型的 Java Bean ，在 Book 类中我们定义了 id、name、price、pages、author 这几个字段，并且生成了相对应的 getter 和 setter 方法，不会生成请自行谷歌 “ Android studio 生成getter and setter ”接着我们修改一下 litepal.xml 的代码，如下所示这里使用<mapping>标签来申明我们要…

论如何优雅的使用ExpandableListView（二）
type: item
date: 2017-06-05T11:59:00+08:00
description: hello，又见面了，前面一篇文章我说的，蚊子会变成灰机，那么现在如何用大炮去打这个灰机呢？前文链接：论如何优雅的使用ExpandableListView因为需求的增加，我们需要显示头像，并且要在点击人员后进入人员信息界面。那么在之前的代码基础上就可以很简单轻松的办到。那么这篇文章会讨论如何去加载头像，以及实现child的点击事件，那么先加载咱们的头像吧1. 加载头像这个实现起来是真的很简单，我们只需要简单的修改一下bean对象以及item布局和适配器然后在数据初始化哪里把头像参数写进去就OK1.1 准备要显示的头像因为是demo，然后作者比较懒，于是只准备了一张头像，让所有人员都加载那一张头像。so，当当的，头像在此1.2 修改bean数据找到存姓名数据的那个ItemBean，修改之，新增一个变量存头像数据1.3 修改item布局关于布局就没什么好说的了，就是个水平布局，然后新增了一个ImageView用来显示头像1.4 修改适配器就先找到MyExAdapter适配器，然后修改它的getChildView方法，在这个方法里把数据填充到布局中1.5 在数据初始化哪里填充头像数据找到我们创建的initData方法，修改一下1.6 编译运行一下看看完美，成功显示头像，OK，接下来就是监听点击事件2. 监听child的点击事件因为我懒的关系，就不新建Activity了，也不为人员信息再新增简述信息了，就弹出一个Toast，提示用户点击了谁这里最常用的就是两个指针和parent了，指针帮助我们定位用户点击了那个分类的那条信息，parent为我们提供Context上下文环境，我们打开新的页面，或者显示提示框，或者就像我这样一个Toast都需要Context参数所以我们的代码就是利用了这个指针从childList取出相应的人员bean，其实这一块我在前面一章有写过，所以应该不难理解。在Toast.makdeText()方法里也需要Context，所以我写的parent.getContext()。2.1 编译运行看看看，当蚊子变成灰机的时候，我们的大炮还是可以应付自如哈

论如何优雅的使用ExpandableListView
type: item
date: 2017-05-25T17:19:00+08:00
description: ExpandableListView 是一个类似于QQ联系人那样的分组的 listView效果如图，这是简单实现，复杂点的话完全可以添加图标头像什么的，为什么说优雅呢？因为我要用的时候发现其他的博客有的是simple适配器，有的是匿名内部类，等等，这实现起来固然快，但是后期扩展比较麻烦，维护也麻烦，点都不清真。我的代码显示这么简单的内容会显得有点多，但是我的目的绝不是说就只是为了告诉看官怎么实现二级list，我还希望告诉看官怎样让ExpandableListView具有扩展性，并且在需求增加的时候只需要做简单的修改就行可以满足增加的需求，所以还请有耐心的看下去1. 准备工作我们的代码量基本就集中在准备工作了，到真正调用的时候就几行代码就搞定了。1.1 创建布局文件首先需要在 layout 目录下创建两个布局文件，分别是分组的组布局和分组下面的数据的布局，也就是截图中的显示销售、主管、售后的布局和显示张三、王麻子的布局。现在看看我创建的布局item_group_exlist.xml·······这个就是显示部门的布局item_exlist.xml················这个是显示员工的布局item_group_exlist.xml<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="wrap_content"> <TextView android:paddingTop="8dp" android:paddingBottom="8dp" android:paddingLeft="36dp" android:textSize="20sp" android:textColor="#000000" android:id="@+id/view_title" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout>布局中的 paddingLeft 是为了防止字体和左边的图标重合。item_exlist.xml<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:paddingTop="8dp" android:paddingBottom="8dp" android:paddingLeft="36dp" android:textColor="#000000" android:id="@+id/view_content" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout>OK，这里布局就创建好了，以后需要添加图标什么的也是在这个布局里添加1.2 创建Bean文件javaBean就是一个数据对象，用来装载数据，可以set和get这里需要创建两个Java类。GroupBean.java······这个是装部门数据的，也就是父级数据ItemBean.java···· ····这个是装人员数据的，也就是子级数据GroupBean.javapackage cc.jethro.expandablelisttest; /** * 部门数据bean * Created by jethro on 2017/5/25. */ public class GroupBean { private String title ; //部门名称 /** * @param title 部门名称 */ GroupBean(String title) { this.title = title; } //获取部门名称 public String getTitle() { return title; } //设置部门名称 public void setTitle(Strin…

BottomNavigationView 进阶使用之切换Fragment
type: item
date: 2017-05-19T16:54:00+08:00
description: 文章里的代码是在简单使用的基础上写的，没看过或者没看的，点击http://jethro.cc/index.php/archives/70/查看效果图这篇文章不会详细讲解 fragment，看不懂的话，建议搜索查看 fragment 的知识0X01 建立要显示的 fragment首先，把 Fragment 和 Fragment 的布局建立好。新建类和布局什么的过于基础了，就不详细写了，这里贴上代码文件的结构以及我Fragment和相应的布局的代码，这里只贴主页的，反正其他的跟这个代码差不多fragment_home.xml<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:id="@+id/text" android:background="#FF0000" android:text="主页" android:textSize="30sp" android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout>HomeActivity.javapackage cc.jethro.bottomnavigationviewtest; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; /** * 主页 fragment * fragment 就相当于一个 Actively * Created by jethro on 2017/5/19. */ public class HomeFragment extends Fragment { @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { //加载 fragment 的布局文件 View view = inflater.inflate(R.layout.fragment_home, container,false); return view; } } 1X01 让 Fragment 显示出来大概思路是为 Fragment 提供一个容器 FragmentLayout，然后在 JAVA 代码里使用 BottomNavigateView 控制让某个 Fragment 显示在容器上面。（不理解的可以把 FragmentLayout 理解为ImageView，把 fragment 理解为图片）01x1 在 Activity 的布局中添加 FragmentLayoutactivity_main.xml<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:orientation="vertical" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="cc.jethro.bottomnavigationviewtest.MainActivity"> <FrameLayout android:id="@+id/content_view" android:layout_width="match_parent" and…

BottomNavigationView 的简单使用
type: item
date: 2017-05-19T13:57:00+08:00
description: BottomNavigationView是Google发布的一个底部导航栏的实现，首先看一下效果图在导航栏的选项超过3个时，将会隐藏未选中的选项的标题，如图接下来就简单的使用一下这个底部导航栏吧0X01 在 build.gradle 中引入 design 包在APP内的 guild.gradle 的 dependencies 中插入 compile 'com.android.support:design:25.3.1' 0X02 在 menu 中建立 item 选项item 是导航栏里的选项0x01在 res 文件夹下新建 menu 文件夹0x02在 menu 文件夹下建立 item.xml这里我建立的是 navigation.xml0x03在 item.xml 里添加 item，xml 代码如下：<?xml version="1.0" encoding="utf-8"?> <item android:id="@+id/navigation_home" android:icon="@drawable/ic_home_black_24dp" android:title="主页" /> <item android:id="@+id/navigation_community" android:icon="@drawable/ic_favorite_black_24dp" android:title="社区" /> <item android:id="@+id/navigation_notifications" android:icon="@drawable/ic_notifications_black_24dp" android:title="消息" /> <item android:id="@+id/navigation_me" android:icon="@drawable/ic_person_black_24dp" android:title="个人" /> 这里的icon是用的AS自带的image，需要新建 Vector Asset，具体步骤：0x03.1这是右键菜单0x03.2点击 icon 图标0x03.3选到你想要的图标后点 next ，再点 finish ,然后就可以在代码里调用这个图标了1X01 在布局 XML 中插入 BottomNavigationView因为它是底部导航栏，那么就把他放在布局的最下面，这里我用的 LinearLayout ,选垂直布局， TextView 的高度是按权重获得最大高度，让他占满除Navigation以外的所有空间，详细代码：<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:orientation="vertical" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="cc.jethro.bottomnavigationviewtest.MainActivity"> <TextView android:padding="16dp" android:id="@+id/content_view" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" android:text="主页" android:textSize="40dp"/> <android.support.design.widget.BottomNavigationView android:background="?android:attr/windowBackground" android:id="@+id/navigation_view" app:menu="@menu/navigation" android:layout_width="match_parent" android:layout_height="wrap_content"> </android.support.design.widget.BottomNavigationView> </LinearLayout> 1X02 在 …

一行代码解决RecyclerView在ScrollView中滑动惯性失效
type: item
date: 2017-05-13T18:52:00+08:00
description: 做项目的时候发现把Recycler嵌套在ScrollView中时，上下滑动会没有惯性，一开始以为是因为图片的原因，上了图片加载框架还是卡顿，后经过查询资料，这个问题只在Android 5.0 及以上出现，只需要对recycler设置一下就好，方法如下：recyclerView.setNestedScrollingEnabled(false);

解决 Android Studio 新建项目卡死（超详细图文教程）
type: item
date: 2017-04-22T17:50:00+08:00
description: 以下AS均指Android StudioAS在新建项目时会下载gradle文件，此文件在国外，大小80M左右，基本上是下载失败的，于是AS就会一直卡住。教程的思路是把gradle下载到本地，然后在AS上设置离线gradle。一 下载Gradle首先让电脑处于断网状态，然后打开AS，打开后新建项目新建项目后如图打开图片中的文件，然后复制横线上的网址恢复网络，打开迅雷，新建任务，把刚才复制的网址黏贴进去，记得在网址前面加上http:// 如图下载成功后将其解压，解压成功打开文件夹，里面还有一个文件夹，文件夹名字格式大概是gradle-3.3这样的，打开gradle-3.3后文件夹结构是这样的然后你可以在D盘新建一个gradle文件夹，把gradle-3.3这个文件夹复制/移动进去。二 设置离线gradle打开AS，进入设置，点屏幕左上角的 File -> Settings 或者按快捷键 Ctrl + Alt + S 第三步是选择你的gradle-3.3文件夹，选择后点OK，等一会儿gradle就build好了，然后点一Try again三 大功告成好了，到这里你的项目就新建成功了喜欢的话点击这里打赏一下作者奥

android6.0运行时权限的使用与封装
type: item
date: 2016-12-28T19:48:00+08:00
description: 在android6.0之前的系统要想申请权限，只需要在AndroidManifest.xml中申明一下就行，而用户在安装软件的时候系统会显示该软件会用到那些权限，用户觉得不合理可以选择不安装，这个设计的初衷是好的，可是总是会出现店大欺客的现象，比如QQ,微信，支付宝等等，明明不需要修改通话记录的权限和修改联系人的权限，可他们就是恬不知耻的申请了，你还必须得同意，不能拒绝安装，因为你的朋友们都在用。或者生活中经常得用到他。后来android6.0加入了运行时权限这样的话，一些会涉及到用户隐私和安全的权限即使软件申明了，用户不同意还是可以安装，安装后在使用过程中，软件需要某种权限的话得向用户请求，用户可以选择允许或者拒绝，用户不需要担心软件一直申请，用户可以选择不再询问。同时，如果这样做了得话，只有用户去设置->应用管理->APP->权限管理里手动开启权限了。那么，现在就来看看这个运行时权限怎么弄吧如何向用户申请权限最基本简单的方式就是，首先建立一个项目，RunTimePermissionsTest，判断程序是否拥有权限，未拥有就申请，拥有就进行你想做的事，比如拨打电话。 public class MainActivity extends AppCompatActivity{ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button button = (Button) findViewById(R.id.button); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //获取拨号权限名，返回一个字符串 String callPhonePermission = Manifest.permission.CALL_PHONE; /** * ContextCompat.checkSelfPermission()检查程序是否拥有某个权限， * 接收两个参数：第一个参数时Context，第二个参数是权限名 * 返回两个结果: * PackageManager.PERMISSION_GRANTED表示已拥有 * PackageManager.PERMISSION_DENIED表示未拥有 */ if (ContextCompat.checkSelfPermission(MainActivity.this, callPhonePermission) != PackageManager.PERMISSION_GRANTED) { //当程序未拥有某个权限的时候进行权限申请 /** * ActivityCompat.requestPermissions() 向用户申请权限 * 接收两个参数： * 第一个参数是Activity实例 * 第二个参数是String数组，把需要申请的权限名放进去 * 第三个参数是唯一的权限请求码，这里就写个1就行了 */ ActivityCompat.requestPermissions(MainActivity.this, new String[]{callPhonePermission}, 1); } else { //当程序拥有某个权限的时候运行需要进行的操作 //这里就进行拨号操作 Intent intent = new Intent(Intent.ACTION_CALL); intent.setData(Uri.parse("tel:10086")); startActivity(intent); } } }); } /** * 申请授权后，系统会回调这个方法 * * @param requestCode 权限申请码 * @param permissions 申请的权限名数组 * @param grantResults 申请的结果数组 */ @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { //建立一个List，存储申请失败的权限 List<String> deniedList = new ArrayList<>(); //遍历grantResults数组 for (int i = 0; i < grantResults.length; i++…

使用menu菜单
type: item
date: 2016-12-09T12:37:00+08:00
description: 在android的标题栏UI中添加一个菜单，还可以按menu键打开。首先在res资源文件夹下创建一个menu菜单，然后右键点new再点menu resource file取名main（看官随意）在xml中添加两个item标签，代码如下<?xml version="1.0" encoding="utf-8"?> <item android:id="@+id/add_item" android:title="add"/> <item android:id="@+id/remove_item" android:title="remove"/> 在activity中重写onCreateOptionsMenu 直接输入方法名，回车，studio会自动补全代码 /** * 将菜单显示出来 * @param menu * @return 返回true表示可用，false反之 */ @Override public boolean onCreateOptionsMenu(Menu menu) { //调用gtMenuInflater方法能够得到MenuInflater对象 //再调用它的inflate方法就可以给当前活动创建菜单了，他需要传入两个参数 //第一个是菜单的资源文件，第二个是指定将菜单项添加到那一个对象，这里用方法中传入的menu对象 getMenuInflater().inflate(R.menu.main,menu); return true; }这样创建的menu就可以显示在活动中了，然后我们再重写onOptionItemSelected方法来使菜单中的选项被点击后有响应。 /** * 对menu添加监听 * @param item 被点击的选项的 * @return */ @Override public boolean onOptionsItemSelected(MenuItem item) { //getItemId() 获取被点击的选项的ID switch (item.getItemId()){ //如果被点击的选项使add_item则Toast弹出“You clicked add” case R.id.add_item: //Toast有三个参数，第一个是Toast将显示在那个活动中，第二个是要显示的内容，第三个是时间的长短 Toast.makeText(this,"You clicked add",Toast.LENGTH_SHORT).show(); break; case R.id.remove_item: Toast.makeText(this,"YOu clicked remove",Toast.LENGTH_SHORT).show(); break; default: } return true; }

TabLayout与ViewPager的辛酸往事
type: item
date: 2016-11-19T18:53:00+08:00
description: 打算做一个APP，第一步自然是先做布局拉！想做个标签页然后就用到了TabLayout和ViewPager，实现的效果就是这样嗯。。。下面就说说我是怎么实现的把。首先引入依赖包嗯。。。我用的google官方的design提供的TabLayout，那么首先就是引入'com.android.support:design:25.0.0'包。在主布局文件添加TabLayout和ViewPager<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="cc.jethro.peaseeds.MainActivity"> <android.support.design.widget.TabLayout android:id="@+id/tabLayout" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="?attr/colorPrimary" app:tabIndicatorHeight="4dp" app:tabTextAppearance="@style/MyTabLayoutTextAppearance"> </android.support.design.widget.TabLayout> <android.support.v4.view.ViewPager android:id="@+id/viewPager" android:layout_width="match_parent" android:layout_height="match_parent"> </android.support.v4.view.ViewPager> </LinearLayout> 布局中只有TabLayout和ViewPager组件创建需要的标签布局和Fragment创建XML布局文件（需要几个标签就创建几个XML布局文件）<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:padding="16dp" android:layout_height="match_parent"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="分析"/> </LinearLayout>创建Fragment/* *Fragment相当于就是一个Activity，他是依附到Activity上的，他具有很高灵活性，能随时添加和移除，Activity生命周期结束被销毁，Fragment也会被销毁。 */ package cc.jethro.peaseeds; import android.app.Fragment; import android.os.Bundle; import android.support.annotation.Nullable; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; /** * Created by jethro on 2016/11/19. */ public class AnalysisFragment extends Fragment { @Nullable @Override public View onCreateView(LayoutInflate…

使用openFileInput和openFileOutput读写文本文件
type: item
date: 2016-11-03T15:42:00+08:00
description: android为我们提供了openFileInput和openFileOutput两个方法来将数据读取和写入文件。//写入文件 try{ FileOutputStream fo = openFileOutput("火焰魔法使用口诀.txt",Context.MODE_WORLD_READABLE); //将字节流转换为字符流，相当于桥 OutputStreamWrite bridge = new OutputStreamWrite(fo); //创建字符缓冲流,将桥传进去 BufferedWriter bw = new BufferedWriter(bridge); String s = "我以屌丝之名召唤火焰！出来吧，大火"; //将字符串s写入到缓冲流中 bw.write(s); //写完了一定要写这句将缓冲流中的数据刷到目的文件中去 bw.flush(); //关闭字符缓冲流和桥以及字节流 bw.close(); fo.close(); }openFileOutput()方法是activity提供的一个打开文件的方法第一个参数是文件名，不能包含路径分隔符 / ，如果文件不存在android会自动创建这个文件，这个文件在data/data//files 目录下，如 data/data/com.myApp.iottxt/file/火焰魔法使用口诀.txt第二个参数是指定操作模式，有四个操作模式Context.MODE_PRIVATE = 0 Context.MODE_APPEND = 32768 Context.MODE_WORLD_READABLE = 1 Context.MODE_WORLD_WRITEABLE = 2 /** *Context.MODE_PRIVATE：为默认操作模式，代表该文件是私有数据，只能被应用本身访问，在该模式下，写入的内容会覆盖原文件的内容，如果想把新写入的内容追加到原文件中。可以使用Context.MODE_APPEND *Context.MODE_APPEND：模式会检查文件是否存在，存在就往文件追加内容，否则就创建新文件。 *Context.MODE_WORLD_READABLE和Context.MODE_WORLD_WRITEABLE用来控制其他应用是否有权限读写该文件。 *MODE_WORLD_READABLE：表示当前文件可以被其他应用读取；MODE_WORLD_WRITEABLE：表示当前文件可以被其他应用写入。 *如果希望文件被其他应用读和写，可以传入：openFileOutput(“火焰魔法使用口诀”, Context.MODE_WORLD_READABLE + Context.MODE_WORLD_WRITEABLE); **/OutputStreamWrite和InputStreamReader的作用以InputStreamReader为例，另外一个用法一样。 openFileInput()返回的是字节流，需要传到InputStreamReader中，再将InputStreamReader传给BufferedReader字符缓冲流//读取文件 try{ FileInputStream fi = openFile("火焰魔法使用口诀.txt") BufferedReader is = new BufferedReader(new InputStreamReader(fi)); String ls ; //临时的字符串 Steing text = ""; //最会读取出来的字符串 while((ls = is.readLine()) != null){ text = text+ls+"\n"; } fi.close(); is.close(); }读取文件和写入文件都是差不多的感觉。。。基本就是打开一个文件，然后将字节文件流转换为字符文件流，再将她转为字符缓冲流。最后用缓冲流对文件进行操作。