09 Anykey右分页重新设计

本文首发于我的Hexo博客:https://likianta.coding.me/2017/PassportPandoraPrj/1229172454/

先来看最终的效果图:

本章内容是根据上章制定好的规范进行的重新设计,其中主界面的两个分页布局变化幅度很大。

本章重点讲解右分页布局的重制,左分页布局会在下章讲解分组设计前补充。

一、重新制作右分页布局

在正式开始制作之前,我们需要预先下载两个软件:

  1. Axure PR 8:用于原型设计,然后根据此设计来实现工程布局
  2. 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%7F50%半透明
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使用CardViewCardView缺点见这里:Home · dmytrodanylyk/shadow-layout Wiki
7使用点九图制作ok

最终我们选择了点九图制作,这里推荐使用Android Shadow Generator在线网站来生成点九图。你可以自定义边角弧度、阴影偏离方向、blur值以及阴影颜色,理论上可以完美实现和Axure设计图一致的阴影效果:

注意:点九图参数设置:

不同的分辨率对应的参数值是不一样的。总的来说我们需要制作以下三种点九图:

切图Radius(边框圆角半径)/pxx偏移/pxy偏移/pxblur备注
一倍切图(360px*640px)6558一倍切图就是我们在设计稿中的分辨率
二倍切图(720px*1280px)12101016二倍切图就是xhdpi等级
三倍切图(1080px*1920px)18151524三倍切图就是xxhdpi等级。
我们使用的Pixel模拟器就是这个等级,其dp-px换算关系为1dp=3px

因此我们最好做三种贴图并放到对应的分辨率目录(不过为了测试方便,这里我只做了三倍切图的阴影)。

属性如下:

BasicFill & OutlineAdvancedPreview备注
Round = 18UnenabledBox 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偏移值,使备注栏的左侧不至于过白,和背景分不清楚。

文件下载:edt_bg_shadow_note.9.png

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>

参考

  1. 【新提醒】关于虚拟按键高度,懂得且感兴趣过来探讨一下 - LG G2 安卓论坛 机锋论坛 http://bbs.gfan.com/android-7971983-1-1.html
  2. Android 5.x新特性之elevation(阴影),tinting(着色)以及clipping(剪裁) - 笨笨丫头~双 - 博客园 https://www.cnblogs.com/ai394495243/p/5075758.html
  3. Android 动画 - TranslateAnimation位移动画 - CSDN博客 http://blog.csdn.net/shibin1990_/article/details/51602564
  4. 颜色相关
    1. ANDROID TEXTVIEW 设置字体颜色 - CSDN博客 http://blog.csdn.net/maigan323/article/details/7026218/
    2. 百分比换算十六进制透明度 - CSDN博客 http://blog.csdn.net/lyltiger/article/details/48292419
    3. android颜色渐变如何实现从四周往中心渐变 或者从中心往四周渐变 都行,不是 从左往右_百度知道 https://zhidao.baidu.com/question/329382365.html
    4. colorAccent,colorPrimary,colorPrimaryDark……来这里你就明白了 - Louie81的博客 - CSDN博客 http://blog.csdn.net/Louie81/article/details/78789285
  5. EditText相关
    1. Android:EditText 多行显示及所有属性 - CSDN博客 http://blog.csdn.net/qyf_5445/article/details/8651740
    2. Android 设置EditText光标Cursor颜色及粗细 - 享受技术带来的快乐 - CSDN博客 http://blog.csdn.net/jdsjlzx/article/details/45075865
    3. Android 如何让EditText不自动获取焦点 - java豆子 - 博客园 https://www.cnblogs.com/error404/archive/2012/12/28/2837294.html
    4. http://m.blog.csdn.net/hotlinhao/article/details/41821279
    5. Android中EditView TextView (padding失效)使用setBackgroundDrawable或setBackgroundResource则xml中设置的 Padding失效 - 风一样的男人 - CSDN博客 http://blog.csdn.net/a2241076850/article/details/73481378
    6. Android 对TextView添加删除线,下划线,加粗,斜体等效果 - CSDN博客 http://blog.csdn.net/lzyang187/article/details/50695563
    7. 真正解决TextView行间距、字间距的问题 - CSDN博客 http://blog.csdn.net/peter6359312/article/details/52370949
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值