65.9K
CodeProject 正在变化。 阅读更多。
Home

让我们创建屏幕 - Android UI 布局和控件

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.82/5 (26投票s)

2014 年 8 月 24 日

CPOL

37分钟阅读

viewsIcon

124650

downloadIcon

5190

学习为您的 Android 应用程序创建 UI 部分

目录

 

引言

在本文中,您将了解 UI 组件的概述。Android 提供了基本的布局和控件,可用于创建应用的 UI。但是,如果您对它们不满意,或者想要更多组件,您可以自己制作。

背景

Android Application UI 是一个您必须学习的有趣事物。它与其他平台略有不同,但设计精美。要学习此课程,您需要了解 Android 应用的基础知识,例如项目结构和资源。您可以从 Hello Android 文章中学习。

交易

在学习本文之前,我想确保大家都在同一个起跑线上。我已经为您提供了基础项目来管理我们的课程材料,您可以从下面的链接下载。

要打开基础项目,您需要解压缩它,然后打开 Android Studio,接着点击文件菜单并选择打开。

选择项目文件夹并点击确定按钮。

如果您已经打开了另一个项目,Android Studio 会询问您要打开项目的窗口。

现在,基础项目已打开。

构建并运行它。

您会看到此应用的主屏幕只有一个列表。列表中有两个示例项,您可以点击该项以查看示例 Activity。在我们的课程中,您将学习创建更多 Activity 并将其添加到此列表中。

Activity 模板

在本文中,我们将使用“空白 Activity”模板来创建新的 Activity

空白 Activity 模板将生成 Java 代码、Layout 文件,并自动将 Activity 应用到 Manifest

为了方便访问 Activity,当您创建 Activity 时,需要将其添加到 MainActivity 的列表中。

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);//set the activity_main.xml as screen content

        List<ActivityData> activityDataList = new ArrayList<ActivityData>();//List of the ActivityData instance, put you ActivityData here
        activityDataList.add(new ActivityData(getString(R.string.title_activity_sample_inflate_view_from_xml),SampleInflateViewFromXmlActivity.class));//add the ActivityData to the List
        activityDataList.add(new ActivityData(getString(R.string.title_activity_sample_create_view_in_java_code),SampleCreateViewInJavaCodeActivity.class));

        ...
    }

我们使用 activityDataList.add() 语句将 Activity 添加到 ListView。您需要创建一个 ActivityData 实例,该实例包含 Activity 的标题及其 class

这是 ActivityData 类的构造函数。

ActivityData(String title,Class activityClass);

现在,尝试创建一个名为“MyFirstActivity”的新 Activity 并将其添加到列表中。

这是我的 MainActivity.java 代码。

....

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);//set the activity_main.xml as screen content

        List<ActivityData> activityDataList = new ArrayList<ActivityData>();//List of the ActivityData instance, put you ActivityData here
        activityDataList.add(new ActivityData(getString(R.string.title_activity_sample_inflate_view_from_xml),SampleInflateViewFromXmlActivity.class));//add the ActivityData to the List
        activityDataList.add(new ActivityData(getString(R.string.title_activity_sample_create_view_in_java_code),SampleCreateViewInJavaCodeActivity.class));
        activityDataList.add(new ActivityData("My Fist Activity",MyFirstActivity.class));

        ....
    }

    ....

}

运行应用程序时,您将在列表中看到一个 MyFirstActivity 项。

基础

Android Application UI 系统由 ViewViewGroup 组成。View 是显示在屏幕上并且用户可以与之交互的元素,而 ViewGroup 用于分组和排列 View

上图显示 Android Application UI 是一个 View 树。根节点只能放置一个元素。因此,ViewGroup 是您应该首先考虑的选择,因为它允许您在屏幕上放置多个元素。

尺寸

Android 建议开发者避免使用像素坐标进行定位和尺寸调整 View。因此,Android 提供了 ViewGroupLayout 来进行分组和排列,并使用特殊的单位来调整 View 的尺寸。

以下是用于调整 View 尺寸和对齐的特殊单位和值。

  • MATCH_PARENT: 将元素尺寸(宽度或高度)设置为匹配其父级。
  • FILL_PARENT:MATCH_PARENT 相同,但它支持 Android 2.2 及更早版本。
  • WRAP_CONTENT: 将元素尺寸(宽度或高度)设置为匹配其父级。
  • dp (dip) 单位: 这是一个抽象单位,根据屏幕的物理密度计算得出。可用于定义元素尺寸或其边距和内边距。
  • sp 单位: 类似于 dp 单位,但它还会根据用户的字体大小偏好进行计算,建议用于调整文本大小。

Android Activity 生命周期

当您打开应用,切换到其他应用,然后关闭它时,您的应用 Activity 将在称为“Activity 生命周期”的状态之间转换。

Android 为每个 Activity 的状态提供了回调方法。因此,您可以管理 Activity 在进入特定状态时如何工作。例如,在应用关闭时停止音乐,在用户切换到另一个应用时结束数据流。

上图显示了 Android Activity 生命周期、状态转换和回调方法。

Activity 收到 Intent 时,它会调用 onCreate() 方法,您可以在此状态下构建此 Activity 的 UI

创建后,将调用 onStart() 方法。在此状态下,您可以添加一些您想在 UI 可见之前运行的代码。

现在,UI 正在显示。Android 将调用 onResume() 方法,您可以添加每次应用恢复时希望运行的代码,例如刷新数据。

恢复后,应用将完全运行,您可以与应用的 UI 进行交互,直到它暂停。

当您切换到另一个应用或返回主屏幕时,Activity 将暂停,并调用 onPause()onSaveInstanceState() 方法。您需要保存未保存的数据,停止动画或 UI 工作,并减少资源使用。

注意:在此状态后,数据可能会丢失。您需要自己保存它。

Activity 不再可见时,UI 将隐藏,Android 将调用 onStop() 方法,您可以终止不再使用的服务。

如果您导航回 Activity,将调用 onRestart() 方法,允许您启动一个 Activity

当另一个 Activity 需要更多资源时,Android 会销毁未使用的 Activities。销毁后,用户仍然可以导航到该 Activity,Android 将调用 onCreate() 方法重新创建它。您可以恢复在暂停状态下保存的数据。

Activity 正在结束或 Android 要求销毁 Activity 时,Android 将调用 onDestroy() 方法,您需要释放线程、数据等资源。

亲眼看看

我已经创建了一个用于学习生命周期回调方法调用顺序的应用,您可以在此处下载。

只需在您的设备上构建并部署该应用,然后尝试切换到其他应用,返回,或执行一些使 Activity 转换为另一种状态的操作。您将通过 Toast 消息(浮动气球中的文本)看到在每个状态下调用的回调方法。

View

View 是显示在屏幕上的视觉元素,它可能与用户有一些交互。Android 提供了许多预定义的 View 供您在应用程序中使用。

您可以在 Java 代码和 XML 中创建 View。我建议您使用 XML,因为 MVC 概念提倡模型、视图和控制器应分开。

这是 XML 代码中 View 用法的示例。

<View
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    />

注意: layout_widthlayout_height 属性是创建 View 的必需属性。

如果您想在 Java 代码中调用此 View,则需要添加 id 属性以用于识别此 View

注意: id 属性的值必须以“@+id/”开头,后跟名称,或者您可以使用 Android 的预定义 id,例如“@android:id/text1”。

<View
    android:id="@+id/my_view"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    />

现在,您可以使用父级 ViewfindViewById() 方法,以 Viewid 作为参数,在 Java 代码中调用此 View

public class MainActivity extends ActionBarActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        View myView = findViewById(R.id.my_view);
    }
}

如果您想在 Java 代码中创建 View,可以通过实例化该 View 类的对象来完成。

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        View myView findViewById(R.id.my_view);
        View newView = new View(this);
    }
}

现在,View 已创建,但在将其添加到现有 layout 或将其设置为内容 View 之前,它不会显示在屏幕上。

在屏幕上显示 View

res/layout 文件夹中创建布局文件后,您需要在 Activity 中放置一些代码来调用您想显示的 ViewActivity 类提供了一个 setContentView() 方法来完成此工作,您可以将 layoutidView 实例作为参数传递。

示例 1

查看基础项目,在 res/layout 文件夹中有一个名为“activity_sample_inflate_view_from_xml.xml”的文件,我想在 SampleInflateViewFromXmlActivity 中显示它。

activity_sample_inflate_view_from_xml.xml

SampleInflateViewFromXmlActivity.java

我必须在 Activity 中放入一个 setContentView() 语句,并将 layoutid 作为参数。

package me.vable.android.viewandlayoutlessons;

import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;

public class SampleInflateViewFromXmlActivity extends ActionBarActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_sample_inflate_view_from_xml);
    }
}

注意: R 类用于在 Java 代码中访问 resourceid

现在,构建并运行应用程序。

示例 2

我有一个 SampleCreateViewInJavaCodeActivity,但我不再想使用 XML 布局文件,而是想在 Java 代码中创建 View

注意:要显示在 Java 代码中构建的 View,您也必须使用 setContentView() 方法,但将 View 实例作为参数而不是 layoutid

这是我的代码。

package me.vable.android.uiexample;

import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.widget.Button;

public class SampleActivity extends ActionBarActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Button helloWorldButton = new Button(this);
        helloWorldButton.setText("Hello World");
        setContentView(helloWorldButton);
    }
}

我创建了一个 Button 并将其设置为显示内容。这是结果。

现在,您可以将 View 显示在屏幕上了。

 

动手编码

在本课中,我将给您一个需要完成的项目。

该项目名为“UserSystem”,包含 5 个 Activities:Login ActivityWelcome ActivitySign up ActivityProfile ActivityUser List Activity。让我们看看界面模型。

登录 Activity

创建一个名为“LoginActivity”的新 Activity。然后请考虑它的 UI。

注意:不要忘记将此 Activity 添加到主页的列表中。

您会发现此 Activity 有 5 种类型的 View:图像、文本输入、复选框、文本链接、按钮。现在,将它们链接到 Android 的基本 View 类型。

  • 图像 - 要显示图像,您应该使用 ImageView
  • 文本输入 - Android 提供 EditText 来完成此工作,它允许您设置输入类型,并且您也可以将其用作密码字段。
  • 复选框 - Android 提供 CheckBox,并在一个 View 中带有标签。
  • 文本链接 - Android 没有文本链接,因为您可以为每个 View 添加点击事件。所以您可以使用 TextView 代替。
  • 按钮 - 您可以使用 Button 或其他 View,随意选择。

注意:当我描述每个 View 时,您可以创建一个新的 Activity 自己尝试,例如 TextViewExampleActivity,用于学习 TextView 的工作原理。

TextView

您可能知道 TextView 在其他平台上是 LabelText BlockTextView 是最简单的 View,用于在屏幕上显示文本。

要创建 TextView,只需从调色板中拖动 TextView 并将其拖放到预览屏幕上。

