天天看点

使用Kotlin&Anko, 扔掉XML开发Android应用

尝鲜使用Kotlin写了一段时间Android。说大幅度的减少了Java代码一点不夸张。用Java的时候动不动就new一个<code>OnClickListener()</code>匿名类,动不动就类型转换的地方都可以省下很多。更不用说特殊的地方使用<code>data class</code>更是少些不知道多少代码。

类型安全,不再需要那么多的<code>findById()</code>之后的类型转换。

null安全,Kotlin里,如果一个变量用?表示为可空,并且使用?之后再调用的时候,即使变量为空也不会引发异常。

无需设备解析XML,因为Anko本质是代码实现的界面和布局,所以省去了这些麻烦。

代码复用,可以通过继承<code>AnkoComponent</code>的方式实现代码复用。XML布局是每一个Activity,每一个View各自专属一个,

代码复用比较少。

来一个列子看一下。为了不太墨迹,一些不必要的xml声明此处略去。

首先,安装一个Kotlin的插件是必须的。有了这个插件才可以使用Kotlin,然后才可以使用Anko。安装这个插件和Android Studio里安装别的插件市一样的。只需要使用Kotlin查找就可以找到,之后安装即可。

在build.gradle里添加下面的代码:

然后sync一把。配置的问题解决。

首先创建一个ListView的item点击之后跳转的activity。这里叫做<code>TabDemo1</code>。

现在就创建这个listview,并在listview的item点击之后调转到相应的activity去。

这个listview非常简单,只在一个竖直的布局中放置,并且宽度和高度都是填满竖直

布局。

分别解释:

竖直布局。本质是<code>LinearLayout</code>,并且<code>orientation</code>的值为<code>vertical</code>。但是

水平方向的就没有vetialLayout这种可以直接使用的了,需要自己写明orientation。

创建一个listview。

给这个listview添加adapter。这里简单实用<code>ArrayAdapter&lt;String&gt;</code>。

添加<code>OnItemClickListener</code>。<code>object : AdapterView.OnItemClickListener</code>用来

创建实现某个接口的匿名类。

<code>startActivity&lt;TabDemo1&gt;()</code>,是Anko的语法糖。<code>startActivity(SourceActivity.this, DestActivity.class)</code>

可以直接简化为<code>startActivity&lt;DestActivity&gt;()</code>。简单了不少。

在<code>lparams</code>中设置layout params相关的内容。默认的都是wrap content。这个设置为

宽、高都为match parent。

热身结束。我们来开始真正的开发阶段。

下面要开发的是一个日记App。一共有三个tab,第一个是日记列表,第二个tab是写日记,第三个tab可以设置一些字体大小等(这里只用来占位,不做实现)。

每一个tab都用一个<code>Fragment</code>来展示内容。这三个tab分别<code>HomeListFragment</code>, <code>DetailFragment</code>,<code>DiarySettingsFragment</code>。这个三个fragment都在一个叫做<code>TabDemo1</code>的托管Activity里。

现在就从这个托管activity:<code>TabDemo1</code>开始。这里我们不使用默认的<code>ActionBar</code>,而是用完全自定义的方式来写一个我们自己的action bar。所以需要把界面设定为全屏模式。设置全屏的模式的方法有很多,我们用设置style的方式来实现。

之后把这个style应用在activity在AndroidManifest.xml配置中。

这个时候这个托管activity的界面布局就是一个完全的白板了。这个白板现在要分为上中下三部分。上部为我们自定义的action bar,最下面的是tab bar,剩下的部分就是每个tab的内容的fragment。

我们来看一下这个布局应该怎么写:

前文的例子用了一个<code>verticalLayout</code>, 这里用的是<code>relativeLayout</code>的布局。

这里是自定义action bar。使用换一个<code>linearLayout</code>。如前所述,要横向布局linear layout

就需要单独的指定orientation:<code>orientation =LinearLayout.HORIZONTAL</code>。这里比较简单,只有一个显示title的text view。

这里需要注意<code>gravity = Gravity.CENTER_HORIZONTAL or Gravity.CENTER_VERTICAL</code>

可以直接写成<code>gravity = Gravity.CENTER</code>。这里是为了突出<code>or</code>的用法。Kotlin里的<code>or</code>

就是java的<code>|</code>操作符的作用。

这部分的布局是tab bar。

这里用的是<code>weightTextView</code>而不是<code>textView</code>。后面会详细的讲解这一部分。

给tab bar添加style。此style不是彼style。这个style,会遍历tab bar的linear layout内部的全部的view,然后根据<code>when</code>表达式匹配对应的规则,之后给对应于规则的view设置相应的属性。比如,这里会用when语句查看view是否为textView,如果是的话就给这个view设置padding、drawable padding、text size以及gravity属性。tab bar的linear layout有三个text view,所以他们都会被设置这些属性。

每一个tab的内容展示用fragment就是这里了。准确的说是fragment的container。

这个container是一个framelayout。在action bar之下,在tab bar之上。在布局的时候有<code>below(ID_TOP_BAR), above(ID_BOTTOM_TAB_BAR)</code>。<code>ID_TOP_BAR</code>和<code>ID_BOTTOM_TAB_BAR</code>就分别是action bar和tab bar的id值。这些id值自由设定。

另外,在java写的时候常用的<code>findViewById()</code>方法在Kotlin和Anko中可以改为的<code>find&lt;FrameLayout&gt;(ID_FRAMELAYOUT)</code>。不见得简单,但是增加了类型安全。不用再强制类型转换。也不用担心相关的错误再发生。

