Android编程示例:创建机场计划模拟器应用程序
在本文中,我们将演示如何使用android studio和java编程语言创建一个示例android应用程序,从“临时”实现高级响应用户界面的功能。本文中讨论的应用程序将实现机场航班时刻表模拟的功能。在开发生命周期中,我们将实现android应用程序的响应式用户界面,用于呈现“到达”和“离开”航班的列表,并提供动态生成和更新实时模式中的航班信息的功能。
我们将大力强调几个java语言编程方面,并深入研究允许我们提供高级android应用程序的编程技术的数量,包括从一开始就创建响应app's drawer和navigation bar应用程序的方面,提供我们自己的custom views并且layouts,如custom search view bar with action button,覆盖默认功能通用app's action bar,保持tabbed layout,渲染recycler views ,不同于listviews或gridviews允许创建的自定义外观的项目lists由应用程序所呈现的数据,与创建各种布局多重嵌套fragments,使用 bottom navigation view等
除了应用程序的界面特定主题,我们还将了解如何创建一个用java编写的高效代码来实现生成和操作数据内容的功能,以及如何提供操作数据的代码和应用程序的用户界面。
具体来说,我们将实施机场航班时刻表模拟器的功能,该模拟器生成随机航班数据集并通过在实时模式中过滤掉航班来模拟航班到达和延长时间线来操纵这些数据,动态更新航班列表被渲染。为此,我们将使用和讨论诸如使用android应用程序的后台任务,使用计时器等主题。
背景
先决条件(在我们开始之前...)
在我们开始讨论之前,让我们花点时间仔细研究一下到目前为止我们特别需要的开发工具和库来构建和运行我们的第一个android应用程序。
因为,我们即将使用java编程语言来部署我们运行android的第一个应用程序,我们必须安装java se。为此,我们需要从/下载并安装java standard edition - se平台。反过来,java se平台包含在pc上构建和运行用java编写的代码所需的所有库和模块。
在我们成功安装java se平台之后,我们还需要正确安装ide和创建android应用程序项目所需的特定库,并构建运行我们正在部署的应用程序的代码。各种ide,编程语言和库的数量,例如由android开发社区授权的microsoft visual studio / c#.net xamarin或android studio,可以有效地用于创建和部署android应用程序。
在本文中,为了提供android应用程序开发生命周期的效率,平台兼容性以及开发流程,我们将特别使用android studio和java编程语言来实现此目的。
这就是为什么,在我们在之前的配置步骤中安装java se平台之后,需要并强烈建议在开发机器上下载并安装android studio(https://developer.android.com/studio/)。
我们可能已经注意到,安装的android studio包含许多开发工具,包括ide,java sdk和ndk库,android系统模拟器,gradle / maven - java编译器的“make”实用程序,可以更轻松地编译和编译用java编程语言编写的链接代码。
反过来,android studio的ide是一个高效且响应迅速的工具,用于轻松创建和编辑android源码以及实现基本应用程序功能的java代码。
除了高效便捷的ide之外,android studio软件包还包括为各种目标(手机,平板电脑,可穿戴设备,android电视......)开发android应用程序所需的java sdk库。具体来说,android studio ide允许通过sdk管理器下载和安装适用于各种android系统版本的sdk,sdk manager是android studio的一部分,或者可选地定期使用java sdk发行版中的本机sdk管理器。
为了编译和链接正在创建的应用程序,android studio的软件包还包括上面提到的gradle / maven'make'实用程序。在android studio中创建我们的第一个android应用程序项目时,gradle组件已下载并配置为与android studio的ide一起使用。每次,当我们构建和运行android应用程序的项目时,gradle实用程序正在执行编译和链接特定的任务,例如创建包含内置android应用程序的apk包,可以在模拟器上运行或者一个android设备。在开发生命周期中,由于已经创建和配置了项目,因此我们可以使用gradle实用程序的多个版本,就像在本文的项目创建部分中讨论的那样。
为了能够在调试开发阶段运行应用程序,android studio还包括一个支持各种android系统版本的android设备模拟器,可通过android studio的模拟器管理器从google和android开发社区。在模拟器上运行应用程序与在目标android设备上运行应用程序非常相似。
在本文的下一部分中,我们将演示如何在安装的android studio环境中创建我们的第一个android应用程序项目。
摘要
创建您的第一个android app项目
在我们成功满足上面讨论的所有安装和配置要求之后,我们要做的第一件事就是运行android studio并创建一个项目来实现我们的机场航班时刻表模拟android应用程序功能。为此,我们将使用android studio主对话框切换启动新的android studio项目选项:
在此之后,android项目创建对话框将出现在屏幕上:
在这个对话框中,我们必须指定一个应用程序名称(在这种情况下,它是` airportapp`),公司域(例如,` epsilon.com`)来正确配置应用程序包,项目位置,特别是包name,在我们的例子中是` com.epsilon.airportapp`。在我们提供了创建项目所需的所有信息后,单击此对话框底部的下一个按钮。
在此步骤之后,我们必须正确选择并指定我们的应用程序的目标设备,包括正确的外形(“手机”或“平板电脑”),最小的sdk及其版本,以及android系统发布版本:
在我们成功选择了目标设备和android发布版本之后,我们还必须选择一种应用程序的活动。活动通常是java类实现功能,负责应用程序的主窗口创建,事件处理以及完成其他用户交互特定任务。实际上,扩展泛型activity类或其他派生类的java 类是任何现有android应用程序的主类:
在这种特殊情况下,我们开始我们的第一个android应用程序开发生命周期,选择一个空活动作为我们的机场计划模拟器应用程序的主要活动。此外,我们将定制和增强默认的空活动,以提供执行机场计划模拟任务所需的功能。
android应用程序创建阶段的最后一步是配置基于活动的java类别名,生成特定的活动布局,以及配置应用程序的向后兼容库。为此,我们必须继续下一个配置对话框:
在最后一步中,我指定一个应用程序基于活动的java类名称,该名称将对应于正在生成的特定活动布局xml文件名。此外,我们必须指定是否要提供应用程序与旧版android的向后兼容性。
由于我们已经配置了应用程序的活动,因此在最后阶段生成特定项目并打开android studio的ide主窗口:
在本文的下一部分中,我们将简要介绍使用android studio创建的android应用程序的项目结构。
android app的项目结构
在这一点上,让我们仔细看看应用程序项目创建后打开的android studio ide主窗口左上角的应用程序解决方案树。通常,解决方案树显示正在创建的项目的内容,该内容与保存到特定位置的目录结构完全对应(例如,“ d:\ airportapp ”)。
androidmanifest.xml中
文件夹“清单”是显示在应用解决方案树顶部的第一个文件夹。它基本上只包含一个文件' androidmanifest.xml '。以下文件主要包含运行正在创建的应用程序所需的xml格式的所有配置数据。androidmanifest.xml文件具有以下结构,对于所有android应用程序都完全相同:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.epsilon.arthurvratz.airportapp"> <application android:allowbackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundicon="@mipmap/ic_launcher_round" android:supportsrtl="true" android:theme="@style/apptheme.noactionbar"> <activity android:name=".airportactivity" android:theme="@style/apptheme.noactionbar" android:windowsoftinputmode="statehidden" android:configchanges="orientation|screensize|keyboard|keyboardhidden"> <intent-filter> <action android:name="android.intent.action.main" /> <category android:name="android.intent.category.launcher" /> </intent-filter> </activity> </application> </manifest>
androidmanifest.xml文件的第二行包含manifest标记,其属性提供命名空间和应用程序的包名称信息。它还包含一个嵌套标签application ,该标签具有定义标签,文本方向和创建的应用程序的一对图标的属性数。由应用程序标签的属性指定的图标和标签基本上显示在应用程序的主窗口中。应用程序标记还包含一个定义默认应用程序主题的属性(例如,android:theme="@style/apptheme")。或者,我们可能希望修改现有的或向应用程序标记添加更多属性,以便提供应用程序主窗口的自定义外观和行为。例如,我们可能想要更改值android:theme属性,以便我们的应用程序将覆盖默认的泛型并使用其自己的应用程序操作栏实现。为此,我们需要将以下标记的值更改为android:theme="@style/apptheme.noactionbar".
通常,application标记具有嵌套标记的数量,例如activity标记,用于提供主应用程序活动的一组配置属性。默认情况下,activity标记只有一个属性,用于定义主应用程序活动的名称(例如android:name=".airportactivity")。要修改应用程序的主要活动配置参数,我们可能需要向以下标记添加更多属性:
android:theme="@style/apptheme.noactionbar" android:windowsoftinputmode="statehidden" android:configchanges="orientation|screensize|keyboard|keyboardhidden">
在这种特殊情况下,我们将以下配置属性添加到activity上面列出的机场计划模拟器应用主标签中。第一个属性是我们之前在application 上面标记中指定的属性的副本。以下属性用于指定将显示正在运行的应用程序中的默认通用应用程序操作栏。第二个属性android:windowsoftinputmode="statehidden"用于指定在启动应用程序时不会自动呈现的软输入方法。最后一个属性 android:configchanges="orientation|screensize|keyboard|keyboardhidden"提供应用程序覆盖的配置更改列表。这意味着应用程序将处理以下更改,而不是android系统。具体来说,应用程序将处理屏幕旋转并根据当前屏幕方向呈现正确的界面布局变化(例如' portrait'或' landscape')。
应用程序标记还包含最内层嵌套标记的数量,例如intent-filter,action和category。在action和category里面的意图标签intent-filter标签指定的主应用程序的入口点。特别是,这些标签指定当前'.airportactivity' 是主应用程序的活动:
<intent-filter> <action android:name="android.intent.action.main" /> <category android:name="android.intent.category.launcher" /> </intent-filter>
gradle脚本
现在,让我们来看看位于应用解决方案树底部的' gradle scripts '兄弟。以下文件夹包含配置上一节中提到的gradle'make'实用程序所需的所有脚本文件,包括项目' '或' '模块的两个' build.gradle '文件实例。第一个build.gradle文件包含以下内容:airportappapp
// top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { repositories { google() jcenter() } dependencies { classpath 'com.android.tools.build:gradle:3.1.3' // note: do not place your application dependencies here; they belong // in the individual module build.gradle files } } allprojects { repositories { google() jcenter() } } task clean(type: delete) { delete rootproject.builddir }
apply plugin: 'com.android.application' android { compilesdkversion 28 defaultconfig { applicationid "com.epsilon.arthurvratz.airportapp" minsdkversion 24 targetsdkversion 28 versioncode 1 versionname "1.0" testinstrumentationrunner "android.support.test.runner.androidjunitrunner" } buildtypes { release { minifyenabled false proguardfiles getdefaultproguardfile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { implementation filetree(dir: 'libs', include: ['*.jar']) implementation 'com.android.support:appcompat-v7:28.0.0-alpha3' implementation 'com.android.support:support-v4:28.0.0-alpha3' implementation 'com.android.support:support-v13:28.0.0-alpha3' implementation 'com.android.support:design:28.0.0-alpha3' implementation 'com.android.support:recyclerview-v7:28.0.0-alpha3' implementation 'com.android.support.constraint:constraint-layout:1.1.2' testimplementation 'junit:junit:4.12' androidtestimplementation 'com.android.support.test:runner:1.0.2' androidtestimplementation 'com.android.support.test.espresso:espresso-core:3.0.2' implementation 'org.jetbrains:annotations-java5:15.0' }
<span courier new",courier,mono; font-size: 12px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; line-height: normal; orphans: 2; text-align: left; text-decoration: none; text-indent: 0px; text-shadow: none; text-transform: none; -webkit-text-stroke-width: 0px; white-space: pre; word-spacing: 0px;">
implementation 'com.android.support:appcompat-v7:28.0.0-alpha3' implementation 'com.android.support:support-v4:28.0.0-alpha3' implementation 'com.android.support:support-v13:28.0.0-alpha3' implementation 'com.android.support:design:28.0.0-alpha3' implementation 'com.android.support:recyclerview-v7:28.0.0-alpha3' implementation 'com.android.support.constraint:constraint-layout:1.1.2'</span>
#thu jul 26 06:49:16 eest 2018 distributionbase=gradle_user_home distributionpath=wrapper/dists zipstorebase=gradle_user_home zipstorepath=wrapper/dists distributionurl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
## this file must *not* be checked into version control systems, # as it contains information specific to your local configuration. # # location of the sdk. this is only used by gradle. # for customization when using a version control system, please read the # header note. #thu jul 26 15:02:12 eest 2018 sdk.dir=c\:\\androidsdk
在此文件中,我们可以指定gradle实用程序版本或android sdk位置的绝对路径。为此,我们必须修改这两个文件的以下行:
distributionurl=https\://services.gradle.org/distributions/gradle-4.9-all.zip
sdk.dir=c\:\\android\androidstudio\sdk
应用程序的活动和布局文件
<resources> <!-- base application theme. --> <style name="apptheme" parent="base.theme.appcompat.light.darkactionbar"> <!-- customize your theme here. --> <item name="colorprimary">@color/colorprimary</item> <item name="colorprimarydark">@color/colorprimarydark</item> <item name="coloraccent">@color/coloraccent</item> </style> </resources>
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.constraintlayout 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" tools:context=".airportactivity"> <textview android:layout_width="wrap_content" android:layout_height="19dp" android:text="hello world!" app:layout_constraintbottom_tobottomof="parent" app:layout_constraintleft_toleftof="parent" app:layout_constraintright_torightof="parent" app:layout_constrainttop_totopof="parent" /> </android.support.constraint.constraintlayout>
package com.epsilon.airportapp; import android.support.v7.app.appcompatactivity; import android.os.bundle; public class airportactivity extends appcompatactivity { @override protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.activity_airport); } }
应用程序的主要布局bluep rint
此时,我们的主要目标是创建机场计划模拟应用程序的主要布局设计草图。更具体地说,主应用程序的布局将具有以下外观:
设计应用程序的主要布局
<?xml version="1.0" encoding="utf-8"?> <!-- use drawerlayout as root container for activity --> <android.support.v4.widget.drawerlayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/airport_drawer_layout" android:layout_width="match_parent" android:layout_height="match_parent" android:fitssystemwindows="true"> <include layout="@layout/content_frame" android:layout_width="match_parent" android:layout_height="wrap_content"/> <android.support.design.widget.navigationview android:id="@+id/airport_navigation_view" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_gravity="start" android:fitssystemwindows="true" app:menu="@menu/main_menu" app:headerlayout="@layout/nav_header_frame"/> </android.support.v4.widget.drawerlayout>
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.constraintlayout 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"> <linearlayout android:id="@+id/search_bar" android:layout_width="match_parent" android:layout_height="wrap_content" app:layout_constraintbottom_totopof="@+id/airport_fragment_container" app:layout_constraintend_toendof="parent" app:layout_constrainthorizontal_bias="1.0" app:layout_constraintstart_tostartof="parent" app:layout_constrainttop_totopof="parent" app:layout_constraintvertical_bias="0.02" android:focusable="true" android:focusableintouchmode="true"> <requestfocus /> <android.support.v7.widget.searchview android:id="@+id/searchable" android:layout_width="match_parent" android:layout_height="wrap_content"/> </linearlayout> <framelayout android:id="@+id/airport_fragment_container" android:layout_width="match_parent" android:layout_height="0dp" android:layout_marginend="8dp" android:layout_marginstart="8dp" app:layout_constraintbottom_totopof="@+id/flights_navigation" app:layout_constraintend_toendof="parent" app:layout_constrainthorizontal_bias="0.0" app:layout_constraintstart_tostartof="parent" app:layout_constrainttop_tobottomof="@+id/search_bar"> </framelayout> <android.support.design.widget.bottomnavigationview android:id="@+id/flights_navigation" android:layout_width="match_parent" android:layout_height="wrap_content" app:layout_constraintbottom_tobottomof="parent" app:layout_constraintend_toendof="parent" app:layout_constrainthorizontal_bias="1.0" app:layout_constraintstart_tostartof="parent" app:layout_constrainttop_tobottomof="@+id/airport_fragment_container" app:menu="@menu/flights_navigation" android:theme="@style/apptheme"/> </android.support.constraint.constraintlayout>
<?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android" > <group android:checkablebehavior="single"> <item android:id="@+id/flights" android:icon="@drawable/ic_flight_black_24dp" android:title="@string/flights" /> <item android:id="@+id/about" android:icon="@drawable/ic_star_black_24dp" android:title="@string/about" /> </group> </menu>
<?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:layout_width="match_parent" android:layout_height="240dp" android:background="@drawable/airport_nav_header" android:gravity="bottom" android:orientation="vertical" android:padding="16dp" android:theme="@style/themeoverlay.appcompat.dark"> <imageview android:id="@+id/imageview" android:layout_width="103dp" android:layout_height="99dp" app:srccompat="@mipmap/ic_launcher_round" /> <space android:layout_width="352dp" android:layout_height="10dp" /> <textview android:id="@+id/airport_app_title" android:layout_width="match_parent" android:layout_height="wrap_content" android:fontfamily="verdana" android:text="@string/nav_header" android:textcolor="@android:color/background_light" android:textisselectable="false" android:textsize="30sp" /> <textview android:id="@+id/airport_app_author" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/airport_app_author" /> </linearlayout>
- ' imageview' - 用于显示机场应用程序的图标;
- ' space' - 在线性布局中创建特定视图之间的间隙;
- ' textview' - 打印机场应用程序的标题或作者的详细信息;
最后,由' navigationview'及其蓝图呈现的应用程序的抽屉布局将具有以下外观:
使用操作按钮创建自定义searchview
searchview是出现在主应用程序窗口顶部的机场计划模拟器应用程序的第一个控件。此时,让我们回到他标记的'content_frame.xml片段,声明搜索视图位于以下布局文件的所有其他视图之前,由' :包裹起来:'.tandroid.support.v7.widget.searchviewlinearlayout<font color="#007000" face=""segoe ui",arial,sans-serif">'</font>
<linearlayout android:id="@+id/search_bar" android:layout_width="match_parent" android:layout_height="wrap_content" app:layout_constraintbottom_totopof="@+id/airport_fragment_container" app:layout_constraintend_toendof="parent" app:layout_constrainthorizontal_bias="1.0" app:layout_constraintstart_tostartof="parent" app:layout_constrainttop_totopof="parent" app:layout_constraintvertical_bias="0.02" android:focusable="true" android:focusableintouchmode="true"> <requestfocus /> <android.support.v7.widget.searchview android:id="@+id/searchable" android:layout_width="match_parent" android:layout_height="wrap_content"/> </linearlayout>
public class searchablewithbuttonview extends view { // searchview basic functionality implementation java-code goes here... }
public void setupsearchablewithbutton() { // set background color of the search view ((viewgroup)m_searchview.findviewbyid(android.support.v7.appcompat.r.id.search_mag_icon). getparent()).setbackgroundcolor(color.parsecolor("#ffffff")); // set default custom search of the search view button icon and look of the custom search view this.setdefaultsearchicon(); this.setupiconifiedbydefault(); // set default search hint displayed in the search view's edit text view m_searchview.setqueryhint("type here..."); // set default query text and remove focus from the search view m_searchview.setquery("", false); getrootview().requestfocus(); // instantinate the search view object and set default action button click event listener m_searchview.findviewbyid(android.support.v7.appcompat.r.id.search_mag_icon). setonclicklistener(new searchableviewlistener()); // instantinate the search view object viewgroup llsearchview = ((viewgroup)m_searchview.findviewbyid( android.support.v7.appcompat.r.id.search_mag_icon).getparent()); // instantinate object of the text editable inside the search view edittext searchedittext = llsearchview.findviewbyid( android.support.v7.appcompat.r.id.search_src_text); // remove the search view text editable default selection searchedittext.setselected(false); // set text editable click event listener searchedittext.setonclicklistener(new searchableviewlistener()); // set text editable ontextchange listener searchedittext.addtextchangedlistener(new searchableviewlistener()); }
public class searchableviewlistener implements onclicklistener, textwatcher { @override public void onclick(view view) { // check if the custom search view button was clicked if (android.support.v7.appcompat.r. id.search_mag_icon == view.getid()) { // if so, perform a check if the default action bar icon was set if (!isdefaulticon) { // if not, set the default icon by invoking setdefaultsearchicon() method setdefaultsearchicon(); // terminate the onclick handler method execution return; } // invoke onclick(...) method from the main app's activity class m_clicklistener.onclick(view); } // otherwise, set navigation-back search icon else setnavbacksearchicon(); } @override public void beforetextchanged(charsequence charsequence, int i, int i1, int i2) { // invoke the beforetextchange(...) method from app's activity class (e.g. its parent) m_textwatcherlistener.beforetextchanged(charsequence, i, i1, i2); } @override public void ontextchanged(charsequence charsequence, int i, int i1, int i2) { <span segoe ui",arial,sans-serif; font-size: 14px; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-decoration: none; text-indent: 0px; text-shadow: none; text-transform: none; -webkit-text-stroke-width: 0px; white-space: normal; word-spacing: 0px;"> // set navigation-back icon and invoke the ontextchanged(...) method </span> <span segoe ui",arial,sans-serif; font-size: 14px; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-decoration: none; text-indent: 0px; text-shadow: none; text-transform: none; -webkit-text-stroke-width: 0px; white-space: normal; word-spacing: 0px;">// from app's activity class (e.g. its parent)</span> setnavbacksearchicon(); m_textwatcherlistener.ontextchanged(charsequence, i, i1, i2); } @override public void aftertextchanged(editable editable) { // perform a check if the editable string is empty if (editable.tostring().isempty()) // if so, set default search view icon setdefaultsearchicon(); // invoke the aftertextchanged(...) method from its parent m_textwatcherlistener.aftertextchanged(editable); } }
另外, “ searchablewithbuttonview”类有以下方法:
下面列出的方法将自定义搜索视图的外观更改为uniconfied:
private void setupiconifiedbydefault() { // disable the iconfied mode to make the search view fill the entire area horizontally m_searchview.seticonified(false); m_searchview.seticonifiedbydefault(false); }
以下方法使用应用程序操作栏按钮的自定义图标替换通用搜索视图的默认图标
private void setdefaultsearchicon() { // replace the default search view icon with the action button icon this.isdefaulticon = true; this.replacesearchicon(r.drawable.ic_dehaze_white_24dp); }
以下方法使用导航后退图标替换默认操作栏按钮图标:
private void setnavbacksearchicon() { // check if the default icon was set if (this.isdefaulticon == true) { // if so, replace search view icon with navigation-back icon this.isdefaulticon = false; this.replacesearchicon(r.drawable.ic_arrow_back_black_24dp); // run the search view icon animation this.setupanimation(); } }
以下方法将默认搜索视图按钮图标替换为从应用程序资源中检索到的图标:
private void replacesearchicon(int resdefaulticon) { // instantinate search view button icon object and set the custom icon // by calling setimagedrawable method that accepts the icon object retrieved // from the app's resources by calling the context's getdrawable(...) method ((imageview)m_searchview.findviewbyid(android.support.v7.appcompat.r.id.search_mag_icon)). setimagedrawable(m_context.getdrawable(resdefaulticon)); // start animating icon this.setupanimation(); }
此方法用于设置搜索视图图标的动画
private void setupanimation() { // instantinate search view icon object final imageview searchiconview = m_searchview.findviewbyid( android.support.v7.appcompat.r.id.search_mag_icon); // compute the icon's width and height values int searchiconwidth = searchiconview.getwidth(); int searchiconheight = searchiconview.getheight(); // instantinate rotateanimation class object and specify the rotation params rotateanimation searchiconanimation = new rotateanimation(0f, 360f, searchiconwidth / 2, searchiconheight / 2); // set animation interpolator searchiconanimation.setinterpolator(new linearinterpolator()); // set animation repeat count searchiconanimation.setrepeatcount(animation.infinite); // set animation duration searchiconanimation.setduration(700); // start animating the icon searchiconview.startanimation(searchiconanimation); // perform a delay for 700ms after the icon animation ends new handler().postdelayed(new runnable() { @override public void run() { searchiconview.setanimation(null); } }, 700); }
通过使用以下方法,我们覆盖了与搜索视图对象一起使用的findviewbyid(...)方法的基本功能:
// override the default findviewbyid method to be used to instantinate // search view object private searchview findsearchviewbyid(int resid) { return ((activity)m_context).findviewbyid(resid); }
通过调用这两个方法,我们设置了主应用程序的activity类中使用的click事件监听器和文本更改事件监听器:
public void setsearchbuttonclicklistener(@nullable onclicklistener clicklistener) { // set click listener class object of its parent m_clicklistener = clicklistener; } public void settextwatchlistener(@nullable textwatcher textwatchlistener) { // set text change watcher listener class object of its parent m_textwatcherlistener = textwatchlistener; }
现在,由于我们已经使用操作按钮实现了自定义搜索视图,现在是时候将其功能添加到主应用程序的活动中,如下所示:
@override protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.activity_airport); // instantinating the drawer layout object m_drawerlayout = findviewbyid(r.id.airport_drawer_layout); // instantinating the navigation view object m_navigationview = findviewbyid(r.id.airport_navigation_view); // instantinating our custom search view object m_searchablewithbuttonview = new searchablewithbuttonview(airportactivity.this, r.id.searchable); // setting up our custom search view m_searchablewithbuttonview.setupsearchablewithbutton(); // adding the text change watcher listener m_searchablewithbuttonview.settextwatchlistener(new searchablewithbuttonlistener()); // adding the search view action button click event listener m_searchablewithbuttonview.setsearchbuttonclicklistener(new searchablewithbuttonlistener()); // setup app's drawer menu click event listener m_navigationview.setnavigationitemselectedlistener(m_navigationbarlistener); // ...
在overriden oncreate方法中,我们通常执行抽屉布局和导航视图对象的即时化,设置我们的自定义搜索视图并添加特定的事件处理程序。要处理各种搜索视图的事件,我们必须声明一个子类' searchablewithbuttonlistener'实现' view.onclicklistener'或' textwatcher'事件处理泛型类:
public class searchablewithbuttonlistener implements view.onclicklistener, textwatcher { @override public void beforetextchanged(charsequence charsequence, int i, int i1, int i2) { } @override public void ontextchanged(charsequence charsequence, int i, int i1, int i2) { } @override public void aftertextchanged(editable editable) { } @override public void onclick(view view) { // perform a check if the app's drawer open if (!m_drawerlayout.isdraweropen(gravitycompat.start)) // if not, open the app's drawer m_drawerlayout.opendrawer(gravitycompat.start); } }
通过本文下一节中讨论的以下类的方法实现的功能。在这种情况下,我们将仅讨论onclick(...)此类中方法的一种实现。以下方法通过调用方法实现应用程序的抽屉打开功能drawerlayout.opendrawer(...) 。
正如我们在opendrawer(...)处理自定义操作栏单击事件后触发该方法时已经讨论的那样,应用程序的抽屉打开,显示应用程序的主菜单。此时我们还必须通过调用'm_navigationview.setnavigationitemselectedlistener(m_navigationbarlistener)'接受侦听器类对象作为其单个参数的方法来提供菜单项单击事件处理。以下代码实现了重写导航菜单项click事件监听器类:
private class navigationbarlistener implements navigationview.onnavigationitemselectedlistener { // this method handles the navigation menu item click events public boolean onnavigationitemselected(menuitem menuitem) { // set item as selected to persist highlight menuitem.setchecked(true); //... if (m_drawerlayout.isdraweropen(gravitycompat.start)) m_drawerlayout.closedrawers(); return true; } }
创建选项卡式应用程序的布局
正如我们已经讨论过的,机场应用程序旨在响应用户的输入并显示各种内容,具体取决于应用程序的抽屉导航菜单中的选项或用户切换的选项卡。特别是在应用程序的抽屉导航菜单中切换“飞行”菜单项后,它通常会呈现选项卡式布局。每个选项卡基本上显示由回收者视图呈现的航班列表。为实现这一点,我们将使用片段。' fragment'是应用程序布局的动态创建和渲染部分,包含其他布局或视图,或两者兼而有之。
在这种情况下,到目前为止我们要做的是创建特定的片段布局和我们自己的实现内容呈现功能的java类。正如我们已经讨论过的那样,两个标签“到达”和“离开”将出现在主应用程序的窗口中。在每个选项卡中,我们将呈现' recyclerview'显示已安排的航班列表。为了提供选项卡式布局功能,我们将使用' tabbedlayout'inside inside' linearlayout',这是flightsfragment当用户在应用程序的抽屉导航菜单中切换第一个菜单项'flight'时显示的根布局。航班片段布局在' res / layout / fragment_flights.xml '文件中实现:
<?xml version="1.0" encoding="utf-8"?> <linearlayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/flights_fragment" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".flightsfragment"> <android.support.design.widget.tablayout android:id="@+id/flights_destination_tabs" android:layout_width="match_parent" android:layout_height="wrap_content" app:tabmaxwidth="0dp" app:tabmode="fixed" app:tabgravity="fill"> <android.support.design.widget.tabitem android:id="@+id/arrivals_tab" android:layout_width="wrap_content" android:layout_height="wrap_content" android:icon="@drawable/ic_flight_land_black_24dp" android:text="@string/arrivals_tab" /> <android.support.design.widget.tabitem android:id="@+id/departures_tab" android:layout_width="wrap_content" android:layout_height="wrap_content" android:icon="@drawable/ic_flight_takeoff_black_24dp" android:text="@string/departures_tab" /> </android.support.design.widget.tablayout> <android.support.v4.view.viewpager android:id="@+id/flights_destination_pager" android:layout_width="wrap_content" android:layout_height="wrap_content"/> <requestfocus/> </linearlayout>
package com.epsilon.arthurvratz.airportapp; import android.net.uri; import android.support.v7.widget.linearlayoutmanager; import android.support.v7.widget.recyclerview; import java.util.arraylist; public class flightsfragmentimpl extends android.support.v4.app.fragment implements arrivalsfragment.onfragmentinteractionlistener, departuresfragment.onfragmentinteractionlistener { public recyclerview m_recyclerview; public recyclerview.adapter m_recycleradapter; public recyclerview.layoutmanager m_layoutmanager; public void setupflightsrecyclerview(recyclerview recyclerview, arraylist<airportdatamodel> dataset) { // setting the recycler view object m_recyclerview = recyclerview; // setting the recycler view has a fixed size m_recyclerview.sethasfixedsize(true); // instantinating the linear layout manager object m_layoutmanager = new linearlayoutmanager(getcontext()); // setting up the recycler view's layout manager m_recyclerview.setlayoutmanager(m_layoutmanager); // instantinating the flights recycler view's adapter object // and adding the flights dataset to the flights recycler view's adapter m_recycleradapter = new flightsrecycleradapter(dataset, getcontext()); // setting up the <span segoe ui",arial,sans-serif; font-size: 14px; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-decoration: none; text-indent: 0px; text-shadow: none; text-transform: none; -webkit-text-stroke-width: 0px; white-space: normal; word-spacing: 0px;">flights recycler view's adapter object</span> m_recyclerview.setadapter(m_recycleradapter); } @override public void onfragmentinteraction(uri uri) { } }
package com.epsilon.arthurvratz.airportapp; import android.content.context; import android.net.uri; import android.os.bundle; import android.support.design.widget.tablayout; import android.support.v4.view.viewpager; import android.view.layoutinflater; import android.view.view; import android.view.viewgroup; public class flightsfragment extends flightsfragmentimpl { private tablayout m_tablayout; private viewpager m_viewpager; final private tabselectedlistener m_tabsellistener = new tabselectedlistener(); public arrivalsfragment m_arrivalsfragment; public departuresfragment m_departuresfragment; private class tabselectedlistener implements tablayout.ontabselectedlistener { @override public void ontabselected(tablayout.tab tab) { m_viewpager.setcurrentitem(tab.getposition()); } @override public void ontabunselected(tablayout.tab tab) { } @override public void ontabreselected(tablayout.tab tab) { } } private onfragmentinteractionlistener mlistener; public flightsfragment() { // required empty public constructor } public static flightsfragment newinstance() { return new flightsfragment(); } @override public void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); } @override public view oncreateview(layoutinflater inflater, viewgroup container, bundle savedinstancestate) { // inflating the flights fragment view's object view flightsfragmentview = inflater.inflate(r.layout.fragment_flights, container, false); // instantinating the tab layout object m_tablayout = flightsfragmentview.findviewbyid(r.id.flights_destination_tabs); // instantinating view pager object m_viewpager = flightsfragmentview.findviewbyid(r.id.flights_destination_pager); // instantinating the tab layout's pager adapter flightsdestpageradapter pageradapter = new flightsdestpageradapter( getchildfragmentmanager(), m_tablayout.gettabcount()); // instantinating the arrivals fragment object m_arrivalsfragment = arrivalsfragment.newinstance(); // instantinating the departures fragment object m_departuresfragment = departuresfragment.newinstance(); // adding the arrivals and departure fragment objects to the view pager adapter pageradapter.add(m_arrivalsfragment); pageradapter.add(m_departuresfragment); // setting up the view pager adapter m_viewpager.setadapter(pageradapter); // adding the generic page sliding event listener m_viewpager.addonpagechangelistener( new tablayout.tablayoutonpagechangelistener(m_tablayout)); m_tablayout.addontabselectedlistener(m_tabsellistener); return flightsfragmentview; } public void onbuttonpressed(uri uri) { if (mlistener != null) { mlistener.onfragmentinteraction(uri); } } @override public void onattach(context context) { super.onattach(context); if (context instanceof onfragmentinteractionlistener) { mlistener = (onfragmentinteractionlistener) context; } else { throw new runtimeexception(context.tostring() + " must implement onfragmentinteractionlistener"); } } @override public void ondetach() { super.ondetach(); mlistener = null; } @override public void onfragmentinteraction(uri uri) { } public interface onfragmentinteractionlistener { // todo: update argument type and name void onfragmentinteraction(uri uri); } }
package com.epsilon.arthurvratz.airportapp; import android.support.v4.app.fragment; import android.support.v4.app.fragmentmanager; import android.support.v4.app.fragmentpageradapter; import java.util.arraylist; public class flightsdestpageradapter extends fragmentpageradapter { private arraylist<fragment> m_fragments = new arraylist<fragment>(); public flightsdestpageradapter(fragmentmanager fragmentmgr, int numberoftabs) { super(fragmentmgr); } public void add(fragment fragment) { m_fragments.add(fragment); } @override public fragment getitem(int position) { return m_fragments.get(position); } @override public int getcount() { return m_fragments.size(); } }
private class navigationbarlistener implements navigationview.onnavigationitemselectedlistener { public boolean onnavigationitemselected(menuitem menuitem) { // set item as selected to persist highlight menuitem.setchecked(true); // instantinate the fragment manager transaction coordinator object m_fragmenttran = m_fragmentmgr.begintransaction(); // perform a check if the flights menu item was selected if (menuitem.getitemid() == r.id.flights) // if so, replace the airport_fragment_container frame layout // with specific flight fragment by using its object. m_fragmenttran.replace(r.id.airport_fragment_container, flightsfragment.newinstance()); else if (menuitem.getitemid() == r.id.about) {} m_fragmenttran.addtobackstack(null); m_fragmenttran.commit(); // check if the app's drawer is still open if (m_drawerlayout.isdraweropen(gravitycompat.start)) // if so, close the app's drawer m_drawerlayout.closedrawers(); return true; } public void setupinitialfragment() { if (m_fragmentmgr == null) // instantinate the support fragment manager object m_fragmentmgr = getsupportfragmentmanager(); // begin fragments transaction m_fragmenttran = m_fragmentmgr.begintransaction(); // add the default flights fragment object and commit transaction m_fragmenttran.add(r.id.airport_fragment_container, flightsfragment.newinstance()).commit(); } }
protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.activity_airport); //m_actiontoolbar = findviewbyid(r.id.airport_actionbar); m_drawerlayout = findviewbyid(r.id.airport_drawer_layout); m_navigationview = findviewbyid(r.id.airport_navigation_view); m_flightsnavigationview = findviewbyid(r.id.flights_navigation); //setsupportactionbar(m_actiontoolbar); //this.setupactionbar(r.drawable.ic_dehaze_white_24dp); m_searchablewithbuttonview = new searchablewithbuttonview(airportactivity.this, r.id.searchable); m_searchablewithbuttonview.setupsearchablewithbutton();
相关文章:
-
-
这里只是对自己的学习进行一次总结,也是为了让自己以后如果长时间不使用快速记忆起来的笔记,如果想要学习,还是去官网看文档比较好一些。、 注意 下面的代... [阅读全文]
-
[译]在Linux上的提高MySQL/MariaDB安全性的12条建议
MySQL 是世界上最流行的开源数据库系统,而MariaDB(MySQL的一个分支)是世界上发展最快的开源数据库系统。安装MySQL服务器之后,它的... [阅读全文] -
根据浏览器的保护规则,跨域的时候我们创建的sessionId是不会被浏览器保存下来的,这样,当我们在进行跨域访问的时候,我们的sessionId就不... [阅读全文]
-
oracle的sys和system默认密码system默认:manager sys默认:change_on_install使用PL/SQL Plus... [阅读全文]
-
1、移除现有Ruby默认源 $ gem sources --remove https://rubygems.org/ 2、使用新的源 $ gem s... [阅读全文]
-
版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。
发表评论