注意:Android Studio 提供了多种 TextView,有 Plain TextViewLarge TextMedium TextSmall Text。您可以选择一个喜欢的。

打开 XML 视图,您将看到像这样的 TextView 元素。

<TextView
    android:text="New Text"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    />

这些是 TextView 的有趣属性。

  • text - 您想在此 View 上显示的文本。
  • textSize - 文本大小。
  • textColor - 文本颜色。
  • textStyle - 文本样式(normal / italic / bold)。
  • fontFamily - 用于渲染文本的字体系列,例如 serif、sans-serif 等。

示例:我想创建一个 TextView 来显示一个红色大粗体 serif 文本“Hello World!”。

<TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World"
        android:textSize="36dp"
        android:textColor="#FF0000"
        android:fontFamily="serif"
        />
编辑 View 的属性

在 Android Studio 中设置 View 属性有多种方法。

1. 使用 XML 代码

layout 文件中的 View 元素,您可以添加已知的属性,如 id。如果您不知道属性名称,可以按 Ctrl + Space(Windows/Linux)键,然后将显示建议。

<TextView
        android:id="@+id/textview_hello_world"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World"
        android:textSize="36dp"
        android:textColor="#FF0000"
        android:fontFamily="serif"
        />

不喜欢 XML?您也可以使用 GUI 属性面板来编辑 View 的属性。

2. 使用属性面板

转到设计视图并单击要编辑的 View。查看 Android Studio 屏幕的右侧,您将看到属性面板,其中包含 View 属性列表。

如果找不到属性面板,您可能会看到“组件树”菜单。单击它,属性面板将会出现。

设置您想要的属性。

3. 双击 View

当您双击 View 时,将显示该 View 的常用属性,您可以在此处进行编辑。

注意:在 2. 和 3. 中,您会发现在某些属性值右侧有一个 [...] 按钮,该按钮用于浏览资源,如字符串、drawable 等。

在 Java 代码中访问 TextView

您可以从 Java 代码访问每个 View 实例,最简单的方法是使用该 View 的 id 来查找特定的 View。

EditText

EditText 也被称为 Text BoxText FieldText Area,它允许用户通过键盘输入/编辑文本。您可以指定输入数据类型,如数字、电子邮件、密码等。

您可以通过从调色板中拖动 EditText (Plain Text) 并将其拖放到预览屏幕上来将 EditText 添加到屏幕。

由于宽度设置为 WRAP_CONTENT,您可能会在预览屏幕上看到一个微小的 TextView,让我们尝试将其更改为 MATCH_PARENT

<EditText
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:id="@+id/editText"
    />

当您运行应用程序并点击 EditText 时,软键盘将出现。

这些是 EditText 的有趣属性。

  • text - EditText 的值。
  • password - 如果将其设置为 true,当您在此 EditText 中键入内容时,每个字符将显示为“•”。
  • textColor - 文本颜色。
  • hint - 当值为空时出现的文本,用于猜测用户应该输入什么。
  • textColorHint - 提示文本的颜色,应比 textColor 更浅。
  • singleLine - 默认情况下,EditText 允许用户输入多行文本,如文本区域,如果您不想要多行,请将此属性设置为 true。
  • maxLines - 您可以使用此属性限制文本行数。
  • inputType - 限制用户在此 EditText 中可以输入的字符类型。

如果您想限制 EditText 中的数据,可以设置输入类型。Android 提供了许多输入类型,如 numbertextUritextEmailAddress 等。输入类型将影响键盘样式,前提是您的软键盘支持该类型。

注意:输入类型仅过滤键盘输入。如果您在 Java 代码中设置文本,输入类型将不起作用。

示例:当我将输入类型设置为 number 时,软键盘样式将发生变化。

<EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/edittext_test"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:inputType="number" />

在 Java 代码中访问 EditText

现在,我们将访问 Java 中的 EditText。设置 EditText 的 id,然后您就可以使用 findViewById() 方法获取其实例。

....
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_edit_text_example);
        EditText testEditText = (EditText) findViewById(R.id.edittext_test);
    }
....

您可以使用 setText() 方法来设置 EditText 的值。

testEditText.setText("value from Java code");

以下是您应该了解的 EditText 方法。

  • getText() - 返回 EditText 的值。通常与 toString() 方法一起使用以获取 String 格式的值。
  • setText() - 设置 EditText 的值。
  • setOnKeyListener() - 设置用于检测某些按键事件(如 Enter 键)的 KeyListener

ImageView

当您想显示图像时,应该使用 ImageView。您可以通过从调色板中拖动 ImageView 并将其拖放到预览屏幕上来创建一个 ImageView

当您将 ImageView 拖放到屏幕上时,它将不显示任何内容,直到您设置其 src 属性。src 属性的值是 drawable resource,您需要将图像添加到 drawable-xxxx 文件夹。

每个 drawable 文件夹用于为特定设备提供图像。如果您在 drawable-xhdpi 中有图像,并且在屏幕较小或分辨率较低的设备上运行该应用,Android 将自动缩放图像以适应该设备的适当尺寸。我将在另一篇文章中介绍替代资源。

注意: Drawable resource 可以是图像文件和 XML 文件。

注意:如果可能,请为每个 dpi 创建图像,因为自动缩放可能会降低性能。

要将图像添加到项目,您可以直接将其放入 drawable-xxxx 文件夹中。我建议您在 XHDPIXXHDPI 下设计屏幕,并使用 Nexus 4 (XHDPI) 或 5 (XXHDPI) 作为预览屏幕设备。

您可以双击 ImageView 来设置 src 属性。单击 [...] 按钮可以浏览 drawable resources

 

这些是 ImageView 的有趣属性。

  • adjustViewBounds - 当您将此属性设置为 true 时,ImageView 将保持纵横比(与图像相同)。
  • scaleType - 如果图像内容不适合 ImageView,如何缩放图像内容。

您可以使用 setImageDrawable()setImageResource()setImageBitmap() 方法在 Java 代码中设置图像。

Button

Button 是用户可以按或点击以执行操作的 View

您可以通过 text 属性像 TextView 一样设置按钮上的文本,并且可以通过 drawableLeftdrawableRightdrawableTopdrawableBottom 属性向按钮添加图像。

点击事件

点击或触摸是您可以与 UI 交互的最重要的操作,您可以通过实现 OnClickListener 接口来检测所有 Android View 上的点击事件。

我有一个 id 为“button”的按钮。

<Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="New Button"
        />

然后,我实现了一个 OnClickListener 并将其设置到 Button 实例。

import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

public class ButtonExampleActivity extends ActionBarActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_button_example);
        Button button = (Button)findViewById(R.id.button);
        button.setOnClickListener(onClickListener);
    }

    View.OnClickListener onClickListener= new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Toast.makeText(ButtonExampleActivity.this,"Button was clicked",Toast.LENGTH_SHORT).show();
        }
    };
}

当我点击(触摸)此按钮时,将显示 Toast 消息。

您也可以通过在 XML 代码中使用 onClick 属性来检测点击事件,而无需实现 OnClickListener

注意:如果 View 不是 Button,您需要将 clickable 属性设置为 true。

注意:onClick 仅调用 Activity 的方法,请勿在 Fragment 中使用它。

<Button
        android:drawableLeft="@drawable/ic_launcher"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="drawableLeft"
        android:onClick="buttonClick"
        />

<TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Clickable TextView"
        android:clickable="true"
        android:onClick="buttonClick"
        />

您需要在 Activity 中创建 buttonClick(View) 方法。

public void buttonClick(View view)
{
        Toast.makeText(ButtonExampleActivity.this,"Invoke buttonClick() method",Toast.LENGTH_SHORT).show();
}

熟悉的 Views: ImageButton、所有实现 onClikListenerView

CheckBox

CheckBox 是一个双态按钮,可以是选中状态或未选中状态。在我们的项目中,我们将使用复选框作为“记住我”选项,用户可以选择该选项以保持登录状态。

<CheckBox
        android:id="@+id/checkbox_remember_me"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="remember me"
        />

您可以使用 checked 属性将其设置为默认选中。

<CheckBox
        android:id="@+id/checkbox_remember_me"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="remember me"
        android:checked="true"
        />

现在,尝试在 Java 代码中操作 CheckBox。您可以使用 isChecked() 方法获取选中状态。

CheckBox rememberMeCheckBox = (CheckBox) findViewById(R.id.checkbox_remember_me);

boolean rememberMe = rememberMeCheckBox.isChecked();

如果您想检测选中状态的更改,可以实现 CompoundButton.OnCheckedChangeListener 接口。

    CompoundButton.OnCheckedChangeListener onRememberMeCheckBoxCheckedChange = new CompoundButton.OnCheckedChangeListener() {
        @Override
        public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
            if(checked)
            {
                Toast.makeText(CheckBoxExampleActivity.this, "rememberMeCheckBox was checked", Toast.LENGTH_SHORT).show();
            }
            else
            {
                Toast.makeText(CheckBoxExampleActivity.this, "rememberMeCheckBox was unchecked", Toast.LENGTH_SHORT).show();
            }
        }
    };

并将其设置到 CheckBox

rememberMeCheckBox.setOnCheckedChangeListener(onRememberMeCheckBoxCheckedChange);

熟悉的 Views: SwitchToggleButton

 

创建 LoginActivity

现在,您已经学习了 5 种基本 View 类型。这足以创建我们项目的 Activity,请立即尝试创建它!(仅 UI 部分)

注意:请使用下图所示的名称。