上文第4点用到了<code>weightTextView</code>。这是一个自定义的view。在Anko布局中,可以根据自己的需要自定义各种各样的view。但是,需要经过一个小小的处理之后才可以使用到Anko的布局中。这个小小的处理就叫做扩展。下面看看如何给Anko添加<code>weightTextView</code>扩展的。

首先自定义一个view:<code>WeightTextView</code>。

附加解释:

方法<code>setSelected()</code>是被迫添加的。在使用Anko,相当于使用代码开发Android布局的时候<code>selector</code>不起作用。只好把点击后的高亮效果写在自定义的text view里。

下面看看如何扩展Anko,来使用我们上面的自定义view。

这里简单介绍一下。拿官网的例子说一下:

现在有这么一个<code>HTML</code>类,那么调用的时候可以这样:

在这么一个lambda表达式里就可以直接这样调用<code>HTML</code>类的方法了,中间的过程是怎么样的呢

其实灰常的简单呢。在方法<code>html()</code>里,参数是一个<code>HTML</code>类的扩展方法,并且此方法无参,返回<code>Unit</code>(java的<code>void</code>)。

在方法执行的过程中,首先初始化了<code>HTML</code>。之后调用了这个作为参数传入的扩展方法。在具体调用<code>html()</code>方法的时候,可以只简单写一个lambda表达式作为传入的<code>HTML</code>扩展方法。既然是一个类的扩展方法,那当然可以调用这个类内部的方法了。

为了帮助理解,这里给出一个参数是方法的方法:

第一个是用lambda表达式作为传入方法,第二个是已经定义好的一个方法作为传入方法。

本文中的重点在于使用Anko做布局,具体的逻辑处理java写和Kotlin写没有什么区别。这里只简单介绍一下。

为了保证兼容,这里使用Support v4来处理Fragment的显示等操作。在activity的一开始就把需要的fragemnt都加载进来。

每一个tab项被点击的时候的处理:

在开始之前需要考虑一个很严重的事情:数据存在什么地方。本来应该是SQLite或者存在云上的。存在云裳就可以实现同一个账号登录在任何地方都可以同步到同样的内容。这里只简单模拟,存放在app的内存里。存放在<code>Application</code>派生类<code>AnkoApplication</code>的

静态属性<code>diaryDataSource</code>里。<code>diaryDataSource</code>是一个<code>ArrayList</code>一样的列表。

<code>HomeListFragment</code>类作为第一个tab内容展示fragment,用来显示全部的日记列表的布局就非常简单了,和我们前面的例子没有什么太大的差别。就是在一个verticalLayout里放一个list view。这个list view的data source只需要一个列表。

在activity里的布局可以直接写<code>vertical{}</code>,但是在fragment里不可以这样。直接写<code>vertical{}</code>就已经把这个layout添加到父view上了,这fragment里是不行的。在fragment里需要创建一个单独的view,并返回。用<code>with</code>语句来创建这样一个单独的view。

在vertial layout里添加了一个textview。

上面一步创建的textview作为list view没有数据的时候显示的empty view来使用。

日记的内容包括,日记title,日记本身的内容还有日记的日期。

所以布局上就包括日记的title、内容输入用的<code>EditText</code>以及为了说明用的text view,还有edit text里的hint。最后还有一个选择

日期的控件。

需要注意打星号的地方。按钮在点击之后会弹出一个dialog fragment来显示日期view。用户可以在这个日期view里选择相应的日期。但是,如何从日期dialog fragment传递选择的日期给<code>DetailFragment</code>呢?这里就涉及到两个fragment之间传递数据的问题。

选择日期的dialog fragment是<code>DatePickerFragment</code>。

首先<code>DatePickerFragment</code>要继承<code>DialogFragment</code>之后override方法<code>onCreateDialog(savedInstanceState: Bundle)</code>。在这个方法里使用上面代码创建一个包含日期选择器的dialog。

在选择日期的时候,会触发<code>DatePicker</code>的<code>OnDateChangedListener</code>接口的<code>onDateChanged</code>方法。我们在这个方法里记录选择好的日期数据,在dialog的positive按钮点击之后把这个数据发送给<code>DetailFragment</code>。

那么怎么发送呢?使用target fargment方法。在detail fragment弹出dialog fragment的时候,把detail fragment设置为target fragment。

在标星下面的一行代码中。<code>datePicker.setTargetFragment(this@DetailFragment,DetailFragment.REQUEST_DATE)</code>将<code>DetailFragment</code>设定为target fragment,并且指定<code>REQUEST_DATE</code>这code,为以后取出数据使用。

在positive按钮点击之后执行方法<code>sendResult</code>回传数据

调用<code>targetFragment</code>的<code>onActivityResult()</code>方法来回传日期数据。

在<code>DetailFragment</code>中通过override方法<code>onActivityResult()</code>来接收数据。

日期数据传输这部分到这里结束。

全文也可以在这里画上一个句点了。以上还有很多关于Anko没有使用的地方。Anko也是可以实现代码界面分离的。继承<code>AnkoComponent</code>可以写出独立的布局文件,并且可以用anko preview插件来预览界面效果。就拿setting这个tab的fragment来举例:

首先定义一个独立的布局文件:

把这个布局文件用在<code>DiarySettingsFragment</code>上:

然后这个布局还可以用在我们刚刚创建的<code>TempActivity</code>上:

Activity上使用就简单很多了,只需要这么一句<code>SettingsUI&lt;TempActivity&gt;().setContentView(this)</code>。

欢迎加群互相学习,共同进步。QQ群:iOS: 58099570 | Android: 572064792 | Nodejs:329118122 做人要厚道,转载请注明出处!

本文转自张昺华-sky博客园博客,原文链接:http://www.cnblogs.com/sunshine-anycall/p/5300305.html,如需转载请自行联系原作者

继续阅读