Android基础:常用布局和数据存储

Posted Jack-Chan

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android基础:常用布局和数据存储相关的知识,希望对你有一定的参考价值。

1. 相对布局RelativeLayout

特点:相对布局所有组件可以叠加在一起;各个组件的布局是独立的,互不影响;所有组件的默认位置都是在左上角(顶部、左部对齐)

属性功能描述
android:layout_toRightOf在指定控件的右边
android:layout_toLeftOf在指定控件的左边
android:layout_above在指定控件的上边
android:layout_below在指定控件的下边
android:layout_alignBaseline跟指定控件水平对齐
android:layout_alignLeft跟指定控件左对齐
android:layout_alignRight跟指定控件右对齐
android:layout_alignTop跟指定控件顶部对齐
android:layout_alignBottom跟指定控件底部对齐
android:layout_alignParentLeft是否跟父元素左对齐
android:layout_alignParentTop是否跟父元素顶部对齐
android:layout_alignParentRight是否跟父元素右对齐
android:layout_alignParentBottom是否跟父元素底部对齐
android:layout_centerVertical在父元素中垂直居中
android:layout_centerHorizontal在父元素中水平居中
android:layout_centerInParent在父元素中居中

示例1:res\\layout\\activity_main.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"
    tools:context=".MainActivity" >
    <!-- android:layout_alignParentRight="true"表示与父元素右对齐(这里,父元素指的就是RelativeLayout,而RelativeLayout占满了整个屏幕) -->
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="第一个" 
        android:layout_alignParentRight="true"/>
    <!-- android:layout_alignParentBottom="true"表示与父元素底部对齐(这里,父元素指的就是RelativeLayout,而RelativeLayout占满了整个屏幕) -->
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="第二个" 
        android:layout_alignParentBottom="true"/>
    <!-- android:layout_centerVertical="true"表示在父元素中垂直居中(这里,父元素指的就是RelativeLayout,而RelativeLayout占满了整个屏幕) -->
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="第三个" 
        android:layout_centerVertical="true"
        />
    <!-- android:layout_centerHorizontal="true"表示在父元素中水平居中(这里,父元素指的就是RelativeLayout,而RelativeLayout占满了整个屏幕) -->
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="第四个" 
        android:layout_centerHorizontal="true"
        />
    <!-- android:layout_centerInParent="true"表示在父元素中水平垂直都居中(这里,父元素指的就是RelativeLayout,而RelativeLayout占满了整个屏幕) -->
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="第五个" 
        android:layout_centerInParent="true"
        />
</RelativeLayout>

运行结果:

img

示例2:res\\layout\\activity_main.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"
    tools:context=".MainActivity" >
    <TextView
        android:id="@+id/tv1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="第一一一一个" />
    <!-- android:layout_alignRight="true"表示与指定组件右对齐。 -->
    <!-- @+id/就是为组件添加id,@id就是通过id引用组件 -->
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="第二个" 
        android:layout_centerVertical="true"
        android:layout_alignRight="@id/tv1"
        />
    <!-- 如果同时使用android:layout_alignRight="true",android:layout_alignLeft="true",组件就会被拉伸 -->
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="第三个" 
        android:layout_alignParentBottom="true"
        android:layout_alignRight="@id/tv1"
        android:layout_alignLeft="@id/tv1"
        />
</RelativeLayout>  

运行结果:

img

示例3:res\\layout\\activity_main.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"
    tools:context=".MainActivity" >

    <TextView
        android:id="@+id/tv1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="第一个"
        android:layout_centerHorizontal="true"/>

    <!-- android:layout_below="@id/tv1"表示在对应组件的下面 -->
    <TextView
        android:id="@+id/tv2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="第二个"
        android:layout_below="@id/tv1"
        />

    <!-- android:layout_above="@id/tv2"表示在对应组件的上面 -->
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="第三个"
        android:layout_above="@id/tv2"
        />

    <!-- android:layout_toRightOf="@id/tv2"表示在对应组件的右边 -->
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="第四个"
        android:layout_toRightOf="@id/tv2"
        />