结果漂亮吗?:(

现在,您将学习更多关于 View 的属性和 Layout,以构建更漂亮的 UI。

 

View 的通用属性

当所有 View extends 自同一个基类时,它们将具有通用属性/属性。我将仅描述重要的属性。

  • alpha - 使 View 透明、半透明或不透明。可能的值在 0.0 到 1.0 之间。
  • background - 设置 View 的背景。
  • clickable - 定义此 View 是否响应点击事件。
  • contentDescription - View 的描述。
  • gravity - 设置 View 的内容位置。可能的值为 lefttoprightbottomcentercenter_horizontalcenter_vertical 或它们的组合。
  • id - View 的 ID。
  • minHeight - View 的最小高度。
  • minWidth - View 的最小宽度。
  • onClick - 用于响应点击事件的方法名称。
  • padding - 设置所有四个边缘的内边距。
  • paddingBottom - 设置底部边缘的内边距。
  • paddingEnd - 设置结束边缘的内边距。
  • paddingLeft - 设置左边缘的内边距。
  • paddingRight - 设置右边缘的内边距。
  • paddingStart - 设置起始边缘的内边距。
  • paddingTop - 设置顶部边缘的内边距。
  • saveEnabled - 设置是否要在 UI 冻结时保存状态。
  • tag - 设置此 View 的标签。
  • visibility - 设置 View 的可见性。

View 放置在 Layout 下时,它们将接收一些属性,用于满足 Layout 的行为,例如 layout_marginlayout_gravity

当您将宽度/高度设置为 WRAP_CONTENT 时,Android 将把 View 的宽度/高度设置为尽可能小。但是,如果您向 View 添加 minWidth/minHeight,Android 将比较 width/heightminWidth/minHeight,然后选择较大的一个。

内边距 (padding) 是内容和 View 边框之间的间隙,外边距 (margin) 则相反,它是此 View 与其他 View 之间的间隙。

gravity 属性定义了内容在 View 中的位置,而 layout_gravity 则定义了 View 在父级 (Layout) 中的位置。

布局

什么是 LayoutLayout 是一种 ViewGroup,用于定义应用 UI 的结构,Android 提供了许多种类的 Layout,如 LinearLayoutFrameLayout 等。在本节中,您将学习每种 Layout 的行为以及如何使用它们。

线性布局

LinearLayout 是最简单的 Layout,它会在单列或单行中排列其子项。

它易于使用但功能强大。

使用 LinearLayout,您可以按权重分配 View 的宽度或高度。

示例:您有一个 ImageView 和 Content 在 LinearLayout 中,并且您想让图像占据 LinearLayout height 的 40%,其余空间用于内容。

这是当前的 XML 代码。

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <ImageView
        android:id="@+id/image_view_logo"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#FF99FF" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:background="#999999">
    </LinearLayout>
</LinearLayout>

这是您想要的。

您需要将 weightSum 属性设置为 LinearLayout

我将 LinearLayoutweightSum 设置为 10。

注意: weightSum 值可以是整数或小数。

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:weightSum="10">
    ....

然后,10 的 40% 是 4,您需要将 ImageViewweight 设置为 4。

    <ImageView
        android:id="@+id/image_view_logo"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#FF99FF"
        android:layout_weight="4"/>

剩余的 60% 高度,您需要将 Content(内部 LinearLayout)的 weight 设置为 6。

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:background="#999999"
        android:layout_weight="6">
    </LinearLayout>

现在,您得到了想要的用户界面。

注意:为了获得更好的性能,当您使用权重行为时,如果您使用垂直 LinearLayout,请将 layout 子项的高度设置为 0dp;如果您使用水平 LinearLayout,请将 layout 子项的宽度设置为 0dp。

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:weightSum="10">

    <ImageView
        android:id="@+id/image_view_logo"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:background="#FF99FF"
        android:layout_weight="4"/>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:orientation="vertical"
        android:background="#999999"
        android:layout_weight="6">
    </LinearLayout>

</LinearLayout>

相对布局

RelativeLayout 是一个非常灵活的布局,它会相对地排列其子项。每个子项都可以通过相对于兄弟元素的位置来指定位置,例如在另一个 View 的上方,在另一个 View 的左侧。RelativeLayout 允许您创建 View 重叠或浮动 View,并且其性能优于嵌套的 LinearLayout

您可以在许多应用程序中看到 RelativeLayout,例如 Facebook 和 Google+。

这些是 RelativeLayout 的子 View 的有趣属性。

  • layout_alignParentTop - 将 View 放置在 RelativeLayout 的顶部。
  • layout_alignParentBottom - 将 View 放置在 RelativeLayout 的底部。
  • layout_alignParentLeft - 将 View 放置在 RelativeLayout 的左侧。
  • layout_alignParentRight - 将 View 放置在 RelativeLayout 的右侧。
  • layout_centerInParent   - 将 View 放置在 RelativeLayout 的中心。
  • layout_centerHorizontal - 将 View 水平居中放置在 RelativeLayout 中。
  • layout_centerVertical - 将 View 垂直居中放置在 RelativeLayout 中。
  • layout_toLeftOf - 将 View 放置在另一个 View 的左侧。
  • layout_toRightOf - 将 View 放置在另一个 View 的右侧。
  • layout_above - 将 View 放置在另一个 View 的上方。
  • layout_below - 将 View 放置在另一个 View 的下方。

看起来很难?您可以在设计视图中将 Views 拖放到 RelativeLayout 上进行定位。

注意:您需要为每个 RelativeLayout 的子 View 设置 id 属性,以便在相对位置中引用。

示例:您想创建一个论坛应用程序,在帖子列表页面,您想创建如下的列表项:

您决定使用 Relative 来构建帖子项 UI,现在就开始尝试吧!!

下图显示了每个 Views 之间的关系。

这是我的代码。

<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="70dp"
    android:background="#252525"
    android:padding="8dp"
    android:layout_margin="8dp">

    <ImageView
        android:id="@+id/imageview_icon"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:src="@drawable/ic_launcher" />

    <TextView
        android:id="@+id/textview_title"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_above="@+id/textview_content"
        android:layout_alignParentRight="true"
        android:layout_alignParentTop="true"
        android:layout_toRightOf="@+id/imageview_icon"

        android:text="Title"
        android:textColor="@android:color/white"
        android:textSize="24sp" />

    <TextView
        android:id="@+id/textview_content"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_alignParentRight="true"
        android:layout_toRightOf="@+id/imageview_icon"
        android:text="Content"
        android:textColor="@android:color/secondary_text_dark"
        android:textSize="18sp" />
</RelativeLayout>

这是结果!!

帧布局

FrameLayout 设计用于只显示一个子项,并将其定位在布局的边缘或中心。但是,您可以向 FrameLayout 添加多个子项,但 View 可能会重叠,而您无法控制它。

FrameLayout 中的 Views 可以重叠时,您可以创建覆盖元素,如 Google+ 的写按钮。

注意:要定位 FrameLayout 中的 View,您可以使用子 View 上的 layout_gravitylayout_margin 属性。

FrameLayout 示例代码。

<FrameLayout 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"
    tools:context="me.vable.android.viewandlayoutlessons.FrameLayoutExampleActivity">

    <TextView
        android:text="top|left"
        android:textSize="20sp"
        android:gravity="center"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:background="#FF99FF"
        android:layout_gravity="top"
        />

    <TextView
        android:text="right|bottom"
        android:textSize="20sp"
        android:gravity="center"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:background="#FF99FF"
        android:layout_gravity="right|bottom" />

    <TextView
        android:text="bottom|center"
        android:textSize="20sp"
        android:gravity="center"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:background="#FF99FF"
        android:layout_gravity="center_horizontal|bottom" />

    <TextView
        android:text="left|bottom"
        android:textSize="20sp"
        android:gravity="center"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:background="#FF99FF"
        android:layout_gravity="left|bottom" />

    <TextView
        android:text="right|center"
        android:textSize="20sp"
        android:gravity="center"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:background="#FF99FF"
        android:layout_gravity="right|center_vertical" />

    <TextView
        android:text="center"
        android:textSize="20sp"
        android:gravity="center"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:background="#FF99FF"
        android:layout_gravity="center" />

    <TextView
        android:text="left|center"
        android:textSize="20sp"
        android:gravity="center"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:background="#FF99FF"
        android:layout_gravity="left|center_vertical" />

    <TextView
        android:text="top|right"
        android:textSize="20sp"
        android:gravity="center"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:background="#FF99FF"
        android:layout_gravity="right|top" />

    <TextView
        android:text="top|center"
        android:textSize="20sp"
        android:gravity="center"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:background="#FF99FF"
        android:layout_gravity="center_horizontal|top" />
    <ImageView
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:id="@+id/imageView"
        android:layout_gravity="right|bottom"
        android:src="@drawable/ic_error"
        android:layout_margin="80dp"/>

</FrameLayout>

 

创建 LoginActivity (续)

现在,我认为您可以使用学到的所有知识为 LoginActivity 创建一个更好的 UI,开始吧!!

我将在下面展示我的 layout(自己尝试,不要复制)。

<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"
    tools:context="me.vable.android.viewandlayoutlessons.LoginActivity"
    >

    <ImageView
        android:layout_width="match_parent"
        android:layout_height="200dp"
        android:id="@+id/imageview_app_logo"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"
        android:src="@drawable/ic_launcher"
        android:adjustViewBounds="true"
        android:padding="32dp"
        android:background="#2DABFF"
        android:layout_marginBottom="16dp"
        />

    <EditText
        android:id="@+id/edittext_username"
        android:layout_centerHorizontal="true"
        android:hint="Username"
        android:layout_width="250dp"
        android:layout_height="wrap_content"
        android:layout_below="@+id/imageview_app_logo"
        android:singleLine="true"
        android:maxLines="1"/>

    <EditText
        android:id="@+id/edittext_password"
        android:layout_below="@+id/edittext_username"
        android:layout_alignRight="@+id/edittext_username"
        android:layout_alignLeft="@+id/edittext_username"
        android:hint="Password"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:inputType="textPassword"
        android:ems="10"
        android:singleLine="true"
        android:maxLines="1"/>

    <CheckBox
        android:id="@+id/checkbox_remember_me"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="remember me"
        android:layout_below="@+id/edittext_password"
        android:layout_alignLeft="@+id/edittext_password"
        android:layout_marginLeft="8dp"
        android:layout_marginTop="16dp"/>

    <TextView
        android:id="@+id/textview_forgot_password"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceSmall"
        android:text="forgot password?"
        android:layout_below="@+id/checkbox_remember_me"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="16dp"
        android:textColor="#FF4040"/>

    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:weightSum="2">

        <Button
            android:id="@+id/button_sign_up"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Sign up"
            android:layout_weight="1" />

        <Button
            android:id="@+id/button_login"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Login"
            android:layout_weight="1" />

    </LinearLayout>
</RelativeLayout>

 

欢迎 Activity

用户登录后,将看到欢迎页面,该页面只显示个人资料图片和问候语。只有一个您尚未学习的选项菜单,我们将在本节中学习它。

选项菜单

Android 提供了标准的 menu 组件。为了保持一致性,您应该使用标准的 menu 而不是自己构建。在旧版 Android 中,有一个菜单硬件按钮用于打开 options menu,但在 Android 3.0 及以上版本中,options menuAction Bar 的一部分。Google 提供了 AppCompat 库,用于为旧版 Android 创建 Action Bar,以将相同的体验带给所有设备。

在 Android 3.0+ 上,options menu 分为 2 种类型:Action Menu(显示为 Action Bar 上的图标)和 Popup Menu(子菜单,直到您点击 菜单才会展开)。

要创建 options menu,您需要在 res/menu 中创建一个 menu resource(当您使用 Blank Activity 模板时,menu resource 会自动生成)。

现在,我创建了 OptionsMenuExampleActivity,其 menu resource 名称为 options_menu_example.xml

这是 XML 代码。

<menu 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"
    tools:context="me.vable.android.viewandlayoutlessons.OptionsMenuExampleActivity" >
    <item android:id="@+id/action_settings"
        android:title="@string/action_settings"
        android:orderInCategory="100"
        app:showAsAction="never" />
</menu>

空白 Activity 模板将默认生成具有 menu 设置的 menu resource

这些是选项 menu 的重要属性。

  • android:id - 此菜单的 ID,没有 ID 您将无法在 Java 代码中访问菜单。
  • android:title - 您想在此菜单上显示的文本。
  • android:orderInCategory - 此菜单在其 category 中的顺序。
  • android:icon - 菜单的图标,当菜单是 Action Menu 时显示,并且在旧式 menu(旧版 Android)中始终显示。
  • app:showAsAction / android:showAsAction - 这是 Action Menu 吗?您可以设置它始终显示为 Action Menu,或者根据 Action Bar 的空间显示,或者从不显示为 Action Menu。

注意:如果您的 Activity 扩展了 AppCompat 库中的 ActionBarActivity,您需要导入 app 命名空间 xmlns:app="http://schemas.android.com/apk/res-auto" 并使用 app 命名空间中的 showAsAction 属性。或者,您可以同时添加 app:showAsActionandroid:showAsAction 以确保 Action Menu 会显示。

我建议您为 Action Menu 的 id 命名为 action_<name>,或者为普通 Menu 命名为 menu_<name>

我将创建一个关于菜单作为 popup menu

<item android:id="@+id/menu_about"
        android:title="About"
        android:orderInCategory="101"
        app:showAsAction="never"
        />

我想在设置菜单下方显示关于菜单,所以我将其顺序设置为 101(值越大,位置越靠后)。

接下来,我将创建一个刷新菜单作为 Action Menu

<item android:id="@+id/action_refresh"
        android:title="Refresh"
        android:orderInCategory="1"
        app:showAsAction="always"
        />

这是结果。

创建 Action Menu 时,您应该添加菜单 icon。Android 建议使用单色图像作为菜单图标,您必须创建简单、含义清晰且广为人知的图像。

我决定使用此图标 作为刷新菜单。Android Studio 提供了图像资源导入向导,可帮助开发者将图像转换为适当的尺寸和颜色。要使用向导,请右键单击 res 文件夹,然后选择新建 > 图像资源。

您将看到向导窗口。

您可以使用此向导导入 3 种可用的 drawable 资源类型:Launcher Icons、Action Bar and Tabs Icons 和 Notification Icon。

我们将使用 Action Bar and Tab Icons 选项导入菜单图标。

这些是选项的描述。

  • Asset Type - 您想创建的 asset 的类型。
  • Foreground - 您可以使用自己的图片,或使用剪贴画,或文本。
    • Image File - 如果您使用自己的图像,向导将允许您选择图像。
    • Clipart - 如果您将前景设置为剪贴画,向导将允许您选择剪贴画。
    • Text - 如果您将前景设置为文本,您可以在此处输入您的文本。
    • Font - 设置文本的字体。
  • Trim surrounding blank space - 修剪前景元素周围的空白。
  • Additional padding - 在前景元素周围添加空白。
  • Theme - 您应用的 theme。如果您选择自定义选项,它将允许您选择图标的颜色。
  • Resource name - 设置此资源的名称。

注意:资源名称应命名为 ic_action_<name>。

选择选项后,点击 next 按钮,您将看到输出预览。

我看不见我的图片,因为它是白色的 :( 选择目标模块为您的应用,并选择您应用的 res 目录。

现在,我有一个名为 ic_action_refresh 的 drawable resource。将其添加到 menu!!

<item android:id="@+id/action_refresh"
        android:title="Refresh"
        android:orderInCategory="1"
        app:showAsAction="always"
        android:icon="@drawable/ic_action_refresh"
        />

这是结果。

接下来,我们将访问 Java 代码中的 options menu 并编写代码来响应 options menu 点击事件。

打开 Activity 文件,您将看到 onCreateOptionsMenu()onOptionsItemSelected() 方法(如果不存在,请创建它们)。

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.options_menu_example, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();
        if (id == R.id.action_settings) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

onCreateOptionsMenu() 方法用于将 options menu 膨胀到 View 上显示。

onOptionsItemSelected() 方法用于检测 menu 上的点击事件。

现在,我们专注于 onOptionsItemSelected() 方法,并将为每个菜单显示 Toast 消息。

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        if (id == R.id.action_settings) {
            //do something
            Toast.makeText(this,"Settings menu was clicked",Toast.LENGTH_SHORT).show();
            return true;
        }
        else if (id == R.id.menu_about) {
            //do something
            Toast.makeText(this,"About menu was clicked",Toast.LENGTH_SHORT).show();
            return true;
        }
        else if (id == R.id.action_refresh) {
            //do something
            Toast.makeText(this,"Refresh menu was clicked",Toast.LENGTH_SHORT).show();
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

然后运行应用!!

 

 

创建 WelcomeActivity

现在,您已经掌握了创建我们项目 WelcomeActivity 所需的所有知识。立即创建它,并将 ViewsidMenuid 设置为下图所示。

这是我的代码。

welcome.xml (menu)

<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:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin"
    tools:context="me.vable.android.viewandlayoutlessons.WelcomeActivity">

    <ImageView
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:adjustViewBounds="true"
        android:id="@+id/imageview_profile"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="32dp"
        android:src="@drawable/ic_man" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceMedium"
        android:text="Hello, John"
        android:id="@+id/textview_greeting"
        android:layout_below="@+id/imageview_profile"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="16dp" />
</RelativeLayout>

activity_welcome.xml (layout)

<menu 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"
    tools:context="me.vable.android.viewandlayoutlessons.WelcomeActivity" >
    <item android:id="@+id/menu_user_list"
        android:title="User List"
        android:orderInCategory="1"
        app:showAsAction="never" />
    <item android:id="@+id/menu_profile"
        android:title="My Profile"
        android:orderInCategory="2"
        app:showAsAction="never" />
    <item android:id="@+id/menu_logout"
        android:title="Log Out"
        android:orderInCategory="3"
        app:showAsAction="never" />
</menu>

 

注册 Activity

当用户打开应用时,他们会找到 LoginActivity。如果他们没有账户,他们需要注册一个新账户。有一个 RadioButtonRadioGroupSwitch,我们还没有学过。现在,我们将学习创建 SignUpActivity

RadioButton / RadioGroup

RadioButtonRadioGroup 一起用于创建一组项目,但您一次只能选择一个项目。

RadioGroup 是用于控制 RadioButtonViewGroupRadioGroup 允许用户只选择一个项目。

注意: RadioGroup 中有 orientation 属性,与 LinearLayout 类似。

RadioButton 的大部分属性都与 CheckBox 的属性相似。我认为您可以自己尝试。

示例:我想创建一个单选题,如图所示。这是我的代码。

<LinearLayout 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:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin"
    tools:context="me.vable.android.viewandlayoutlessons.RadioButtonExampleActivity"
    android:orientation="vertical">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceMedium"
        android:text="what are you feeling right now?"
        android:id="@+id/textView" />

    <RadioGroup
        android:id="@+id/radiogroup_feeling"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content">

        <RadioButton
            android:id="@+id/radiobutton_felling_happy"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Happy"/>

        <RadioButton
            android:id="@+id/radiobutton_felling_sad"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Sad"/>

        <RadioButton
            android:id="@+id/radiobutton_felling_bored"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Bored" />
    </RadioGroup>

</LinearLayout>

现在,我想检测哪个按钮被选中。实现 RadioGroupOnCheckedChangeListener 来检测何时发生选择更改。您将看到 onCheckedChanged() 方法,它会在选定的项目更改时被调用,它提供 RadioGroupRadioButton 的 id 作为参数。

package me.vable.android.viewandlayoutlessons;

import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.RadioGroup;
import android.widget.Toast;

public class RadioButtonExampleActivity extends ActionBarActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_radio_button_example);
        RadioGroup feelingRadioGroup = (RadioGroup) findViewById(R.id.radiogroup_feeling);
        feelingRadioGroup.setOnCheckedChangeListener(onCheckedChangeListener);

    }

    RadioGroup.OnCheckedChangeListener onCheckedChangeListener= new RadioGroup.OnCheckedChangeListener() {
        @Override
        public void onCheckedChanged(RadioGroup radioGroup, int i) {
            if(i == R.id.radiobutton_felling_happy)
            {
                Toast.makeText(RadioButtonExampleActivity.this,"You're happy!!",Toast.LENGTH_SHORT).show();
            }
            else if(i == R.id.radiobutton_felling_sad)
            {
                Toast.makeText(RadioButtonExampleActivity.this,"You're sad",Toast.LENGTH_SHORT).show();
            }
            else if(i == R.id.radiobutton_felling_bored)
            {
                Toast.makeText(RadioButtonExampleActivity.this,"You're bored",Toast.LENGTH_SHORT).show();
            }
        }
    };
}

 

