本文首发于我的Hexo博客:https://likianta.coding.me/2017/PassportPandoraPrj/1229172454/
先来看最终的效果图:
本章内容是根据上章制定好的规范进行的重新设计,其中主界面的两个分页布局变化幅度很大。
本章重点讲解右分页布局的重制,左分页布局会在下章讲解分组设计前补充。
一、重新制作右分页布局
在正式开始制作之前,我们需要预先下载两个软件:
- Axure PR 8:用于原型设计,然后根据此设计来实现工程布局
- X-Mind 8 Pro:用于制作思维导图,然后根据思维导图来实现工程代码的逻辑结构
虽然我们不是专业的设计师或产品经理,但是这两个软件的基本功能其实还是很容易上手的,下面就是简单的应用过程:
1. 使用Axure软件进行原型设计
下载好Axure PR 8后,打开软件界面如下所示:
第一步 开启网格辅助线
点击 菜单栏 - 布局 - 栅格和辅助线 - 勾选显示网格&进入网格设置:
在网格设置中的“网格”标签下勾选“显示网格”和“对齐网格”,设置Spacing = 40px
,样式为线:
这样我们就成功地开启了网格辅助线。网格线有助于我们对组件规范、整齐地摆放,对于有整理癖的人来说非常好。
第二步 拖入一倍切图
在《关于Android开发中如何使用dp表示长宽 | Likianta Blog》一文中介绍了我们如何在布局中使用合适的切图作为参考,简单来说有以下几点:
- 使用一倍切图(360px*640px)作为原型设计稿
- 在Android开发中,使用Pixel作为默认的模拟器设备,则我们的xml文件中用360dp可以表示屏幕的总宽度,640dp代表屏幕的总高度,数值跟一倍切图稿是1:1的转换关系
另外值得一提的是Android的状态栏的高度是24dp,虚拟按键的高度是48dp,也就是说我们的一倍设计稿中有24+48px要从总高度中抛去,所以我们实际可设计的空间范围是:360px*568px。
下面在Axure中的标注和讲解我都会用“px”作为单位,然后到了制作Android xml布局文件的时候改用“dp”,由于数值之间是1:1的转换关系,所以大家不用太纠结 :)
第三步 制作右分页母版
本章我们的目的是重新制作右分页布局,所以把右分页中的固定元素做成母版(也就是模板),这样方便我们经常复用。
首先从左侧的组件库拖一个矩形2到中间的工作区,在属性面板中设置它的宽度为360px,高度为640px,位置的话就放在(40,80)吧:
(注:Axure中的位置都是从控件的左上角作为起始点计算的。)
依次拖入下面组件:
这样我们的右分页母版就完成了。
以后在每次复用时,只需将左下方的母版拖到中间的工作区,并右键“脱离母版”即可自由使用了。
第四步 制作布局里的元素(重点)
为了让设计显得简洁,这里将之前加入的很多元素都去除掉了,只保留了最核心的部分:
- 头像及头像背景
- 帐号栏
- 密码栏
- 备注栏
- 扩展信息按钮
- 保存按钮
另外根据预想的设计规范,按钮应该有丰富的响应动画。最典型的就是按下编辑框后应该有聚焦动画。为此我们给每个编辑框设置了三种常用状态和五种特定状态:
(不过在实际开发中发现这些设计所考虑到的东西是远远不够的。后面我们会一个问题一个问题地解决它们。)
第五步 添加说明注释和跳转链接
这项工作主要是在控件旁边写一些颜色、相对位置、注意事项这三类说明。主要用到的工具有:
顺便附上下载链接:该设计图原始文件(2017年12月30日)
如果你想要尽可能快地上手Axure这个软件,可以看一看我写的Axure从零快速上手 第一期 :)
二、在Android Studio中实现布局
有了原型设计图,你会发现制作xml布局比以前轻松了很多。
不过实际制作过程中会遇到一些比较严重的问题,比如下面提到的:
Q1:如何将Axure中的不透明度百分数转化为Android Studio中的两位十六进制数?
根据下面的透明度参照表:
透明度 | 不透明度 | 转换成十六进制数 | 备注 |
---|---|---|---|
00% | 100% | FF | 完全显示 |
05% | 95% | F2 | |
10% | 90% | E5 | |
15% | 85% | D8 | |
20% | 80% | CC | |
25% | 75% | BF | |
30% | 70% | B2 | |
35% | 65% | A5 | |
40% | 60% | 99 | |
45% | 55% | 8C | 往上越来越“深” |
50% | 50% | 7F | 50%半透明 |
55% | 45% | 72 | 往下越来越“浅” |
60% | 40% | 66 | |
65% | 35% | 59 | |
70% | 30% | 4C | |
75% | 25% | 3F | |
80% | 20% | 33 | |
85% | 15% | 21 | |
90% | 10% | 19 | |
95% | 05% | 0C | |
100% | 00% | 00 | 完全透明 |
PS:
- Axure和Android Studio用的都是不透明度,所以只需看第2列和第3列的对应关系即可
- 所以我们在Axure中设计时也尽量取整5整10的不透明度,这样方便照着表直接转换
Q2:设计图中的编辑框内右侧的小箭头图标(>
)怎么制作?
在Android Studio Asset网站你可以找到这个图标(图标名称“chevron”)。生成图表注意事项:
终于,我们下面要开始制作右分页布局了:
三、制作右分页布局(xml布局代码)
先看一下最终的效果图:
1. 将布局中用到的文字全部写到strings.xml文件中
res/values/strings.xml
:
<resources>
<string name="app_name">AnyKey</string>
<string name="title_activity_login">AnyKey</string>
<!-- Strings related to login -->
<string name="prompt_email">Email</string>
<string name="prompt_password">Password (optional)</string>
<string name="action_sign_in">Sign in or register</string>
<string name="action_sign_in_short">Sign in</string>
<string name="error_invalid_email">This email address is invalid</string>
<string name="error_invalid_password">This password is too short</string>
<string name="error_incorrect_password">This password is incorrect</string>
<string name="error_field_required">This field is required</string>
<string name="permission_rationale">"Contacts permissions are needed for providing email completions."</string>
<!-- Strings related to page2 -->
<string name="title_hint">Title</string>
<string name="title_popup">▲TITLE</string>
<string name="title_error">Please Input Title Strings</string>
<string name="account_hint">Account</string>
<string name="account_popup">▲ACCOUNT</string>
<string name="pwd_hint">Password</string>
<string name="pwd_popup">▲PASSWORD</string>
<string name="note_hint">Take your note here.</string>
<string name="note_popup">◆NOTE</string>
</resources>
2. 制作xml布局
res/layout/page2.xml
(原page_card_list.xml
改名了):
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/blaWindowBackground">
<!-- 最外层是一个RelativeLayout,背景色与左分页背景同色 -->
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content">
<!-- 在滑动布局中建一个相对布局。编辑框等控件都在此布局里面 -->
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<!-- 头像的背景,根据头像主色调来生成(方法在函数中实现) -->
<ImageView
android:id="@+id/user_avatar_bg"
android:layout_width="match_parent"
android:layout_height="120dp"
android:background="@color/blaThemeColorWeak" />
<!-- 使用开源控件CircleTextImageView制作圆形头像 -->
<com.thinkcool.circletextimageview.CircleTextImageView
android:id="@+id/user_avatar"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_centerHorizontal="true"
android:layout_marginTop="80dp"
android:src="@drawable/avatar_test"
app:citv_border_color="@color/blaWindowBackground"
app:citv_border_width="2dp" />
<!-- 下面是4个EditText,分别是“标题栏”、“帐号栏”、“密码栏”、“备注栏” -->
<!-- 标题栏属性:宽240dp,高28dp,水平居中,与头像相距40dp,背景是一个圆角矩形背景(后面会贴代码),限制为单行输入,文字颜色和hint颜色均在colors.xml文件中定义好了,这里就引用过来。最后设置文字风格为等宽字体(monospace),文字大小14sp,字体默认加粗 -->
<EditText
android:id="@+id/user_title"
android:layout_width="240dp"
android:layout_height="28dp"
android:layout_below="@id/user_avatar"
android:layout_centerHorizontal="true"
android:layout_marginTop="40dp"
android:background="@drawable/edt_bg_normal"
android:gravity="center_vertical"
android:hint="@string/title_hint"
android:inputType="textCapCharacters"
android:paddingEnd="12dp"
android:paddingStart="12dp"
android:singleLine="true"
android:textColor="@color/blaTextColorStrong"
android:textColorHint="@color/blaTextColorWeak"
android:textSize="14sp"
android:textStyle="bold"
android:typeface="monospace" />
<!-- 接下来的“帐号栏”和“密码栏”的属性与“标题栏”一样,只是修改为各自的id、hint字符串内容不同而已 -->
<!-- 帐号栏 -->
<EditText
android:id="@+id/user_account"
android:layout_width="240dp"
android:layout_height="28dp"
android:layout_alignTop="@id/user_title"
android:layout_centerHorizontal="true"
android:layout_marginTop="60dp"
android:background="@drawable/edt_bg_normal"
android:gravity="center_vertical"
android:hint="@string/account_hint"
android:paddingEnd="12dp"
android:paddingStart="12dp"
android:singleLine="true"
android:textColor="@color/blaTextColorStrong"
android:textColorHint="@color/blaTextColorWeak"
android:textSize="14sp"
android:textStyle="bold"
android:typeface="monospace" />
<!-- 密码栏 -->
<EditText
android:id="@+id/user_pwd"
android:layout_width="240dp"
android:layout_height="28dp"
android:layout_alignTop="@id/user_title"
android:layout_centerHorizontal="true"
android:layout_marginTop="120dp"
android:background="@drawable/edt_bg_normal"
android:gravity="center_vertical"
android:hint="@string/pwd_hint"
android:inputType="textVisiblePassword"
android:paddingEnd="12dp"
android:paddingStart="12dp"
android:singleLine="true"
android:textColor="@color/blaTextColorStrong"
android:textColorHint="@color/blaTextColorWeak"
android:textSize="14sp"
android:textStyle="bold"
android:typeface="monospace" />
<!-- 备注栏的设置有些不一样,区别在于:宽度为300dp,高度为自适应,但是最低高度值是“7”行(minLines=7),其背景用的是和上面不一样的背景资源(后面也会贴上背景代码),inputType类型是“textMultiLine” -->
<EditText
android:id="@+id/user_note"
android:layout_width="300dp"
android:layout_height="wrap_content"
android:layout_alignTop="@id/user_title"
android:layout_centerHorizontal="true"
android:layout_marginTop="180dp"
android:background="@drawable/edt_bg_normal_note"
android:gravity="start|top"
android:hint="@string/note_hint"
android:inputType="textMultiLine"
android:minLines="7"
android:padding="12dp"
android:textColor="@color/blaTextColorStrong"
android:textColorHint="@color/blaTextColorWeak"
android:textSize="14sp"
android:textStyle="bold"
android:typeface="monospace" />
<!-- 下面是编辑框上的提示文字,也就是这个部分:http://ozurciydg.bkt.clouddn.com/17-12-31/73887956.jpg -->
<!-- 标题栏提示文字。提示文字仅在框内有输入文字时出现,默认状态下是隐藏的 -->
<TextView
android:id="@+id/user_title_txt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignStart="@id/user_title"
android:layout_alignTop="@id/user_title"
android:layout_marginStart="-10dp"
android:layout_marginTop="-18dp"
android:text="@string/title_popup"
android:textColor="@color/blaTextColorStrong"
android:textSize="12sp"
android:textStyle="bold"
android:visibility="invisible" />
<!-- 帐号栏提示文字 -->
<TextView
android:id="@+id/user_account_txt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignStart="@id/user_account"
android:layout_alignTop="@id/user_account"
android:layout_marginStart="-10dp"
android:layout_marginTop="-18dp"
android:text="@string/account_popup"
android:textColor="@color/blaTextColorStrong"
android:textSize="12sp"
android:textStyle="bold"
android:visibility="invisible" />
<!-- 密码栏提示文字 -->
<TextView
android:id="@+id/user_pwd_txt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignStart="@id/user_pwd"
android:layout_alignTop="@id/user_pwd"
android:layout_marginStart="-10dp"
android:layout_marginTop="-18dp"
android:text="@string/pwd_popup"
android:textColor="@color/blaTextColorStrong"
android:textSize="12sp"
android:textStyle="bold"
android:visibility="invisible" />
<!-- 备注栏提示文字 -->
<TextView
android:id="@+id/user_note_txt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignStart="@id/user_note"
android:layout_alignTop="@id/user_note"
android:layout_marginStart="-8dp"
android:layout_marginTop="-18dp"
android:text="@string/note_popup"
android:textColor="@color/blaTextColorStrong"
android:textSize="12sp"
android:textStyle="bold"
android:visibility="invisible" />
<!-- 大家应该注意到上面的编辑框还没有写框内右侧的小按钮,下面用ImageView分别加上 -->
<!-- Icon Chevron related to EditTexts -->
<!-- 标题栏按钮。图标是从Android Studio Assets网站下载的 -->
<ImageView
android:id="@+id/user_title_chevron"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignEnd="@id/user_title"
android:layout_alignTop="@id/user_title"
android:layout_marginEnd="6dp"
android:layout_marginTop="9dp"
android:src="@drawable/icon_chevron_right" />
<!-- 帐号栏按钮 -->
<ImageView
android:id="@+id/user_account_chevron"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignEnd="@id/user_account"
android:layout_alignTop="@id/user_account"
android:layout_marginEnd="6dp"
android:layout_marginTop="9dp"
android:src="@drawable/icon_chevron_right" />
<!-- 密码栏按钮 -->
<ImageView
android:id="@+id/user_pwd_chevron"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignEnd="@id/user_pwd"
android:layout_alignTop="@id/user_pwd"
android:layout_marginEnd="6dp"
android:layout_marginTop="9dp"
android:src="@drawable/icon_chevron_right" />
<!-- 备注栏没有chevron按钮 -->
</RelativeLayout>
</ScrollView>
</RelativeLayout>
其中需要用到的背景布局:
1. 标题栏、帐号栏、密码栏背景布局文件:res/drawable/edt_bg_normal.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 被按下时的状态(正在按。这个状态很短暂) -->
<!-- 此时圆角矩形的边框颜色与框内填充颜色融为一体,为纯白色 -->
<item android:state_pressed="true">
<shape android:shape="rectangle">
<solid android:color="@color/blaEdtColorActivated" />
<corners android:radius="6dp" />
</shape>
</item>
<!-- 按下后的状态(聚焦状态) -->
<!-- 此时的状态和上一个一样。不过我们会在代码中给它附加上阴影效果 -->
<item android:state_focused="true">
<shape android:shape="rectangle">
<solid android:color="@color/blaEdtColorActivated" />
<corners android:radius="6dp" />
</shape>
</item>
<!-- 普通状态(未获得焦点时的状态) -->
<!-- 边框颜色为浅灰色(与hint文字颜色一致),框内填充色为纯白色 -->
<item android:state_window_focused="false">
<shape android:shape="rectangle">
<solid android:color="@color/blaEdtColorNormal" />
<stroke android:width="1dp" android:color="@color/blaTextColorWeak" />
<corners android:radius="6dp" />
</shape>
</item>
</selector>
2. 备注栏的背景布局文件:res/drawable/edt_bg_normal_note.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 被按下时的状态(正在按。这个状态很短暂) -->
<!-- 省略不写 -->
<!-- 按下后的状态(聚焦状态) -->
<!-- 这里引用了一个图片资源。在下一小节会对其讲解 -->
<item android:drawable="@drawable/edt_bg_shadow" android:state_focused="true" />
<!-- 普通状态(未获得焦点时的初始状态。和上一个背景的初始状态一样) -->
<item android:state_window_focused="false">
<shape android:shape="rectangle">
<solid android:color="@color/blaEdtColorNormal" />
<stroke android:width="1dp" android:color="@color/blaTextColorWeak" />
<corners android:radius="6dp" />
</shape>
</item>
</selector>
3. 阴影该怎么制作?
Android 5.0加入了高度(elevation)属性,只需要在EditText中设置elevation="6"
即可生成高度为6dp(z=6)的阴影。
但是用这个方法做出来的阴影效果并不好看:
并且elevation属性也不支持调节阴影方向、blur值以及阴影颜色。为了实现和Axure软件同样的效果,我们要考虑采用哪种阴影方案:
方案 | 说明 | 缺点 |
---|---|---|
1 | 增加elevation高度 | 效果不好,是向四周扩散的 |
2 | 看看有没更改阴影颜色的代码 | 默认不支持,自定义太麻烦 |
3 | 使用嵌套结构layer-list | 阴影效果不好,没办法做出来设计图中的效果 |
4 | 使用translation x & y | 会造成点击错位(不过影响不大),实际测试还是觉得阴影的效果不好 |
5 | 使用开源组件——shadow layout的功能特性非常符合需求 | shadow-layout需要给每个view套一个外壳,且自带4dp内间距,导致提示文字和按钮的相对位置一直调不准 |
6 | 使用CardView | CardView缺点见这里:Home · dmytrodanylyk/shadow-layout Wiki |
7 | 使用点九图制作 | ok |
最终我们选择了点九图制作,这里推荐使用Android Shadow Generator在线网站来生成点九图。你可以自定义边角弧度、阴影偏离方向、blur值以及阴影颜色,理论上可以完美实现和Axure设计图一致的阴影效果:
注意:点九图参数设置:
不同的分辨率对应的参数值是不一样的。总的来说我们需要制作以下三种点九图:
切图 | Radius(边框圆角半径)/px | x偏移/px | y偏移/px | blur | 备注 |
---|---|---|---|---|---|
一倍切图(360px*640px) | 6 | 5 | 5 | 8 | 一倍切图就是我们在设计稿中的分辨率 |
二倍切图(720px*1280px) | 12 | 10 | 10 | 16 | 二倍切图就是xhdpi等级 |
三倍切图(1080px*1920px) | 18 | 15 | 15 | 24 | 三倍切图就是xxhdpi等级。 我们使用的Pixel模拟器就是这个等级,其dp-px换算关系为1dp=3px |
因此我们最好做三种贴图并放到对应的分辨率目录(不过为了测试方便,这里我只做了三倍切图的阴影)。
属性如下:
Basic | Fill & Outline | Advanced | Preview | 备注 |
---|---|---|---|---|
Round = 18 | Unenabled | Box Size width = 144 | / | / |
Shadow blur = 24 | / | Box Size height = 144 | / | / |
Shadow color: rgba(0,0,0,0.35) | / | / | / | / |
Shadow offset X = 15 | / | / | / | / |
Shadow offset y = 15 | / | / | / | 文件下载:edt_bg_shadow.9.png |
把下载下来的点九图阴影复制到drawable目录下,接着我们给右分页布局中的EditText控件添加阴影:
res/layout/page2.xml
<RelativeLayout>
<ScrollView>
<RelativeLayout>
<!-- 头像的背景,根据头像主色调来生成(方法在函数中实现) -->
<ImageView ... />
<!-- 使用开源控件CircleTextImageView制作圆形头像 -->
<com.thinkcool.circletextimageview.CircleTextImageView ... />
<!-- EditText shadow on the behind -->
<!-- 1. 阴影放在EditText之前写,这样可以让后写的控件压到先写的阴影之上 -->
<!-- 2. 阴影的长宽比EditText各多出10dp,这个是目测出来的结果([WHY?](http://ozurciydg.bkt.clouddn.com/17-12-31/86825566.jpg)) -->
<!-- 3. 阴影默认状态是隐藏的,只有在EditText被点击时才会被触发 -->
<!-- 标题栏的阴影 -->
<ImageView
android:id="@+id/user_title_shadow"
android:layout_width="250dp"
android:layout_height="38dp"
android:layout_alignStart="@id/user_title"
android:layout_alignTop="@id/user_title"
android:background="@drawable/edt_bg_shadow"
android:visibility="invisible" />
<!-- 帐号栏的阴影 -->
<ImageView
android:id="@+id/user_account_shadow"
android:layout_width="250dp"
android:layout_height="38dp"
android:layout_alignStart="@id/user_account"
android:layout_alignTop="@id/user_account"
android:background="@drawable/edt_bg_shadow"
android:visibility="invisible" />
<!-- 密码栏的阴影 -->
<ImageView
android:id="@+id/user_pwd_shadow"
android:layout_width="250dp"
android:layout_height="38dp"
android:layout_alignStart="@id/user_pwd"
android:layout_alignTop="@id/user_pwd"
android:background="@drawable/edt_bg_shadow"
android:visibility="invisible" />
<!-- 由于备注栏的高度是随文本多少而动态变化的,使用阴影的话需要在代码中动态匹配,一方面逻辑比较复杂,另一方面实测发现设定了“inputType="textMultiLines"”属性的EditText无法监听按键事件,给我们的匹配方法造成了致命的阻碍。因此我们不制作备注栏阴影,改用了另一种更省事的方法(下面会说) -->
<!-- EditText -->
<EditText android:id="@+id/user_title" ... />
<EditText android:id="@+id/user_account" ... />
<EditText android:id="@+id/user_pwd" ... />
<EditText android:id="@+id/user_note" ... />
...
</RelativeLayout>
</ScrollView>
</RelativeLayout>
由于备注栏的高度是随文本多少而动态变化的,使用阴影的话需要在代码中动态匹配,一方面逻辑比较复杂,另一方面实测发现设定了“inputType=”textMultiLines””属性的EditText无法监听按键事件,给我们的匹配方法造成了致命的阻碍。因此我们不得不手动将点九图阴影直接写入到备注栏的背景中去:
res/drawable/edt_bg_normal_note.xml
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 按下后的状态(聚焦状态) -->
<!-- 这里引用了一个图片资源 -->
<item android:drawable="@drawable/edt_bg_shadow" android:state_focused="true" />
<!-- 普通状态 -->
<item ... />
</selector>
四、启动测试
由于我们对右分页布局进行了大换血,原MainActivity中的很多代码可能不能再使用了,为了保证本次测试成功,我们需要适当地对MainActivity进行一番改动(主要就是把以前的关联代码注释掉或删除掉):
MainActivity.java
package com.likianta.anykey;
import android.content.Context;
import android.content.Intent;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
import com.thinkcool.circletextimageview.CircleTextImageView;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
List<CardData> cardDataList = new ArrayList<>();
// 以下变量是与分页相关的变量
private View page1; // 左分页
private View page2; // 右分页
private ViewPager viewPager; // 控制分页逻辑的容器
private ArrayList<View> pageList; // 装载分页元素的容器
// 以下变量是与左分页相关的控件
private RecyclerView recyclerView; // 卡片列表
private CardAdapter cardAdapter;
private DataAdapter dataAdapter;
private List<Card> cardList = new ArrayList<>(); // 卡片数据
// 以下变量是与右分页相关的控件
private CircleTextImageView userAvatar;
private EditText userTitle;
private EditText userAccount;
private EditText userPassword;
private EditText userNote;
private ImageView userTitleShadow;
private ImageView userAccountShadow;
private ImageView userPasswordShadow;
private ImageView userTitleChevron;
private ImageView userAccountChevron;
private ImageView userPasswordChevron;
private TextView userTitleText;
private TextView userAccountText;
private TextView userPasswordText;
private TextView userNoteText;
private Button userSaveButton;
private String groupName = "未分类"; // temple conversion string
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 开始绑定按钮
initBinding();
// 初始化左分页
initPager1();
// 初始化右分页(在onCreate方法中没必要做。在点击保存按钮后使用该方法来清空右分页的表单编辑框)
// initPager2();
// 监听按钮点击
// test
Button button = (Button) findViewById(R.id.btn_test);
button.setOnClickListener(this);
}
public void initBinding() {
viewPager = (ViewPager) findViewById(R.id.view_pager);
// 用LayoutInflater来绑定布局
LayoutInflater inflater = getLayoutInflater();
page1 = inflater.inflate(R.layout.page1, null); // 预加载左分页
page2 = inflater.inflate(R.layout.page2, null); // 预加载右分页
pageList = new ArrayList<>(); // pageList被实例化为装载View元素的数组
pageList.add(page1);
pageList.add(page2);
// add的先后顺序不要搞错,先add的就是array[0]位置的元素了
// 绑定分页的按钮
recyclerView = (RecyclerView) page1.findViewById(R.id.page1_recycler);
// bind page2 views
userAvatar = (CircleTextImageView) page2.findViewById(R.id.user_avatar);
userTitle = (EditText) page2.findViewById(R.id.user_title);
userTitleShadow = (ImageView) page2.findViewById(R.id.user_title_shadow);
userTitleText = (TextView) page2.findViewById(R.id.user_title_txt);
userTitleChevron = (ImageView) page2.findViewById(R.id.user_title_chevron);
userAccount = (EditText) page2.findViewById(R.id.user_account);
userAccountShadow = (ImageView) page2.findViewById(R.id.user_account_shadow);
userAccountText = (TextView) page2.findViewById(R.id.user_account_txt);
userAccountChevron = (ImageView) page2.findViewById(R.id.user_account_chevron);
userPassword = (EditText) page2.findViewById(R.id.user_pwd);
userPasswordShadow = (ImageView) page2.findViewById(R.id.user_pwd_shadow);
userPasswordText = (TextView) page2.findViewById(R.id.user_pwd_txt);
userPasswordChevron = (ImageView) page2.findViewById(R.id.user_pwd_chevron);
userNote = (EditText) page2.findViewById(R.id.user_note);
userNoteText = (TextView) page2.findViewById(R.id.user_note_txt);
}
public void initPager1() {
PagerAdapter pagerAdapter = new PagerAdapter() {
// https://www.cnblogs.com/weixing/archive/2013/10/11/3363951.html
// 获取页卡总数量
@Override
public int getCount() {
return pageList.size();
}
// 判断是否由对象生成界面,这个很重要,是用来把pageView数组中的page1和page2生成到当前布局中的方法
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
// 使从ViewGroup中移出当前View
@Override
public void destroyItem(ViewGroup arg0, int arg1, Object arg2) {
((ViewPager) arg0).removeView(pageList.get(arg1));
}
// 返回一个对象,这个对象表明了PagerAdapter适配器选择哪个对象放在当前的ViewPager中
@Override
public Object instantiateItem(ViewGroup arg0, int arg1) {
//这个方法用来实例化页卡
((ViewPager) arg0).addView(pageList.get(arg1));
return pageList.get(arg1);
}
};
viewPager.setAdapter(pagerAdapter); // 绑定适配器
// 设置viewPager的初始界面为第一个界面
viewPager.setCurrentItem(0); // 这里的0对应的是viewPager[0],也就是page1了
// 添加切换界面的监听器
viewPager.addOnPageChangeListener(new MyOnPageChangeListener());
// 为左分页加载卡片列表
//PageRender();
}
public void initPager2() {
userTitle.setText("");
userAccount.setText("");
userPassword.setText("");
userNote.setText("");
// 不知道什么原因,如果在此处重置头像为默认,会发现卡片头像也会“突变”为默认。
// 而把重置头像的业务放到页面监听里面就不会引起此bug,所以不得已把头像重置的代码放到MyOnPageChangeListener的case1里面了
}
// 渲染分页
public void PageRender() {
cardDataList = new SavedToMySharedPrefs(MainActivity.this, "card_data").getCardData();
dataAdapter = new DataAdapter(cardDataList);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(dataAdapter);
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.btn_test:
int w = userNote.getWidth();
int h = userNote.getHeight();
LogUtil.d("ma user title edit text width is: " + w);
LogUtil.d("ma user title edit text height is: " + h);
default:
}
}
// 判断从GroupActivity来的返回值
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
switch (requestCode) {
case 1:
if (resultCode == RESULT_OK) {
groupName = intent.getStringExtra("get groupName");
LogUtil.d("ma you get the returned result (groupName): " + groupName);
}
break;
default:
}
}
@Override
public void onDestroy() {
super.onDestroy();
// Save all data.
new SavedToMySharedPrefs(MainActivity.this, "card_data").setCardData(cardDataList);
}
// 页面滚动监听器功能,实现标签页左右滑动切换效果
public class MyOnPageChangeListener implements ViewPager.OnPageChangeListener {
@Override
public void onPageSelected(int index) {
switch (index) {
case 0:
InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
inputMethodManager.hideSoftInputFromWindow(MainActivity.this.getCurrentFocus().getWindowToken(), 0); // 强制隐藏软键盘
break;
case 1:
userAvatar.setText("");
userAvatar.setImageResource(R.drawable.avatar_test);
break;
}
}
@Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
}
@Override
public void onPageScrollStateChanged(int arg0) {
}
}
}
另外对activity_main.xml
也做了大幅度简化:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorWhite"
tools:context="com.likianta.anykey.MainActivity">
<!-- 分页容器 -->
<android.support.v4.view.ViewPager
android:id="@+id/view_pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentTop="true">
</android.support.v4.view.ViewPager>
<Button
android:id="@+id/btn_test"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="test"/>
</RelativeLayout>
实际测试效果
(这里用的是下一章的图。里面增加了按钮动画和文本判断)
经过精心的制作后,我们本章的主要任务就完成了。
通过这次制作过程我们会发现,在设计时有很多细节都是考虑不到的,比如说EditText的inputType
、颜色值的百分比转换、阴影的制作问题等。通过自己的设计和实现可以对产品的开发有更多认识。
下章将讲解如何制作按钮的点击动画,以及根据文本内容适用不同的背景资源。
下章链接:https://likianta.coding.me/2017/PassportPandoraPrj/1231204322/
五、扩展环节
1. 如何让EditText默认不获取焦点?
在第一次打开后滑动到右分页,会发现标题栏会自动获取到焦点。为了让它不默认获取焦点,我们需要让别的View率先抢占焦点(只需增加两行代码):
page2.xml
<RelativeLayout>
<ScrollView>
<RelativeLayout>
<!-- 头像的背景 -->
<ImageView ... />
<!-- 圆形头像。让圆形头像抢占焦点 -->
<com.thinkcool.circletextimageview.CircleTextImageView
...
android:focusable="true"
android:focusableInTouchMode="true" />
<!-- EditText shadow on the behind -->
<ImageView android:id="@+id/user_title_shadow" ... />
<ImageView android:id="@+id/user_account_shadow" ... />
<ImageView android:id="@+id/user_pwd_shadow" ... />
<!-- EditText -->
<EditText android:id="@+id/user_title" ... />
<EditText android:id="@+id/user_account" ... />
<EditText android:id="@+id/user_pwd" ... />
<EditText android:id="@+id/user_note" ... />
...
</RelativeLayout>
</ScrollView>
</RelativeLayout>
2. 自定义光标样式
Android默认的光标是粉红色的,我们可以修改为自定义的光标样式。
首先创建一个背景布局res/drawable/shape_cursor.xml
:
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<size android:width="2dp" />
<solid android:color="@color/colorBlack" />
</shape>
然后让右分页的编辑框的光标全部装载此样式:
page2.xml
<RelativeLayout>
<ScrollView>
<RelativeLayout>
...
<!-- EditText -->
<EditText
android:id="@+id/user_title"
...
android:textCursorDrawable="@drawable/shape_cursor" />
<EditText
android:id="@+id/user_account"
...
android:textCursorDrawable="@drawable/shape_cursor" />
<EditText
android:id="@+id/user_pwd"
...
android:textCursorDrawable="@drawable/shape_cursor" />
<EditText
android:id="@+id/user_note"
...
android:textCursorDrawable="@drawable/shape_cursor" />
...
</RelativeLayout>
</ScrollView>
</RelativeLayout>
3. 为备注栏制作专用的阴影图
新的点九图更改了阴影的x偏移值,使备注栏的左侧不至于过白,和背景分不清楚。
在res/drawable/edt_bg_normal_note.xml
中修改为:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/edt_bg_shadow_note" android:state_focused="true" />
...
</selector>
参考
- 【新提醒】关于虚拟按键高度,懂得且感兴趣过来探讨一下 - LG G2 安卓论坛 机锋论坛 http://bbs.gfan.com/android-7971983-1-1.html
- Android 5.x新特性之elevation(阴影),tinting(着色)以及clipping(剪裁) - 笨笨丫头~双 - 博客园 https://www.cnblogs.com/ai394495243/p/5075758.html
- Android 动画 - TranslateAnimation位移动画 - CSDN博客 http://blog.csdn.net/shibin1990_/article/details/51602564
- 颜色相关
- ANDROID TEXTVIEW 设置字体颜色 - CSDN博客 http://blog.csdn.net/maigan323/article/details/7026218/
- 百分比换算十六进制透明度 - CSDN博客 http://blog.csdn.net/lyltiger/article/details/48292419
- android颜色渐变如何实现从四周往中心渐变 或者从中心往四周渐变 都行,不是 从左往右_百度知道 https://zhidao.baidu.com/question/329382365.html
- colorAccent,colorPrimary,colorPrimaryDark……来这里你就明白了 - Louie81的博客 - CSDN博客 http://blog.csdn.net/Louie81/article/details/78789285
- EditText相关
- Android:EditText 多行显示及所有属性 - CSDN博客 http://blog.csdn.net/qyf_5445/article/details/8651740
- Android 设置EditText光标Cursor颜色及粗细 - 享受技术带来的快乐 - CSDN博客 http://blog.csdn.net/jdsjlzx/article/details/45075865
- Android 如何让EditText不自动获取焦点 - java豆子 - 博客园 https://www.cnblogs.com/error404/archive/2012/12/28/2837294.html
- http://m.blog.csdn.net/hotlinhao/article/details/41821279
- Android中EditView TextView (padding失效)使用setBackgroundDrawable或setBackgroundResource则xml中设置的 Padding失效 - 风一样的男人 - CSDN博客 http://blog.csdn.net/a2241076850/article/details/73481378
- Android 对TextView添加删除线,下划线,加粗,斜体等效果 - CSDN博客 http://blog.csdn.net/lzyang187/article/details/50695563
- 真正解决TextView行间距、字间距的问题 - CSDN博客 http://blog.csdn.net/peter6359312/article/details/52370949