</RelativeLayout>

运行结果:

img

练习:实现如下效果

img

代码:res\\layout\\activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
    <Button 
        android:id="@+id/center"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="中间"
        android:layout_centerInParent="true"/>
    <Button 
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="上边"
        android:layout_above="@id/center"
        android:layout_alignLeft="@id/center"
        android:layout_alignRight="@id/center"/>
    <Button 
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="下边"
        android:layout_below="@id/center"
        android:layout_alignLeft="@id/center"
        android:layout_alignRight="@id/center"/>
    <Button 
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="左边"
        android:layout_toLeftOf="@id/center"
        android:layout_alignTop="@id/center"
        android:layout_alignBottom="@id/center"
        android:layout_alignParentLeft="true"/>
    <Button 
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="右边"
        android:layout_toRightOf="@id/center"
        android:layout_alignTop="@id/center"
        android:layout_alignBottom="@id/center"
        android:layout_alignParentRight="true"/>
        <Button 
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="左上"
        android:layout_toLeftOf="@id/center"
        android:layout_above="@id/center"
        android:layout_alignParentLeft="true"/>
        <Button 
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="左下"
        android:layout_toLeftOf="@id/center"
        android:layout_below="@id/center"
        android:layout_alignParentLeft="true"/>
        <Button 
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="右上"
        android:layout_toRightOf="@id/center"
        android:layout_above="@id/center"
        android:layout_alignParentRight="true"/>
        <Button 
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="右下"
        android:layout_toRightOf="@id/center"
        android:layout_below="@id/center"
        android:layout_alignParentRight="true"/>
</RelativeLayout>

运行结果:

img

2. 帧布局FrameLayout

特点:帧布局和相对布局一样,组件可以重叠;所有组件的默认位置是在左上角(顶部、左部对齐)
示例:res\\layout\\activity_main.xml

<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=".MainActivity" >
    <!-- 帧布局中,修改组件的位置使用android:layout_gravity -->
    <TextView 
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="第一个"
        android:layout_gravity="right"
        />
    <!-- android:layout_gravity="bottom|right" ,表示组件的位置在右下角 -->
    <TextView 
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="第二个"
        android:layout_gravity="bottom|right" 
        />
    <TextView 
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="第三个"
        android:layout_gravity="center" 
        />
</FrameLayout>  

运行结果:

img

练习:实现如下效果

img

代码:res\\layout\\activity_main.xml

<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=".MainActivity" >
    <TextView 
        android:layout_width="240dp"
        android:layout_height="240dp"
        android:background="#ff0000"
        android:layout_gravity="center"
        />
    <TextView 
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:background="#00ff00"
        android:layout_gravity="center"
        />
    <TextView 
        android:layout_width="160dp"
        android:layout_height="160dp"
        android:background="#0000ff"
        android:layout_gravity="center"
        />
    <TextView 
        android:layout_width="120dp"
        android:layout_height="120dp"
        android:background="#ffff00"
        android:layout_gravity="center"
        />
    <TextView 
        android:layout_width="80dp"
        android:layout_height="80dp"
        android:background="#ff00ff"
        android:layout_gravity="center"
        />
    <TextView 
        android:layout_width="40dp"
        android:layout_height="40dp"
        android:background="#ffffff"
        android:layout_gravity="center"
        />
</FrameLayout>   

运行结果:

img

3. 表格布局TableLayout

示例:res\\layout\\activity_main.xml

<TableLayout 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=".MainActivity" >
    <!-- 表格布局宽高可以不用设置,TableRow的宽度只能是match_parent,高度只能是wrap_content。调正宽度可以通过权重解决。-->
    <TableRow>
        <TextView 
                android:text="姓名:"/>
        <EditText
            android:layout_weight="1"
            />
    </TableRow>
    <TableRow>
        <TextView 
                android:text="年龄:"/>
        <EditText
            android:layout_weight="1"
            />
    </TableRow>
</TableLayout>*复制代码*   运行结果:

img