Switch

Switch 的工作方式类似于 CheckBox,但它看起来像现实世界中的开关。您可以设置开/关标签。Switch 需要 Android 4.0 或更高版本。如果您的设备运行的是 4.0 以下的 Android 版本,请使用复选框。

这是一个例子。

    <Switch
        android:id="@+id/switch_subscription"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Email subscriptions"
        android:textOn=""
        />

您还可以使用 textOntextOff 属性来设置开/关文本。

     <Switch
        android:id="@+id/switch_subscription"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Email subscriptions"
        android:textOn="Yes"
        android:textOff="No"
        />

熟悉的 Views: CheckBoxToggleButton

 

创建 SignUpActivity

现在,让我们创建 SignUpActivity!!

注意:请使用我提供在下图中的 id。

这是我的代码。

activity_sign_up.xml

<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:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin"
    tools:context="me.vable.android.viewandlayoutlessons.SignupActivity">

    <ImageView
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:adjustViewBounds="true"
        android:id="@+id/imageview_profile"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="32dp"
        android:src="@drawable/ic_man" />

    <EditText
        android:id="@+id/edittext_username"
        android:layout_width="250dp"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceMedium"
        android:hint="Username"
        android:layout_below="@+id/imageview_profile"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="16dp" />

    <EditText
        android:id="@+id/edittext_password"
        android:layout_width="250dp"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceMedium"
        android:hint="Password"
        android:password="true"
        android:layout_below="@+id/edittext_username"
        android:layout_centerHorizontal="true"
        />

    <EditText
        android:id="@+id/edittext_email"
        android:inputType="textEmailAddress"
        android:layout_width="250dp"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceMedium"
        android:hint="Username"
        android:layout_below="@+id/edittext_password"
        android:layout_centerHorizontal="true"
        />
    <RadioGroup
        android:id="@+id/radiogroup_gender"
        android:layout_width="250dp"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:layout_below="@+id/edittext_email"
        android:layout_centerHorizontal="true"
        >
        <RadioButton
            android:id="@+id/radiobutton_male"
            android:text="male"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
        <RadioButton
            android:text="female"
            android:id="@+id/radiobutton_female"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
    </RadioGroup>

    <Switch
        android:id="@+id/switch_subscription"
        android:layout_width="300dp"
        android:layout_height="wrap_content"
        android:text="Email subscriptions"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="16dp"
        android:layout_below="@+id/radiogroup_gender"
        />

    <Switch
        android:id="@+id/switch_allow_email"
        android:layout_width="300dp"
        android:layout_height="wrap_content"
        android:text="Allow email from other"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="16dp"
        android:layout_below="@+id/switch_subscription"
        />