4. 绝对布局AbsoluteLayout

绝对布局是直接使用android:layout_x,android:layout_y定位控件的坐标,做不了屏幕适配,所以不常使用。某些没有必要做屏幕适配的开发可以用绝对布局,例如:电视屏幕固定,做机顶盒开发。

示例:res\\layout\\activity_main.xml

<AbsoluteLayout 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=".MainActivity" >
    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_x="109dp"
        android:layout_y="83dp"
        android:text="Button" />
</AbsoluteLayout>*复制代码*   运行结果:

img

4. LogCat

Console只能显示Windows下运行的平台信息,例如:模拟器的运行状态(模拟器是运行在Windows平台上的程序)。但模拟器内的程序运行状态就不能显示在Console上了,因为这些程序不是运行到Windows上,这些信息会在LogCat中显示。

LogCat分为5个等级,依次为:error(错误)、warn(情报)、info(信息)、debug(调试)、verbose(冗余)

img

示例:为LogCat添加过滤器,便于筛选信息

src/cn.itcast.logcat/MainActivity.java

package cn.itcast.logcat;
import android.os.Bundle;
import android.app.Activity;
import android.util.Log;
import android.view.Menu;
public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        String tag = "黑马程序员";
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        System.out.println("云鹤,儿童节快乐");
        //tag表示标签,msg表示正文
        //下面这种写法是在日志需要长期保留时使用。
        Log.v(tag, "云鹤就业薪资13000");
        Log.d(tag, "云鹤就业薪资13000");
        Log.i(tag, "云鹤就业薪资13000");
        Log.w(tag, "云鹤就业薪资13000");
        Log.e(tag, "云鹤就业薪资13000");
   }
}

运行结果:

img

第一步:点击加号

img

第二步:填入过滤器设置信息。

img

第三步:进行过滤。

img

5. 在内部存储中读写文件

Android内存

  • RAM:运行内存,相当于电脑内存。关机,数据就会丢失。
  • ROM:内部存储空间,Android系统必须的存储空间,持久化保持数据,相当于电脑的硬盘。
  • SD卡/USB存储器(内置在手机内):外部存储空间,对于Android系统是可有可无的,相当于移动硬盘。
  • 内部存储路径:data/data/包名文件夹/,每个包名文件夹都是一个应用的专属空间。

img

示例:res\\layout\\activity_main.xml

<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:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" 
    android:orientation="vertical">
    <EditText
        android:id="@+id/et_name"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>
    <!-- android:inputType="textPassword"表示输入的是密码,显示时用密文代替 -->
    <EditText
        android:id="@+id/et_pass"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:inputType="textPassword"/>
    <RelativeLayout 
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
            <CheckBox 
                android:id="@+id/cb"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="记住账号密码"
                android:layout_centerVertical="true"
                />
            <Button 
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="登陆"
                android:onClick="login"
                android:layout_alignParentRight="true"
                />
        </RelativeLayout>
</LinearLayout>

src/cn.itcast.rwinrom/MainActivity.java

package cn.itcast.rwinrom;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.Toast;
public class MainActivity extends Activity {
    private EditText et_name;
    private EditText et_pass;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        et_name = (EditText) findViewById(R.id.et_name);
        et_pass = (EditText) findViewById(R.id.et_pass);
        readAccount();
    }
    public void login(View v){
            //获取用户输入的数据
            String name = et_name.getText().toString();
            String pass = et_pass.getText().toString();
            CheckBox cb = (CheckBox) findViewById(R.id.cb);
            if(cb.isChecked()){
                    File file = new File("data/data/cn.itcast.rwinrom/info.txt");
                    try{
                            FileOutputStream fos = new FileOutputStream(file);
                            fos.write((name + "##" + pass).getBytes());
                            fos.close();
                    }catch(Exception e){
                            e.printStackTrace();
                    }
            }
            //弹出吐司对话框
            //Activity是Context的子类
            //Toast.LENGTH_LONG表示显示5秒
            //Toast.LENGTH_SHORT表示显示3秒
            Toast.makeText(this, "登陆成功", Toast.LENGTH_LONG).show();
    }
    public void readAccount(){
            File file = new File("data/data/cn.itcast.rwinrom/info.txt");
            if(file.exists()){
                    try{
                            FileInputStream fis = new FileInputStream(file);
                            //把字节流转换成字符流
                            BufferedReader br = new BufferedReader(new InputStreamReader(fis));
                            String text = br.readLine();
                            String[] s = text.split("##");
                            et_name.setText(s[0]);
                            et_pass.setText(s[1]);
                            fis.close();
                    }catch(Exception e){
                            e.printStackTrace();
                    }
            }
    }
}