</RelativeLayout>

sign_up.xml (Options menu)

<menu 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"
    tools:context="me.vable.android.viewandlayoutlessons.SignUpActivity">
    <item
        android:id="@+id/action_submit"
        android:icon="@drawable/ic_action_check"
        android:title="Submit"
        app:showAsAction="always"/>
</menu>

然后,在设备上运行应用!!

 

个人资料 Activity

ProfileActivity 将显示用户的信息,但不包括密码。请自己创建!!

完成后,我们将在创建下一个 Activity 之前为每个 Activity 添加逻辑。

添加逻辑

在创建最后一个 Activity 之前,我们将为所有 Activity 添加逻辑代码以使我们的应用程序正常工作。我已经在基础项目中提供了逻辑代码,您只需要将其实现到您的 Activities 中。

User 类

User 是用于存储用户信息类的。

这些是 User 类的属性。

  •     private String username;
  •     private String password;
  •     private String email;
  •     private String profileImage;
  •     private Gender gender;
  •     private boolean newsletterSubscribed;
  •     private boolean allowedOtherEmail;

您可以通过访问器方法(get/set)来设置或获取它。

这是 Gender enum 的结构。

public enum Gender implements Serializable {
    MALE,FEMALE
}

UserService 类

UserService 用于管理 User 账户,它具有创建新用户、登录、注销等功能。您将使用此类与屏幕进行交互。

这些是此类的方法。

  • getInstance() - 获取此类的当前实例。
  • getCurrentUser() - 获取当前登录的 User
  • login() - 使用用户名和密码进行用户身份验证,您需要传递 LoginListener 以便接收结果。
  • logout() - 从系统中注销。
  • register() - 创建一个新的用户账户,您需要传递 RegisterListener 以便接收结果。
  • getProfileImage() - 返回用户个人资料图片的 Bitmap 对象。
  • getUserList() - 获取用户账户列表,您需要传递 GetUserListListener 以便接收结果。

此外,还有一些您在使用某些方法时需要实现的接口。

    public interface LoginListener
    {
        public void onResponce(boolean loggedin,String message, User user);
    }

    public interface RegisterListener
    {
        public void onResponce(boolean registered, String message, User user);
    }

    public interface GetUserListListener
    {
        public void onResponce(boolean success, String message, List<User> users);
    }

每个 interface 都将在第一个参数中返回结果,第二个参数为消息,第三个参数为 User 对象或 User List 对象。

LoginActivity

现在,开始着手 LoginActivity

首先,您需要获取所有 View 的实例。

    private EditText usernameEditText;
    private EditText passwordEduitText;
    private CheckBox rememberMeCheckBox;
    private TextView forgotPasswordTextView;
    private Button loginButton;
    private Button signUpButton;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);

        usernameEditText = (EditText) findViewById(R.id.edittext_username);
        passwordEduitText = (EditText) findViewById(R.id.edittext_password);
        rememberMeCheckBox = (CheckBox) findViewById(R.id.checkbox_remember_me);
        forgotPasswordTextView =  (TextView) findViewById(R.id.textview_forgot_password);
        loginButton =  (Button) findViewById(R.id.button_login);
        signUpButton =  (Button) findViewById(R.id.button_sign_up);
    }

接下来,我们将创建 login() 方法。

    private void login(String username,String password)
    {
        progressDialog.show();
        UserService.getInstance(LoginActivity.this).login(username, password, loginListener);
    }

login() 方法将调用 UserService 类的 login 方法。

注意:您需要使用 UserService.getInstance(Context) 来获取 UserService 类的实例,不要自己创建。

创建 ProgressDialog 以在执行长时间操作方法时显示。

private ProgressDialog progressDialog;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...
        progressDialog = new ProgressDialog(LoginActivity.this);
        progressDialog.setIndeterminate(true);
        ...
    }

然后我们需要实现 LoginListener 以接收结果回调。

    UserService.LoginListener loginListener = new UserService.LoginListener() {
        @Override
        public void onResponce(boolean loggedin, String message, User user) {
            progressDialog.dismiss();
            Toast.makeText(LoginActivity.this,message,Toast.LENGTH_SHORT).show();
            if(loggedin)
            {
                SharedPreferences sharedPreferences = getSharedPreferences("user_data",MODE_PRIVATE);
                SharedPreferences.Editor editor = sharedPreferences.edit();
                if(rememberMeCheckBox.isChecked())
                {
                    editor.putBoolean("remembered", true);
                    editor.putString("username", user.getUsername());
                    editor.putString("password", user.getPassword());
                }
                else
                {
                    editor.putBoolean("remembered",false);
                    editor.remove("username");
                    editor.remove("password");
                }
                editor.commit();
                goToWelcomeActivity();
            }
        }
    };

如果登录成功,我们需要检查“记住我”按钮的状态。如果用户想要记住,我们需要将账户数据保存到 SharedPreferences

Android 提供了 SharedPreferences 来存储应用数据,如设置。您可以从 Context 类的 getSharedPreferences() 方法获取 SharedPreferences 实例。并且您可以使用 SharedPreferences.Editor 类来保存或管理数据。

注意:每次使用添加、删除或编辑 SharedPreferences 数据时,您都需要通过使用 Editor.commit() 方法来提交。

当用户登录后,您需要将用户带到 WelcomeActivity。所以我创建了 goToWelcomeActivity() 方法来完成这项工作。

    private void goToWelcomeActivity()
    {
        Intent intent = new Intent(this,WelcomeActivity.class);
        startActivity(intent);
        finish();
    }

Intent 对象就像您发送给 Android 的消息,当 Android 收到 Intent 时,它会找到负责处理该 Intent 的对象。当您需要切换到另一个 Activity 时,您需要创建一个 Intent,并传入 Context 和另一个 Activity 类,然后调用 startActivity() 方法。finish() 方法用于结束 Activity,在用户登录后,我们结束 Activity,因为我们不再需要它了。

接下来,我们将检测登录按钮的点击事件。

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...
        loginButton.setOnClickListener(onClickLoginButtonListener);
        ...
    }

    View.OnClickListener onClickLoginButtonListener = new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            String username = usernameEditText.getText().toString();
            String password = passwordEduitText.getText().toString();
            login(username, password);
        }
    };

我们需要获取 usernameEditTextpasswordEditText 的值,并将它们传递给 login() 方法。

之后,我们必须实现注册按钮的点击事件。

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...
        signUpButton.setOnClickListener(onClickSignUpButtonListener);
        ...
    }

    View.OnClickListener onClickSignUpButtonListener = new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            goToSignupActivity();
        }
    };

    private void goToSignupActivity()
    {
        Intent intent = new Intent(this,SignUpActivity.class);
        startActivity(intent);
    }

当用户点击注册按钮时,应用程序将导航到 SignUpActivity

几乎完成了,现在有一个“忘记密码”TextView,它将用于重置密码,但我们目前没有该功能。我们需要告诉用户此功能尚未实现。

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...
        forgotPasswordTextView.setOnClickListener(onClickForgotPasswordTextViewListener);
    }
    ...
    View.OnClickListener onClickForgotPasswordTextViewListener = new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            AlertDialog.Builder builder = new AlertDialog.Builder(LoginActivity.this);
            builder.setTitle("Waraing");
            builder.setIcon(R.drawable.ic_error);
            builder.setMessage("Not implement");
            builder.setPositiveButton("OK",null);
            builder.show();
        }
    };

我创建了一个 AlertDialog 来向用户显示此功能尚未实现。

接下来,我们需要在用户进入此 Activity 时检查用户的登录状态。如果他们已登录,应用程序应立即导航到 WelcomeActivity

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...
        if(UserService.getInstance(this).getCurrentUser()!=null)
        {
            goToWelcomeActivity();
            return;
        }
    }

要检查登录状态,您可以使用 UserService 的 getCurrentUser() 方法。如果当前用户不为 null,则需要切换到 WelcomeActivity

最后,如果我们存储了“记住我”功能的用户数据,我们需要立即登录该用户。

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...
        SharedPreferences sharedPreferences = getSharedPreferences("user_data",MODE_PRIVATE);
        boolean remembered = sharedPreferences.getBoolean("remembered",false);
        if(remembered)
        {
            rememberMeCheckBox.setChecked(true);
            String username = sharedPreferences.getString("username", null);
            String password = sharedPreferences.getString("password", null);
            login(username,password);
        }
    }

这是完整的源代码。

package me.vable.android.viewandlayoutlessons;

import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Intent;
import android.content.SharedPreferences;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import me.vable.android.viewandlayoutlessons.data.User;
import me.vable.android.viewandlayoutlessons.data.service.UserService;

public class LoginActivity extends ActionBarActivity {

    private EditText usernameEditText;
    private EditText passwordEduitText;
    private CheckBox rememberMeCheckBox;
    private TextView forgotPasswordTextView;
    private Button loginButton;
    private Button signUpButton;
    private ProgressDialog progressDialog;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);

        if(UserService.getInstance(this).getCurrentUser()!=null)
        {
            goToWelcomeActivity();
            return;
        }

        progressDialog = new ProgressDialog(LoginActivity.this);
        progressDialog.setIndeterminate(true);

        usernameEditText = (EditText) findViewById(R.id.edittext_username);
        passwordEduitText = (EditText) findViewById(R.id.edittext_password);
        rememberMeCheckBox = (CheckBox) findViewById(R.id.checkbox_remember_me);
        forgotPasswordTextView =  (TextView) findViewById(R.id.textview_forgot_password);
        loginButton =  (Button) findViewById(R.id.button_login);
        signUpButton =  (Button) findViewById(R.id.button_sign_up);

        loginButton.setOnClickListener(onClickLoginButtonListener);
        signUpButton.setOnClickListener(onClickSignUpButtonListener);
        forgotPasswordTextView.setOnClickListener(onClickForgotPasswordTextViewListener);

        SharedPreferences sharedPreferences = getSharedPreferences("user_data",MODE_PRIVATE);
        boolean remembered = sharedPreferences.getBoolean("remembered",false);
        if(remembered)
        {
            rememberMeCheckBox.setChecked(true);
            String username = sharedPreferences.getString("username", null);
            String password = sharedPreferences.getString("password", null);
            login(username,password);
        }
    }

    private void login(String username,String password)
    {
        progressDialog.show();
        UserService.getInstance(LoginActivity.this).login(username, password, loginListener);
    }

    private void goToSignupActivity()
    {
        Intent intent = new Intent(this,SignUpActivity.class);
        startActivity(intent);
    }

    private void goToWelcomeActivity()
    {
        Intent intent = new Intent(this,WelcomeActivity.class);
        startActivity(intent);
        finish();
    }

    View.OnClickListener onClickLoginButtonListener = new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            String username = usernameEditText.getText().toString();
            String password = passwordEduitText.getText().toString();

            login(username, password);
        }
    };

    View.OnClickListener onClickSignUpButtonListener = new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            goToSignupActivity();
        }
    };

    View.OnClickListener onClickForgotPasswordTextViewListener = new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            AlertDialog.Builder builder = new AlertDialog.Builder(LoginActivity.this);
            builder.setTitle("Waraing");
            builder.setIcon(R.drawable.ic_error);
            builder.setMessage("Not implement");
            builder.setPositiveButton("OK",null);
            builder.show();
        }
    };

    UserService.LoginListener loginListener = new UserService.LoginListener() {
        @Override
        public void onResponce(boolean loggedin, String message, User user) {
            progressDialog.dismiss();
            Toast.makeText(LoginActivity.this,message,Toast.LENGTH_SHORT).show();
            if(loggedin)
            {
                SharedPreferences sharedPreferences = getSharedPreferences("user_data",MODE_PRIVATE);
                SharedPreferences.Editor editor = sharedPreferences.edit();
                if(rememberMeCheckBox.isChecked())
                {
                    editor.putBoolean("remembered", true);
                    editor.putString("username", user.getUsername());
                    editor.putString("password", user.getPassword());
                }
                else
                {
                    editor.putBoolean("remembered",false);
                    editor.remove("username");
                    editor.remove("password");
                }
                editor.commit();
                goToWelcomeActivity();
            }
        }
    };
}

SignUpActivity

要为 SignupActivity 添加逻辑,您需要先获取所有 View 的实例。

    private ImageView profileImageView;
    private EditText usernameEditText;
    private EditText passwordEditText;
    private EditText emailEditText;
    private RadioGroup genderRadioGroup;
    private RadioButton maleRadioButton;
    private RadioButton femaleRadioButton;
    private CompoundButton newsletterSubscriptionCompoundButton;
    private CompoundButton allowOtherEmailCompoundButton;
    private ProgressDialog progressDialog;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_sign_up);

        progressDialog = new ProgressDialog(this);
        progressDialog.setIndeterminate(true);

        profileImageView = (ImageView) findViewById(R.id.imageview_profile);
        usernameEditText = (EditText) findViewById(R.id.edittext_username);
        passwordEditText = (EditText) findViewById(R.id.edittext_password);
        emailEditText = (EditText) findViewById(R.id.edittext_email);
        genderRadioGroup = (RadioGroup) findViewById(R.id.radiogroup_gender);
        maleRadioButton = (RadioButton) findViewById(R.id.radiobutton_male);
        femaleRadioButton = (RadioButton) findViewById(R.id.radiobutton_female);
        newsletterSubscriptionCompoundButton = (CompoundButton) findViewById(R.id.switch_subscription);
        if(newsletterSubscriptionCompoundButton == null)
        {
            newsletterSubscriptionCompoundButton = (CompoundButton) findViewById(R.id.checkbox_subscription);
        }
        allowOtherEmailCompoundButton = (CompoundButton) findViewById(R.id.switch_allow_email);
        if(allowOtherEmailCompoundButton == null) {
            allowOtherEmailCompoundButton = (CompoundButton) findViewById(R.id.checkbox_allow_email);
        }
    }

我使用 CompoundButton 而不是 SwitchCheckBox,因为我需要同时支持它们。

为了确保性别 RadioButton 中的一个被选中,默认将其中的一个设为选中。

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...
        maleRadioButton.setChecked(true);
    }

当用户点击个人资料 imageView 时,我想允许他们从设备中选择图片。

    private static final int SELECT_PICTURE = 1;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...
        profileImageView.setOnClickListener(onClickProfileImageViewListener);
    }

    View.OnClickListener onClickProfileImageViewListener = new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            chooseImage();
        }
    };

    private void chooseImage()
    {
        Intent intent = new Intent(Intent.ACTION_PICK,android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
        startActivityForResult(intent, SELECT_PICTURE);
    }

我使用 Intent 打开了提供图像的其他应用。

当收到图像后,我们将调整大小并显示在个人资料 ImageView 上。

    private Bitmap bitmap;
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == SELECT_PICTURE && resultCode == Activity.RESULT_OK)
            try {
                // We need to recyle unused bitmaps
                if (bitmap != null) {
                    bitmap.recycle();
                }
                InputStream stream = getContentResolver().openInputStream(data.getData());
                BitmapFactory.Options options = new BitmapFactory.Options();
                options.inSampleSize=2;
                bitmap = BitmapFactory.decodeStream(stream, null, options);
                int width = bitmap.getWidth();
                int height = bitmap.getHeight();
                double scale = 100.0/height;
                height = (int)(height*scale);
                width = (int)(width*scale);
                bitmap = Bitmap.createScaledBitmap(bitmap, width,height, false);
                stream.close();
                profileImageView.setImageBitmap(bitmap);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        super.onActivityResult(requestCode, resultCode, data);
    }

接下来,当用户点击提交 Action Menu 时,数据将发送到 UserService 以创建账户。

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.sign_up, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        if (id == R.id.action_submit) {
            String username = usernameEditText.getText().toString();
            String password = passwordEditText.getText().toString();
            String email = emailEditText.getText().toString();
            User.Gender gender;
            if(genderRadioGroup.getCheckedRadioButtonId() == R.id.radiobutton_male)
            {
                gender = User.Gender.MALE;
            }
            else
            {
                gender = User.Gender.FEMALE;
            }
            boolean newsletterSubscribed = newsletterSubscriptionCompoundButton.isChecked();
            boolean allowedOtherEmail = allowOtherEmailCompoundButton.isChecked();

            register(username,password,email,gender,newsletterSubscribed,allowedOtherEmail);

            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    private void register(String username,String password, String email, User.Gender gender, boolean newsletterSubscribed, boolean allowedOtherEmail)
    {
        progressDialog.show();
        UserService.getInstance(SignUpActivity.this).register(username,password,email,gender,newsletterSubscribed,allowedOtherEmail,registerListener,bitmap);
    }

实现 RegisterListener 以接收注册结果。

    UserService.RegisterListener registerListener = new UserService.RegisterListener() {
        @Override
        public void onResponce(boolean registered, String message, User user) {
            progressDialog.dismiss();
            Toast.makeText(SignUpActivity.this, message, Toast.LENGTH_SHORT).show();
            if(registered)
            {
                goToWelcomeActivity();
            }
        }
    };

    private void goToWelcomeActivity()
    {
        Intent intent = new Intent(this,WelcomeActivity.class);
        startActivity(intent);
        finish();
    }

在此之后,您应该在用户进入此 Activity 时检查用户的登录状态。

     protected void onCreate(Bundle savedInstanceState) {
        ....
        if(UserService.getInstance(this).getCurrentUser()!=null)
        {
            goToWelcomeActivity();
        }
        ....
    }

这是完整的源代码。

package me.vable.android.viewandlayoutlessons;

import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.Toast;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import me.vable.android.viewandlayoutlessons.data.User;
import me.vable.android.viewandlayoutlessons.data.service.UserService;

public class SignUpActivity extends ActionBarActivity {

    private ImageView profileImageView;
    private EditText usernameEditText;
    private EditText passwordEditText;
    private EditText emailEditText;
    private RadioGroup genderRadioGroup;
    private RadioButton maleRadioButton;
    private RadioButton femaleRadioButton;
    private CompoundButton newsletterSubscriptionCompoundButton;
    private CompoundButton allowOtherEmailCompoundButton;
    private ProgressDialog progressDialog;
    private Bitmap bitmap;

    private static final int SELECT_PICTURE = 1;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_sign_up);

        if(UserService.getInstance(this).getCurrentUser()!=null)
        {
            goToWelcomeActivity();
        }

        progressDialog = new ProgressDialog(this);
        progressDialog.setIndeterminate(true);

        profileImageView = (ImageView) findViewById(R.id.imageview_profile);
        usernameEditText = (EditText) findViewById(R.id.edittext_username);
        passwordEditText = (EditText) findViewById(R.id.edittext_password);
        emailEditText = (EditText) findViewById(R.id.edittext_email);
        genderRadioGroup = (RadioGroup) findViewById(R.id.radiogroup_gender);
        maleRadioButton = (RadioButton) findViewById(R.id.radiobutton_male);
        femaleRadioButton = (RadioButton) findViewById(R.id.radiobutton_female);
        newsletterSubscriptionCompoundButton = (CompoundButton) findViewById(R.id.switch_subscription);
        if(newsletterSubscriptionCompoundButton == null)
        {
            newsletterSubscriptionCompoundButton = (CompoundButton) findViewById(R.id.checkbox_subscription);
        }
        allowOtherEmailCompoundButton = (CompoundButton) findViewById(R.id.switch_allow_email);
        if(allowOtherEmailCompoundButton == null) {
            allowOtherEmailCompoundButton = (CompoundButton) findViewById(R.id.checkbox_allow_email);
        }
        maleRadioButton.setChecked(true);

        profileImageView.setOnClickListener(onClickProfileImageViewListener);

    }

    View.OnClickListener onClickProfileImageViewListener = new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            chooseImage();
        }
    };

    private void chooseImage()
    {
        Intent intent = new Intent(Intent.ACTION_PICK,android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
        startActivityForResult(intent, SELECT_PICTURE);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == SELECT_PICTURE && resultCode == Activity.RESULT_OK)
            try {
                // We need to recyle unused bitmaps
                if (bitmap != null) {
                    bitmap.recycle();
                }
                InputStream stream = getContentResolver().openInputStream(data.getData());
                BitmapFactory.Options options = new BitmapFactory.Options();
                options.inSampleSize=2;
                bitmap = BitmapFactory.decodeStream(stream, null, options);
                int width = bitmap.getWidth();
                int height = bitmap.getHeight();
                double scale = 100.0/height;
                height = (int)(height*scale);
                width = (int)(width*scale);
                bitmap = Bitmap.createScaledBitmap(bitmap, width,height, false);
                stream.close();
                profileImageView.setImageBitmap(bitmap);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        super.onActivityResult(requestCode, resultCode, data);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.sign_up, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        if (id == R.id.action_submit) {
            String username = usernameEditText.getText().toString();
            String password = passwordEditText.getText().toString();
            String email = emailEditText.getText().toString();
            User.Gender gender;
            if(genderRadioGroup.getCheckedRadioButtonId() == R.id.radiobutton_male)
            {
                gender = User.Gender.MALE;
            }
            else
            {
                gender = User.Gender.FEMALE;
            }
            boolean newsletterSubscribed = newsletterSubscriptionCompoundButton.isChecked();
            boolean allowedOtherEmail = allowOtherEmailCompoundButton.isChecked();

            register(username,password,email,gender,newsletterSubscribed,allowedOtherEmail);

            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    private void register(String username,String password, String email, User.Gender gender, boolean newsletterSubscribed, boolean allowedOtherEmail)
    {
        progressDialog.show();
        UserService.getInstance(SignUpActivity.this).register(username,password,email,gender,newsletterSubscribed,allowedOtherEmail,registerListener,bitmap);
    }

    UserService.RegisterListener registerListener = new UserService.RegisterListener() {
        @Override
        public void onResponce(boolean registered, String message, User user) {
            progressDialog.dismiss();
            Toast.makeText(SignUpActivity.this, message, Toast.LENGTH_SHORT).show();
            if(registered)
            {
                goToWelcomeActivity();
            }
        }
    };

    private void goToWelcomeActivity()
    {
        Intent intent = new Intent(this,WelcomeActivity.class);
        startActivity(intent);
        finish();
    }
}

欢迎 Activity

Activity 将只显示用户个人资料图片和问候语。

您需要获取 View 的实例并设置它们的值。

    private ImageView profileImageView;
    private TextView greetingTextView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_welcome);

        profileImageView = (ImageView) findViewById(R.id.imageview_profile);
        greetingTextView = (TextView) findViewById(R.id.textview_greeting);

        greetingTextView.setText(String.format("Hello, %s", user.getUsername()));
        profileImageView.setImageBitmap(UserService.getInstance(this).getProfileImage(user));
    }