运行结果:

1、在模拟器上运行应用程序,输入用户名、密码,勾上记住账号密码,点击登陆。可以看到弹出吐司,登陆成功。

img

2、在Android设备文件目录下,可以看到成功生成info.txt文件,点击右上角的“pull a file from the device”按钮,将info.txt提取出来,保存到桌面。

img

3、打开info.txt文件,可以看到成功存入数据。

img

4、重新运行应用程序,可以看到读取数据成功,用户名、密码成功显示。

img

PS:
1、点击返回键,会摧毁当前Activity,重新点击应用图标,会重新创建Activity。
2、点击HOME键,只会将Activity放到后台去,重新点击图标,只是重新提取出来。

所以,上面的应用程序,即使是第一次运行,输入用户名、密码,并且不勾选“记住账户密码”。如果此时点击HOME键,Activity消失。然后,再点击应用程序图标,竟然可以看到Activity显示出了用户名、密码!然而,这根本不是回显,Activity只是暂时放到后台,然后重新提取出来罢了。

3、在内部存储空间中读写不需要任何权限!

5.1 使用API获取内部存储空间的路径

如果将上面应用程序的MainActivity类中的代码进行如下修改,可以看到后台报警告。原因是不能访问其他应用程序的专属空间,只能访问自己的。

img

img

为了防止内部存储空间不小心写错,API提供了获取内部存储空间路径的方法。修改后,代码如下:

img

img

运行结果,如下:

img

getFilesDir方法所在的类为上下文包装类,ContextWrapper。

img

并且,Activity就是ContextWrapper的子类。

img

所谓的上下文就是一个应用环境全局信息的接口,也就是说,通过上下文的API可以拿到应用环境的全局信息,例如:包名、版本号、内部存储等信息。
Toast.makeText(this,”登陆成功”,0).show();语句中的this就是上下文,表示吐司需要显示在哪个上下文中。
Button bt = new Button(this);语句中的this也是上下文,表示该组件创建出来之后,显示在哪个上下文中。
谷歌的API还提供了另一个方法获取内部存储空间路径:getCacheDir(),它返回的路径为data/data/cn.itcast.rwinrom/cache,得到的是缓存路径,也就是cache文件夹路径。修改后,代码如下:

img

运行结果,如下:

img

区别:

  • getFilesDir方法返回的路径中的files文件夹是持久化保存数据的。
  • getCacheDir方法返回的路径中的Cache文件夹是保存缓存数据的。当设备内存可用存储空间比较少时,Cache中的文件可能会被系统删除。

6. 在外部存储中读写文件

SD卡路径为storage/sdcard(4.3版本),之所以还存在根目录sdcard(2.3版本之前)及mnt/sdcard(2.3版本~4.3版本),是因为早期版本的sd卡路径正是在根目录,后期移到了mnt/sdcard,为了与低版本兼容,所以现在他们还存在,其实只是快捷方式而已,指向的路径依然是storage/sdcard。

img

往SD卡中写数据需要权限。而从SD卡中读取数据在4.0版本以前是不需要权限,后来谷歌在Android系统中添加了保护SD卡的选项(如下图)。如果勾选了,那么在真机上是有效的(也就是说,模拟器上是无效的)。不过,一般情况下,不勾选。因为,很多应用程序并没有申请读取SD卡的权限,一旦勾上,很多应用程序将无法读取SD卡。因此,为了向下兼容,一般不勾。