检查 User 实例。如果不存在,您应立即关闭此 Activity。

        User user = UserService.getInstance(this).getCurrentUser();

        if(user==null) {
            finish();
            return;
        }

然后,通过使用 onOptionsItemSelected() 方法检测选项菜单项的点击事件,并为每个事件创建操作。

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.welcome, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        if (id == R.id.menu_user_list) {
            return true;
        }else if (id == R.id.menu_profile) {
            goToMenuProfilePage();
            return true;
        }else if (id == R.id.menu_logout) {
            logout();
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    private void goToMenuProfilePage()
    {
        Intent intent = new Intent(this,ProfileActivity.class);
        startActivity(intent);
    }

    private void logout()
    {
        UserService.getInstance(this).logout();
        SharedPreferences sharedPreferences = getSharedPreferences("user_data",MODE_PRIVATE);
        SharedPreferences.Editor editor = sharedPreferences.edit();
        editor.putBoolean("remembered",false);
        editor.remove("username");
        editor.remove("password");
        editor.commit();
        finish();
    }
}

goToMenuProfilePage() 中,我创建了 Intent 并将额外数据放入其中,因为我想将 User 对象传递给 ProfileActivity

这是完整的源代码。

package me.vable.android.viewandlayoutlessons;

import android.content.Intent;
import android.content.SharedPreferences;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.ImageView;
import android.widget.TextView;
import me.vable.android.viewandlayoutlessons.data.User;
import me.vable.android.viewandlayoutlessons.data.service.UserService;

public class WelcomeActivity extends ActionBarActivity {

    private ImageView profileImageView;
    private TextView greetingTextView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_welcome);

        profileImageView = (ImageView) findViewById(R.id.imageview_profile);
        greetingTextView = (TextView) findViewById(R.id.textview_greeting);

        User user = UserService.getInstance(this).getCurrentUser();

        if(user==null) {
            finish();
            return;
        }

        greetingTextView.setText(String.format("Hello, %s", user.getUsername()));
        profileImageView.setImageBitmap(UserService.getInstance(this).getProfileImage(user));
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.welcome, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        if (id == R.id.menu_user_list) {
            return true;
        }else if (id == R.id.menu_profile) {
            goToMenuProfilePage();
            return true;
        }else if (id == R.id.menu_logout) {
            logout();
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    private void goToMenuProfilePage()
    {
        Intent intent = new Intent(this,ProfileActivity.class);
        startActivity(intent);
    }

    private void logout()
    {
        UserService.getInstance(this).logout();
        SharedPreferences sharedPreferences = getSharedPreferences("user_data",MODE_PRIVATE);
        SharedPreferences.Editor editor = sharedPreferences.edit();
        editor.putBoolean("remembered",false);
        editor.remove("username");
        editor.remove("password");
        editor.commit();
        finish();
    }
}

个人资料 Activity

ProfileActivity 将从 intent 接收 User 对象并显示屏幕上的用户信息。首先,我们需要从 intent 接收 User 对象并获取所有 View 实例。

    private ImageView profileImageView;
    private TextView usernameTextView;
    private TextView emailTextVIew;
    private TextView genderTextView;
    private CompoundButton newsletterSubscriptionCompoundButton;
    private CompoundButton allowOtherEmailCompoundButton;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_profile);

        User user = (User) getIntent().getExtras().getSerializable("user");

        if(user==null) {
            finish();
            return;
        }

        profileImageView = (ImageView) findViewById(R.id.imageview_profile);
        usernameTextView = (TextView) findViewById(R.id.textview_username);
        emailTextVIew = (TextView) findViewById(R.id.textview_email);
        genderTextView = (TextView) findViewById(R.id.textview_gender);
        newsletterSubscriptionCompoundButton = (Switch) findViewById(R.id.switch_subscription);
        if(newsletterSubscriptionCompoundButton==null)
        {
            newsletterSubscriptionCompoundButton = (CheckBox) findViewById(R.id.checkbox_subscription);
        }
        allowOtherEmailCompoundButton = (Switch) findViewById(R.id.switch_allow_email);
        if(allowOtherEmailCompoundButton==null)
        {
            allowOtherEmailCompoundButton = (CheckBox) findViewById(R.id.checkbox_allow_email);
        }
    }

接下来,将值设置给 View

        profileImageView.setImageBitmap(UserService.getInstance(this).getProfileImage(user));
        usernameTextView.setText(String.format("Username: %s",user.getUsername()));
        emailTextVIew.setText(String.format("Email: %s",user.getEmail()));
        genderTextView.setText(String.format("Gender: %s",user.getGender()== User.Gender.MALE?"Male":"Female"));
        newsletterSubscriptionCompoundButton.setChecked(user.isNewsletterSubscribed());
        allowOtherEmailCompoundButton.setChecked(user.isAllowedOtherEmail());

最后,如果 allowOtherEmailCompoundButton 未选中,我们需要隐藏电子邮件。

        if(!allowOtherEmailCompoundButton.isChecked())
        {
            emailTextVIew.setVisibility(View.GONE);
        }

这是完整的源代码。

package me.vable.android.viewandlayoutlessons;

import android.app.Activity;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.ImageView;
import android.widget.Switch;
import android.widget.TextView;

import org.w3c.dom.Text;

import me.vable.android.viewandlayoutlessons.data.User;
import me.vable.android.viewandlayoutlessons.data.service.UserService;

public class ProfileActivity extends ActionBarActivity {

    private ImageView profileImageView;
    private TextView usernameTextView;
    private TextView emailTextVIew;
    private TextView genderTextView;
    private CompoundButton newsletterSubscriptionCompoundButton;
    private CompoundButton allowOtherEmailCompoundButton;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_profile);

        User user = (User) getIntent().getExtras().getSerializable("user");

        if(user==null) {
            finish();
            return;
        }

        profileImageView = (ImageView) findViewById(R.id.imageview_profile);
        usernameTextView = (TextView) findViewById(R.id.textview_username);
        emailTextVIew = (TextView) findViewById(R.id.textview_email);
        genderTextView = (TextView) findViewById(R.id.textview_gender);
        newsletterSubscriptionCompoundButton = (Switch) findViewById(R.id.switch_subscription);
        if(newsletterSubscriptionCompoundButton==null)
        {
            newsletterSubscriptionCompoundButton = (CheckBox) findViewById(R.id.checkbox_subscription);
        }
        allowOtherEmailCompoundButton = (Switch) findViewById(R.id.switch_allow_email);
        if(allowOtherEmailCompoundButton==null)
        {
            allowOtherEmailCompoundButton = (CheckBox) findViewById(R.id.checkbox_allow_email);
        }

        profileImageView.setImageBitmap(UserService.getInstance(this).getProfileImage(user));
        usernameTextView.setText(String.format("Username: %s",user.getUsername()));
        emailTextVIew.setText(String.format("Email: %s",user.getEmail()));
        genderTextView.setText(String.format("Gender: %s",user.getGender()== User.Gender.MALE?"Male":"Female"));
        newsletterSubscriptionCompoundButton.setChecked(user.isNewsletterSubscribed());
        allowOtherEmailCompoundButton.setChecked(user.isAllowedOtherEmail());

        if(!allowOtherEmailCompoundButton.isChecked())
        {
            emailTextVIew.setVisibility(View.GONE);
        }
    }
}

用户列表 Activity,应用的最后一个 Activity。

这是一篇关于 Views 和 ViewGroups 的文章,但没有代码,您将无法理解此 Activity 中的 ViewGroup。这就是为什么我用这个 Activity 来结束这篇文章,我说的 ViewGroupListView,是每个应用程序中的常见视图。

ListView

Play 商店中的大多数应用程序都有 ListViewListView 的基本思想是管理一系列数据,当数据由用户动态创建时,您永远不知道其大小。ListView 是解决此问题的最佳方案,您只需为单个数据创建模板 View,Adapter 将把所有数据项应用于 View 并自动排列到列表中。

ListView 是一个 View 容器ViewGroup),它不是 Layout,您不能直接将项目放在 ListView 上,而是需要创建一个 Adapter,它像一个中间件,为 ListView 提供 View 项目。

上图显示了 ListView 的工作方式,Adapter 将消耗数据并创建 View(列表项),然后将其提供给 ListView

机制

当您将 Adapter 添加到 ListView 时,ListView 会询问数据源中的数据项数量,然后 ListView 会请求显示在屏幕上的数据项的 View。每次数据源更改后,此过程将重新运行。

注意: ListView 一次只存储 n+1 个项目,其中 n 是 ListView 在不滚动的情况下一次显示的最大项目数。

基础项目的 MainActivity 是一个很好的例子,查看 MainActivity 的布局,您将只看到一个 ListView

<ListView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@android:id/list"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

</ListView>

然后查看 MainActivity 的 Java 代码,您将看到 ActivityData 实例的集合。

        List<ActivityData> activityDataList = new ArrayList<ActivityData>();//List of the ActivityData instance, put you ActivityData here
        activityDataList.add(new ActivityData("Login",LoginActivity.class));
        activityDataList.add(new ActivityData("Welcome",WelcomeActivity.class));
        activityDataList.add(new ActivityData("Sign up Activity",SignUpActivity.class));
        activityDataList.add(new ActivityData("Profile Activity",ProfileActivity.class));
        activityDataList.add(new ActivityData(getString(R.string.title_activity_sample_inflate_view_from_xml),SampleInflateViewFromXmlActivity.class));//add the ActivityData to the List
        activityDataList.add(new ActivityData(getString(R.string.title_activity_sample_create_view_in_java_code),SampleCreateViewInJavaCodeActivity.class));
        activityDataList.add(new ActivityData("My Fist Activity",MyFirstActivity.class));
        activityDataList.add(new ActivityData("TextView Example",TextViewExampleActivity.class));
        activityDataList.add(new ActivityData("EditText Example",EditTextExampleActivity.class));
        activityDataList.add(new ActivityData("ImageView Example",ImageViewExampleActivity.class));
        activityDataList.add(new ActivityData("Button Example",ButtonExampleActivity.class));
        activityDataList.add(new ActivityData("CheckBox Example",CheckBoxExampleActivity.class));
        activityDataList.add(new ActivityData("LinearLayout  Example",LinearLayoutExampleActivity.class));
        activityDataList.add(new ActivityData("RelativeLayout  Example",RelativeLayoutExampleActivity.class));
        activityDataList.add(new ActivityData("Options Menu  Example",OptionsMenuExampleActivity.class));
        activityDataList.add(new ActivityData("Radio Button  Example",RadioButtonExampleActivity.class));
        activityDataList.add(new ActivityData("Switch  Example",SwitchExampleActivity.class));

ActivityData 的成员。

  • Title - 将在 ListView 上显示的标题。
  • ActivityClass - 用于打开 Activity 的 Activity 类。

我想在 ListView 上显示此集合,Adapter 是我需要的东西。要创建 Adapter,您需要创建一个继承自 BaseAdapter 的类。

这些是您需要重写的 Adapter 方法。

  • getCount() - 数据源(或 Collection)中的项目数。
  • getItem() - 返回特定位置的数据项。
  • getItemId() - 返回特定位置的数据项的 id。
  • getView() - 返回特定位置的数据项的 View 实例。

ActivityDataListAdapter 中,我创建了带有 ContextActivityData Collection 作为参数的构造函数。

public class ActivityDataListAdapter extends BaseAdapter {

    private Context mContext;
    private List<ActivityData> mItems;

    public ActivityDataListAdapter(Context context,List<ActivityData> items)
    {
        mContext = context;
        mItems = items;
    }

    ...

}

然后,我创建了用于管理 Collection 的方法。

    public void add(ActivityData activityData)
    {
        mItems.add(activityData);
        notifyDataSetChanged();
    }

    public void addAll(List<ActivityData> items)
    {
        mItems.addAll(items);
        notifyDataSetChanged();
    }

    public void remove(int index)
    {
        mItems.remove(index);
        notifyDataSetChanged();
    }

    public void remove(ActivityData activityData)
    {
        mItems.remove(activityData);
        notifyDataSetChanged();
    }

注意: notifyDataSetChanged() 方法用于请求 ListView 刷新。在添加、删除、编辑数据源中的项目后,您需要调用它。

getCount() 方法将返回数据源大小。

    @Override
    public int getCount() {
        return mItems.size();
    }

getItem() 方法将返回特定位置的数据项。

    @Override
    public ActivityData getItem(int i) {
        return mItems.get(i);
    }

我们的数据没有 id 属性,在 getItemId() 方法中,只需返回零。

   @Override
    public long getItemId(int i) {
        return 0;
    }

然后,我需要为 getView() 方法创建 View。我创建了一个包含 2 个 TextViewView,第一个用于标题,第二个用于 Activity 类名。

listitem_activity_data.xml

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="8dp">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:text="@string/listitem_textview_default_activity_title"
        android:id="@+id/textview_title"
        android:padding="8dp"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceSmall"
        android:text="@string/listitem_textview_activity_class_name"
        android:id="@+id/textview_class"
        android:padding="8dp"/>
</LinearLayout>

注意:列表项布局的名称应以“listitem”开头。

然后,重写 getView() 方法。

    @Override
    public View getView(int i, View view, ViewGroup viewGroup) {
        
        if(view == null)
        {
            view = LayoutInflater.from(mContext).inflate(R.layout.listitem_activity_data,null);
        }

        ActivityData activityData = getItem(i);

        String title = activityData.getTitle();
        Class clazz = activityData.getActivityClass();

        TextView titleTextView = (TextView)view.findViewById(R.id.textview_title);
        TextView classTextView = (TextView)view.findViewById(R.id.textview_class);

        if(title==null)
            titleTextView.setText(mContext.getString(R.string.listitem_textview_default_activity_title));
        else
            titleTextView.setText(title);

        if(clazz==null)
            classTextView.setText(mContext.getString(R.string.listitem_textview_activity_class_name));
        else
            classTextView.setText(clazz.getSimpleName());

        return view;
    }

getView() 方法中,有 IntegerViewViewGroup 参数,第一个参数是当前位置,第二个参数是当前项目的 View,第三个参数是 ListView 实例。首先,我检查 View 的实例,如果不存在,则膨胀一个新的,然后获取特定位置的项目,获取 Views 实例并设置数据。

回到 MainActivity 的 Java 代码,创建 ActivityDataListAdapter 的实例并将其设置给 ListView

ActivityDataListAdapter activityDataListAdapter = new ActivityDataListAdapter(this,activityDataList);
ListView listView = (ListView)findViewById(android.R.id.list);
listView.setAdapter(activityDataListAdapter);

运行应用时,您将看到 ActivityData 列表如下。

现在,当用户点击列表项时,添加操作,方法是实现 OnItemClickListener

    AdapterView.OnItemClickListener onItemClickListener = new AdapterView.OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
            ActivityData activityData = (ActivityData)adapterView.getAdapter().getItem(i);//get the selected item from the Adapter
            if(activityData.getActivityClass()!=null)
            {
                try{
                    Intent intent = new Intent(MainActivity.this, activityData.getActivityClass());//create the intent for start the new Activity
                    startActivity(intent);//start the new Activity
                }
                catch(ActivityNotFoundException e)//Activity not found or the class that you provide is not an Activity
                {
                    AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);//Create alert dialog builder
                    builder.setIcon(R.drawable.ic_error);//set dialog icon to drawable resource
                    builder.setTitle(R.string.error_dialog_title);//set dialog title
                    builder.setMessage(
                            String.format(
                                    getString(R.string.error_dialog_activity_not_found_message),
                                    activityData.getActivityClass().getSimpleName()
                            )
                    );//set dialog message
                    builder.setPositiveButton(android.R.string.ok,null);//set positive button title and action
                    builder.show();//show the dialog
                }
            }
        }
    };

OnItemClickListener 实例将使用户转到他们选择的 Activity,如果找不到 Activity,则会显示错误。

然后设置 ListView 在项目被点击时调用此 Listener

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...
        listView.setOnItemClickListener(onItemClickListener);
    }

完成!!

熟悉的 Views: GridViewSpinner

创建 UserListActivity

现在,您已经了解了 ListViewAdapter。您现在可以创建 UserListActivity,让我们开始吧!!

首先,您需要创建 Activitylayout。然后将 ListView 添加到 layout 文件中。

<ListView 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"
    tools:context="me.vable.android.viewandlayoutlessons.UserListActivity"
    android:id="@android:id/list">

</ListView>

第二,创建 User 项目的 View

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="66dp"
    >

    <ImageView
        android:id="@+id/imageview_profile"
        android:src="@drawable/ic_man"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:adjustViewBounds="true"
        android:layout_margin="8dp"
        />

    <TextView
        android:id="@+id/textview_username"
        android:layout_gravity="center_vertical"
        android:text="Username"
        android:textSize="18sp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="8dp"/>

</LinearLayout>

第三,创建名为 UserListAdapterAdapter,它消耗来自 User 列表的数据。

package me.vable.android.viewandlayoutlessons.data.adapter;

import android.content.Context;
import android.media.Image;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;

import java.util.List;

import me.vable.android.viewandlayoutlessons.R;
import me.vable.android.viewandlayoutlessons.data.User;
import me.vable.android.viewandlayoutlessons.data.service.UserService;

/**
 * Created by Varavut on 8/23/2014.
 */
public class UserListAdapter extends BaseAdapter {
    
    Context mContext;
    List<User> mItems;
    public UserListAdapter(Context context,List<User> users)
    {
        mContext = context;
        mItems = users;
    }
    
    @Override
    public int getCount() {
        return mItems.size();
    }

    @Override
    public User getItem(int i) {
        return mItems.get(i);
    }

    @Override
    public long getItemId(int i) {
        return 0;
    }

    @Override
    public View getView(int i, View view, ViewGroup viewGroup) {
        if(view == null)
        {
            view = LayoutInflater.from(mContext).inflate(R.layout.listitem_user,null);
        }
        User user = getItem(i);
        ImageView profileImageView = (ImageView)view.findViewById(R.id.imageview_profile);
        TextView usernameTextView = (TextView)view.findViewById(R.id.textview_username);

        profileImageView.setImageBitmap(UserService.getInstance(mContext).getProfileImage(user));
        usernameTextView.setText(user.getUsername());
        return view;
    }
}

接下来,从 UserService 获取 User List,在 UserListActivity 的 Java 代码中创建 UseListAdapter 的实例并将其设置给 ListView

这是我的代码。

package me.vable.android.viewandlayoutlessons;

import android.app.Activity;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.ListView;

import java.util.ArrayList;
import java.util.List;

import me.vable.android.viewandlayoutlessons.data.User;
import me.vable.android.viewandlayoutlessons.data.adapter.UserListAdapter;
import me.vable.android.viewandlayoutlessons.data.service.UserService;

public class UserListActivity extends ActionBarActivity {

    List<User> users = new ArrayList<User>();
    UserListAdapter adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_user_list);

        adapter = new UserListAdapter(this, users);

        UserService.getInstance(this).getUserList(getUserListListener);

        ListView userListView = (ListView) findViewById(android.R.id.list);
        userListView.setAdapter(adapter);
    }

    UserService.GetUserListListener getUserListListener = new UserService.GetUserListListener() {
        @Override
        public void onResponce(boolean success, String message, List<User> userList) {
            if(success)
            {
                users.clear();
                adapter.notifyDataSetChanged();
                users.addAll(userList);
                adapter.notifyDataSetChanged();
            }
        }
    };
}

当用户点击列表项时,添加操作,应用程序将导航到 UserProfileActivity

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...
        userListView.setOnItemClickListener(onItemClickListener);
    }

    AdapterView.OnItemClickListener onItemClickListener = new AdapterView.OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
            Intent intent = new Intent(UserListActivity.this,ProfileActivity.class);
            intent.putExtra("user",adapter.getItem(i));
            startActivity(intent);
        }
    };

最后,回到 WelcomeActivity,有一个没有操作的 User List menu。添加用于打开 UserListActivity 的操作。

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        if (id == R.id.menu_user_list) {
            goToUserListPage();
            return true;
        }else if (id == R.id.menu_profile) {
            goToMenuProfilePage();
            return true;
        }else if (id == R.id.menu_logout) {
            logout();
            return true;
        }
        return super.onOptionsItemSelected(item);
    }
    
    private void goToUserListPage()
    {
        Intent intent = new Intent(this,UserListActivity.class);
        startActivity(intent);
    }

运行应用程序并尝试使用所有功能!!

完整的项目仓库

关注点

Android 提供了许多基本的 ViewsViewGroup 组件,此外,您还可以创建自己的自定义 View 和 Compound View。本文只是 Android View 系统的一小部分,还有很多 Views 您应该学习。希望您享受您的 Android 应用开发生活!!

历史

2014/08/24 首次提交。

2014/08/24 添加了熟悉的 Views 信息。

2014/08/24 修正了拼写错误。

2014/08/25 修正了设备信息。

© . All rights reserved.