img

示例:将上面“在内部存储中读取文件”中示例的MainActivity类中代码做一下修改,如下。

img

img

为读写SD卡添加权限(读SD卡权限可加可不加):

img

img

运行结果:

img

img

6.1 检测SD卡状态

很多手机厂商都修改了SD卡路径,因此,很多手机SD卡的路径不是storage/sdcard。但是,sdcard的快捷方式基本上都是存在的,并且指向真实的SD卡路径。因此,访问SD卡路径,可以通过获取SD卡快捷方式的方法。当然,也可以调用API中提供的相应方法获取SD卡路径:Environment.getExternalStorageDirectory()

外部存储空间对于手机来说并不一定是必须的。所以,在使用前,一定要做确认手机的SD卡是否存在。可以通过Environment.getExternalStorageState()确定SD卡的状态,此方法的常见返回值说明如下:

  • MEDIA_REMOVED:sd卡被拔出
  • MEDIA_UNMOUNTED:sd卡未挂载(4.0版本以前,手机可以通过点击settings–>Storage–>Storage settings–>Unmounted SD card,使SD卡未挂载)
  • MEDIA_- CHECKING:sd卡正在准备
  • MEDIA_MOUNTED:sd卡已挂载,当前可用
  • MEDIA_READ_ONLY:sd卡挂载可用,但是只读

示例:将上面“在内部存储中读取文件”中示例的MainActivity类中代码做一下修改,如下

img

运行结果:由于在4.0版本之前才有手动使SD卡未挂载的功能,这里使用2.3.3版本测试。首先,Unmounted SD card,观察效果,可以看到提示:sd不可用。

img

img

然后,mounted SD card,观察效果,成功保存用户名、密码信息。

img

img

当有多个模拟器运行时,想要观察某一个模拟器的文件目录,只需要在Devices选项卡中点击那个模拟器,即可在File Explorer选项卡中看到那个模拟器的文件目录。

img

6.2 获取SD卡剩余容量

当用户做下载等操作时,SD卡的空间可能不足,所以需要提前获取SD卡剩余容量,给予用户提示信息。在Android系统中,可以查看SD卡剩余容量,点击Setting–>Storage

img

sdk文件夹中的sources文件夹中的源码是Android jar包的源码,Android系统源码则不同,一定要区分开。如下图。

img

其中,packages/apps路径为Android系统级应用程序所在的目录,可以看到settings应用程序。

img

7. 查看settings应用程序的源码

7.1 导入settings应用程序到eclipse中

点击File–>New–>Other….

img

点击Android下的Android Project from Existing Code–>Next

img

点击Root Directory右侧的Browse…按钮,找到settings应用程序所在的目录,点击确定

img

下图中的tests为测试settings的应用程序,不勾

img

导入成功

img

之所以会报错,是因为普通应用开发是无法访问系统级API的(settings工程导入eclipse就成为了一个普通应用开发项目)

7.2 查找实现查看SD卡剩余空间的源码

通过搜索关键字“Available space”开始查找

img

通过eclipse的“File Search”在整个工作空间中搜索“Available space”

img

img

查找到以后,双击

img

img

再搜索“memory_available”

img

img

再搜索“memory_sd_avail”

img

img

再在本文件内搜索“MEMORY_SD_AVAIL”

img

再在本文件内搜索“mSdAvail”,可以看到,已经查找到了该段代码

img

7.3 解析源码

所有存储设备都会被划分成若干个区块,存储设备的总容量 = 每个区块大小 * 区块总数量

img

其中的formatSize函数实际上就是将得出的字节数,转换成MB、GB、TB及PB显示给用户

img

示例:我们自己写程序获取SD卡剩余容量

res\\layout\\activity_main.xml

<RelativeLayout xmlns:android=Android-常用布局与数据存储

在Android中,如何将数据从类传递到相应的布局/片段文件?

Android基础之常用五种布局

Android获取片段中的布局高度和宽度

iOS 相当于 Android 片段/布局

android片段表格布局