• android常用控件和布局


    新建一个UIWidgetDemo来测试Android中的控件和布局。

    控件

    TextView

      android中所有控件都有android:layout_width和android:layout_height这两个属性。这两个属性的可选值有3个:match_parent,fill_parent和wrap_parent。其中,match_parent和fill_parent意义相同,官方更推荐使用match_parent。

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <!-- gravity属性为文字的对齐方式,可选值有top,bottom,left,right和center,可以使用'|'来分隔多个值 -->
        <TextView
        android:id="@+id/text_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textColor="#00FFEA"
        android:gravity="center"
        android:textSize="24sp"
        android:text="This is a TextView!" />
    </LinearLayout>
    

    image.png

    Button

      默认情况下,系统会将button内的英文自动转换为大写,如果这不是你想要的效果,可以将textAllCaps属性设为false。

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    	android:orientation="vertical"
    	android:layout_width="match_parent"
    	android:layout_height="match_parent">
    
    	<Button
    		android:id="@+id/button"
    		android:layout_width="match_parent"
    		android:layout_height="wrap_content"
    		android:text="I am a button."
    		android:textAllCaps="false"
    		android:textColor="#ff0000" />
    
    </LinearLayout>
    

    image.png

    可以为button控件的点击事件注册一个监听器:

    import android.view.View;
    import android.widget.Button;
    import android.widget.Toast;
    
    public class MainActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            Button button = (Button)findViewById(R.id.button);
            button.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Toast.makeText(MainActivity.this, "You clicked the button", Toast.LENGTH_SHORT).show();
                }
            });
        }
    }
    
    

    image

      当然了,如果觉得使用匿名内部类的方式来注册监听器不是很直观,也可以使用实现View.OnClickListener接口的方法来注册事件监听。

    import android.os.Bundle;
    import android.view.View;
    import android.widget.Button;
    import android.widget.Toast;
    
    public class MainActivity extends AppCompatActivity implements View.OnClickListener {
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            Button button = (Button) findViewById(R.id.button);
            button.setOnClickListener(this);
        }
    
        @Override
        public void onClick(View v) {
            switch (v.getId()) {
                case R.id.button:
                    Toast.makeText(MainActivity.this, "You clicked the button", Toast.LENGTH_SHORT).show();
                    break;
                default:
                    break;
            }
        }
    }
    

    image.png

    EditText

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <!-- hint表示输入框中的一段提示性文本 -->
        <EditText
            android:id="@+id/edit_text"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="Type something here!"
            android:maxLines="2" />
        <!-- maxLines表示输入框中可向下拓展的最大行数,超过这个行数则会产生滚动 -->
    </LinearLayout>
    

    image.png

    可以结合按钮和输入框来做一些事情:

    public class MainActivity extends AppCompatActivity implements View.OnClickListener {
        private EditText editText;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            Button button = (Button) findViewById(R.id.button);
            editText = (EditText) findViewById(R.id.edit_text);
            button.setOnClickListener(this);
        }
    
        @Override
        public void onClick(View v) {
            switch(v.getId()) {
                case R.id.button:
                    // 在按钮的点击事件处理函数中得到输入框的输入并以toast的形式显示出来
                    String inputText = editText.getText().toString();
                    Toast.makeText(MainActivity.this, inputText, Toast.LENGTH_SHORT).show();
                    break;
                default:
                    break;
            }
        }
    }
    

    image

    ImageView

    <?xml version="1.0" encoding="utf-8" ?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
            <ImageView
                android:id="@+id/img1"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:src="@drawable/img_1" />
    
            <Button
                android:id="@+id/button"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="I am a button."
                android:textAllCaps="false"
                android:textColor="#ff0000" />
    
    </LinearLayout>
    

    image.png

    可以在程序中通过代码动态的更改ImageView中的图片,修改MainActivity的代码:

    public class MainActivity extends AppCompatActivity implements View.OnClickListener {
        private ImageView img1;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            Button button = (Button) findViewById(R.id.button);
            img1  = (ImageView) findViewById(R.id.img1);
            button.setOnClickListener(this);
        }
    
        @Override
        public void onClick(View v) {
            switch(v.getId()) {
                case R.id.button:
                    // 点击按钮后改变图片
                    img1.setImageResource(R.drawable.img_2);
                    break;
                default:
                    break;
            }
        }
    }
    

    image

    ProgressBar

    <?xml version="1.0" encoding="utf-8" ?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
            <Button
                android:id="@+id/button"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="I am button."
                android:textAllCaps="false"
                android:textColor="#ff0000" />
    
            <ProgressBar
                android:id="@+id/progress_bar"
                android:layout_width="match_parent"
                android:layout_height="wrap_content" />
    
    </LinearLayout>
    

    image

      这时我们可能会注意到一个问题,旋转的进度条表示程序正在加载数据,而数据总会有加载完的时候,如何让进度条在数据加载完成后就消失呢?

      这就需要用到android:visibility控件可见属性进行指定,它的可选值有3种:visible,invisible和gone。visible表示控件是可见的,是默认值;invisible表示控件不可见,但它仍然会占据着原来的位置,可以理解为控件变为全透明状态了;gone则表示控件不仅不可见,而且不会在占用任何屏幕控件。

      除了可以直接设置控件的xml属性android:visibility来指定控件的可见性,还可以在代码中使用setVisibility()方法指定控件的可见与否,可以向它传入View.VISIBLE,View.INVISIBLE和View.GONE这3个值。

    接下来我们就来实现当数据加载完成后进度条消失的效果:

    public class MainActivity extends AppCompatActivity implements View.OnClickListener {
        private Button button;
        private ProgressBar progressBar;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            button = (Button) findViewById(R.id.button);
            progressBar = (ProgressBar) findViewById(R.id.progress_bar);
            button.setOnClickListener(this);
        }
    
        @Override
        public void onClick(View v) {
            switch (v.getId()) {
                case R.id.button:
                    // 通过getVisibility()来判断当前进度条是否可见
                    if(progressBar.getVisibility() == View.GONE) {
                        progressBar.setVisibility(View.VISIBLE);
                    } else {
                        progressBar.setVisibility(View.GONE);
                    }
                    break;
                default:
                    break;
            }
        }
    }
    

    image

    另外,默认情况下,进度条是圆形样式,可以使用style属性将它指定为水平进度条:

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        ...
    
        <ProgressBar
            android:id="@+id/progress_bar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            style="?android:attr/progressBarStyleHorizontal"
            android:max="100" />
            <!-- 给进度条指定max最大值100 -->
    </LinearLayout>
    

    image.png

    将进度条改为水平进度条后,我们就可以在程序中动态的更新进度条的进度值:

    public class MainActivity extends AppCompatActivity implements View.OnClickListener {
        private Button button;
        private ProgressBar progressBar;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            button = (Button) findViewById(R.id.button);
            progressBar = (ProgressBar) findViewById(R.id.progress_bar);
            button.setOnClickListener(this);
        }
    
        @Override
        public void onClick(View v) {
            switch (v.getId()) {
                case R.id.button:
                    // 使用getProgress()获取当前的进度值
                    int progress = progressBar.getProgress();
                    progress = progress + 10;
                    progressBar.setProgress(progress);
                    break;
                default:
                    break;
            }
        }
    }
    

    image

    AlertDialog

      AlertDialog可以在当前的界面中弹出一个对话框,这个对话框是置顶于所有界面元素之上的,能够屏蔽掉其他控件的交互能力(模态窗口)。因此AlertDialog一般用于提示一些非常重要的内容或警告信息。

    下面我们来看看怎么使用AlertDialog控件:

    import android.content.DialogInterface;
    import android.support.v7.app.AlertDialog;
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.view.View;
    import android.widget.*;
    
    public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            Button button = (Button) findViewById(R.id.button);
            button.setOnClickListener(this);
        }
    
        @Override
        public void onClick(View v) {
            switch (v.getId()) {
                case R.id.button:   // 点击事件是由R.id.button发起的
                    // 首先构造一个AlertDialog控件对象
                    AlertDialog.Builder dialog = new AlertDialog.Builder(MainActivity.this);
                    dialog.setTitle("This is a Dialog");
                    dialog.setMessage("Something important.");
                    dialog.setCancelable(false);
                    // 设置"确定"按钮的点击事件
                    dialog.setPositiveButton("OK", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {}
                    });
                    // 设置"取消"按钮的点击事件
                    dialog.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {}
                    });
    
                    // 显示对话框
                    dialog.show();
                    break;
                default:
                    break;
            }
        }
    }
    

    image

    ProgressDialog

      ProgressDialog和AlertDialog有点类似,都可以在界面上弹出一个对话框并屏蔽其他控件的交互能力。但不同的是,ProgressDialog会在对话框中显示一个进度条,一般用于表示当前操作比较耗时,让用户耐心的等待。

    它的用法和AlertDialog很类似:

    import android.app.ProgressDialog;
    import android.content.DialogInterface;
    import android.os.Bundle;
    import android.view.View;
    import android.widget.*;
    
    public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            Button button = (Button) findViewById(R.id.button);
            button.setOnClickListener(this);
        }
    
        @Override
        public void onClick(View v) {
            switch (v.getId()) {
                case R.id.button:
                    // 首先构造一个AlertDialog控件对象
                    ProgressDialog progressDialog = new ProgressDialog(MainActivity.this);
                    progressDialog.setTitle("This is a Dialog");
                    progressDialog.setMessage("Something important.");
                    progressDialog.setCancelable(true);     // 允许取消
    
                    progressDialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
                        @Override
                        public void onDismiss(DialogInterface dialog) {
                            Toast.makeText(MainActivity.this, "你点击了取消", Toast.LENGTH_SHORT).show();
                        }
                    });
    
                    // 显示对话框
                    progressDialog.show();
                    break;
                default:
                    break;
            }
        }
    }
    

    image

    ListView

      ListView应该可以算是Android中最常用的控件之一,几乎所有的应用程序都会用到它。

      由于手机屏幕空间是有限的,所以能够一次性在屏幕上显示的内容并不多,当我们的程序中有大量的数据需要展示的时候,就可以借助ListView来实现。

      ListView允许用户通过手指上下滑动的方式将屏幕外的数据滚动到屏幕内,同时屏幕上原有的数据则会滚动出屏幕。事实上,我们几乎每天都在接触这个控件,比如查看QQ聊天记录,翻阅微博最新消息,等等。

    ListView的简单用法

    首先给程序一个布局:

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <ListView
            android:id="@+id/list_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    
    </LinearLayout>
    

    在xml布局中加入ListView还算简单,下面看一看MainActivity中的代码:

    public class MainActivity extends AppCompatActivity {
        private String[] data = {
            "Apple", "Banana", "Orange", "Watermelon", "Pear", "Grape",
            "Pineapple", "Strawberry", "Cherry", "Mango", 
            "Apple", "Banana", "Orange", "Watermelon", "Pear", "Grape",
            "Pineapple", "Strawberry", "Cherry", "Mango"
        }
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            // 定义一个数组适配器adapter(上述数组中的元素都是字符串)
            ArrayAdapter<String> adapter = new ArrayAdapter<String>(MainActivity.this, android.R.layout.simple_list_item_1, data);
            // 使用android.R.layout.simple_list_item_1, data作为ListView子项布局的id,这是一个Android内置的布局文件,里面只有一个TextView,可用于简单地显示一段文本
            ListView listView = (ListView) findViewById(R.id.list_view);
            // listView应用这个适配器
            listView.setAdapter(adapter);
        }
    }
    

      ListView中的展示数据可以是从数据库中读取出来的,也可以是从网上下载下来的,这里我们就简单使用一个data数组来测试。

    image

    定制ListView界面

    只能显示一段文本的ListView实在是太单调了,我们可以对ListView做一些个性化的定制。

    下面的例子中,我们要在每个列表项前面加上一张图片。

    首先创建一个实体类,作为ListView适配器的适配类型。新建类Fruit:

    public class Fruit {
        private String name;    // 水果的名称
        private int imageId;    // 对应的图片的资源id
    
        public Fruit(String name, int imageId) {
            this.name = name;
            this.imageId = imageId;
        }
    
        public String getName() {
            return name;
        }
    
        public int getImageId() {
            return imageId;
        }
    }
    

    然后需要为ListView子项指定一个我们自定义的布局,新建fruit_item.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">
    <!--
        ListView中的每一项都对应有一个固定的布局文件,前面我们使用的是内置的android.R.layout.simple_list_item_1.xml,
        这里为了将图片和水果名称字符串都放在ListView中的一项中,我们使用自定义布局文件
     -->
        <ImageView
            android:id="@+id/fruit_image"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
    
        <TextView
            android:id="@+id/fruit_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:layout_marginLeft="10dp" />
    
    </LinearLayout>
    

    接下来就是最重要的,我们要创建自定义的适配器,当前,它要继承ArrayAdapter。新建类FruitAdapter:

    import android.content.Context;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.ArrayAdapter;
    import android.widget.ImageView;
    import android.widget.TextView;
    import java.util.List;
    
    public class FruitAdapter extends ArrayAdapter<Fruit> {
        private int resourceId;
    
        public FruitAdapter(Context context,  int textViewResourceId,  List<Fruit> objects) {
            // 可见,我们的自定义适配器FruitAdapter,在真正的适配部分其实还是调用的ArrayAdapter父类的构造方法
            super(context, textViewResourceId, objects);
            resourceId = textViewResourceId;
        }
    
        @Override
        // 重写ArrayAdapter的getView()方法,这个方法在每个子项滚动到屏幕内时会自动调用
        public View getView(int position,  View convertView,  ViewGroup parent) {
            Fruit fruit = getItem(position);	// 获取当前项的Fruit实例
            // 为每个子项加载我们传入的布局
            View view = LayoutInflater.from(getContext()).inflate(resourceId, parent, false);
            ImageView fruitImage = (ImageView) view.findViewById(R.id.fruit_image);
            TextView fruitName = (TextView) view.findViewById(R.id.fruit_name);
            fruitImage.setImageResource(fruit.getImageId());
            fruitName.setText(fruit.getName());
            return view;
        }
    }
    

    最后一步,就是更改MainActivity中的代码了:

    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.view.View;
    import android.widget.AdapterView;
    import android.widget.ArrayAdapter;
    import android.widget.ListView;
    import android.widget.Toast;
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class MainActivity extends AppCompatActivity {
    
        private List<Fruit> fruitList = new ArrayList<Fruit>();
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            initFruits();   // 初始化水果数据,初始化fruitList[ ]
            FruitAdapter adapter = new FruitAdapter(MainActivity.this, R.layout.fruit_item, fruitList);
            ListView listView = (ListView) findViewById(R.id.list_view);
    
            listView.setAdapter(adapter);   // 设定适配器
            // 为ListView中的每一项都添加一个点击事件监听器
            listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
                @Override
                public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                    Fruit fruit = fruitList.get(position);    // 得到点击位置处显示的对应fruitList中的元素
                    Toast.makeText(MainActivity.this, fruit.getName(), Toast.LENGTH_SHORT).show();
                }
            });
        }
    
        private void initFruits() {
            // fruitList列表中的每一项内容都是Fruit类型
            for (int i = 0; i < 2; i++) {   // 添加两遍(凑数)
                Fruit apple = new Fruit("Apple", R.drawable.apple_pic);
                fruitList.add(apple);
                Fruit banana = new Fruit("Banana", R.drawable.banana_pic);
                fruitList.add(banana);
                Fruit orange = new Fruit("Orange", R.drawable.orange_pic);
                fruitList.add(orange);
                Fruit watermelon = new Fruit("Watermelon", R.drawable.watermelon_pic);
                fruitList.add(watermelon);
                Fruit pear = new Fruit("Pear", R.drawable.pear_pic);
                fruitList.add(pear);
                Fruit grape = new Fruit("Grape", R.drawable.grape_pic);
                fruitList.add(grape);
                Fruit pineapple = new Fruit("Pineapple", R.drawable.pineapple_pic);
                fruitList.add(pineapple);
                Fruit strawberry = new Fruit("Strawberry", R.drawable.strawberry_pic);
                fruitList.add(strawberry);
                Fruit cherry = new Fruit("Cherry", R.drawable.cherry_pic);
                fruitList.add(cherry);
                Fruit mango = new Fruit("Mango", R.drawable.mango_pic);
                fruitList.add(mango);
            }
        }
    }
    

    image.png
    image

    RecyclerView

      ListView的功能是非常强大的,但也并不是没有缺点,如果我们不使用一些技巧来提示它的运行效率,那么它的性能会非常差。还有,ListView的扩展性也不够好,它只能实现纵向滚动的效果,如果想实现横向滚动的话,ListView就无能为力了。

      为此,Android提供了一个更强大的滚动控件——RecycleView

    布局

    线性布局LinearLayout

    线性布局内控件按水平或垂直方向顺次排布。

    <!-- android命名空间只需要在最顶层的布局中声明一次 -->
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
    
            <Button
                android:id="@+id/btn1"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="I am btn1."
                android:textAllCaps="false"
                android:textColor="#ff0000" />
    
            <Button
                android:id="@+id/btn2"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="I am btn2."
                android:textAllCaps="false"
                android:textColor="#ff0000" />
    
            <Button
                android:id="@+id/btn3"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="I am btn3."
                android:textAllCaps="false"
                android:textColor="#ff0000" />
    
            <Button
                android:id="@+id/btn4"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="I am button4."
                android:textAllCaps="false"
                android:textColor="#ff0000" />
    
            <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:orientation="horizontal"
                android:gravity="center">
                <!-- 嵌套布局 -->
    
                    <Button
                        android:id="@+id/btn5"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="I am btn5"
                        android:textAllCaps="false"
                        android:textColor="#0000ff" />
    
                    <Button
                        android:id="@+id/btn6"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="I am btn6"
                        android:textAllCaps="false"
                        android:textColor="#0000ff" />
    
                    <Button
                        android:id="@+id/btn7"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="I am btn7"
                        android:textAllCaps="false"
                        android:textColor="#0000ff" />
    
                    <Button
                        android:id="@+id/btn8"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="I am btn8"
                        android:textAllCaps="false"
                        android:textColor="#0000ff" />
    
            </LinearLayout>
    
    </LinearLayout>
    

    image.png

    相对布局RelativeLayout

    相对布局可以通过相对定位的方式让控件出现在布局的任意位置上。

    <?xml version="1.0" encoding="utf-8" ?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
    
        <Button
            android:id="@+id/btn1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentLeft="true"
            android:layout_alignParentTop="true"
            android:text="button 1" />
    
        <Button
            android:id="@+id/btn2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentRight="true"
            android:layout_alignParentTop="true"
            android:text="button 2" />
    
        <Button
            android:id="@+id/btn3"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:text="button 3" />
    
        <Button
            android:id="@+id/btn4"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_alignParentLeft="true"
            android:text="button 4" />
        <!-- 其内控件可相对于其他控件或父容器排列 -->
    </RelativeLayout>
    

    image.png

    帧布局FrameLayout

    帧布局内所有控件默认都会摆放在布局的左上角。

    <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <TextView
            android:id="@+id/text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="This is TextView" />
    
        <ImageView
            android:id="@+id/img"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@mipmap/ic_launcher" />
    
    </FrameLayout>
    

    image.png

    百分比布局PercentFrameLayout和PercentRelativeLayout

      前面提到的LinearLayout,RelativeLayout和FrameLayout都是从Android 1.0版本就开始支持了,一直沿用到现在,可以说是满足了绝大多数场景的界面设计需求。不过细心的你还是可以发现,除了LinearLayout支持使用layout_weight属性来实现按比例指定控件大小的功能外,其余两种布局都不支持。比如说如果要使用RelativeLayout来实现让两个按钮平分布局宽度的效果是很困难的。

      为此,Android引入了一种全新的布局方式来解决该问题——百分比问题。在这种布局中,我们可以不再使用wrap_content,match_parent等方式来指定控件的大小,而是允许直接指定控件在布局中所占的百分比,这样一来,就可以轻松实现平分布局甚至是任意比例分割布局的效果。

      由于LinearLayout本身已经支持按比例指定控件大小了,因此百分比布局只为FrameLayout和RelativeLayout进行了功能扩展,提供了PercentFrameLayout和PercentRelativeLayout这两个全新的布局。

      android团队将百分比布局定义在了support库中,只需在build.gradle中添加百分比布局库的依赖,就能保证百分比布局在所有系统版本上都可以使用。在build.gradle(Module:app)中添加一行:

    dependencies {
    	implementation 'com.android.support:percent:28.0.0'
    }
    

    然后就可以使用百分比布局了,因为百分比布局不是直接内置在SDK中的,所以要写出完整的包名。

    <android.support.percent.PercentFrameLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <Button
            android:id="@+id/button1"
            android:text="Button 1"
            android:layout_gravity="left|top"
            app:layout_widthPercent="50%"
            app:layout_heightPercent="50%" />
    
        <Button
            android:id="@+id/button2"
            android:text="Button 2"
            android:layout_gravity="right|top"
            app:layout_widthPercent="50%"
            app:layout_heightPercent="50%" />
    
        <Button
            android:id="@+id/button31"
            android:text="Button 3"
            android:layout_gravity="left|bottom"
            app:layout_widthPercent="50%"
            app:layout_heightPercent="50%" />
    
        <Button
            android:id="@+id/button4"
            android:text="Button 4"
            android:layout_gravity="right|bottom"
            app:layout_widthPercent="50%"
            app:layout_heightPercent="50%" />
    
    </android.support.percent.PercentFrameLayout>
    

    image.png

    表格布局TableLayout

    <TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:stretchColumns="1" >
        <!-- 如果某一行控件的总宽不能铺满父容器,则拉伸第一列以适应屏幕宽度 -->
    
        <TableRow>
            <!-- TableRow内无法为控件指定宽度 -->
            <TextView
                android:layout_height="wrap_content"
                android:text="Account:" />
            <EditText
                android:id="@+id/account"
                android:layout_height="wrap_content"
                android:hint="Input your account" />
    
        </TableRow>
    
        <TableRow>
            <TextView
                android:layout_height="wrap_content"
                android:text="Password:" />
            <EditText
                android:id="@+id/password"
                android:layout_height="wrap_content"
                android:inputType="numberPassword" />
        </TableRow>
    
        <TableRow>
            <CheckBox
                android:id="@+id/remember_pass"
                android:layout_height="wrap_content" />
            <TextView
                android:layout_height="wrap_content"
                android:text="remember_password" />
        </TableRow>
    
        <TableRow>
            <Button
                android:id="@+id/login"
                android:layout_height="wrap_content"
                android:layout_span="2"
                android:text="login" />
        </TableRow>
    
    </TableLayout>
    

    image

    创建自定义控件

      所有控件都是直接或间接继承View的,所用的所有部件都直接或间接继承ViewGroup。View是Android中最基本的一种UI组件,它可以在屏幕上绘制一块矩形区域,并能响应这块区域的各种事件。

      因此,我们平常所用的各种组件实际上就是在View的基础上有添加了各自特有的功能。而ViewGroup可以看做是一种特殊的View,它可以包含很多子View和子ViewGroup,是一个用于放置控件和布局的容器。

    image.png

    引入布局

    当系统自带的控件不能满足我们的需求时,可以根据上面的继承关系来实现我们的自定义控件。

    下面我们来自定义一个标题栏。

    首先新建一个布局title.xml,这是用于actionBar的布局文件。

    <?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="wrap_content"
        android:background="@drawable/title_bg">
    
        <Button
            android:id="@+id/title_back"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_margin="5dp"
            android:background="@drawable/back_bg"
            android:text="Back"
            android:textColor="#fff" />
    
        <TextView
            android:id="@+id/title_text"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_weight="1"
            android:gravity="center"
            android:text="Title Text"
            android:textColor="#fff"
            android:textSize="24sp" />
    
        <Button
            android:id="@+id/title_edit"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_margin="5dp"
            android:background="@drawable/edit_bg"
            android:text="Edit"
            android:textColor="#fff" />
    
    </LinearLayout>
    

    然后将这个布局引入到主布局文件activity_main.xml中(使用include标签):

    <?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">
    
        <include layout="@layout/title" />
    
    </LinearLayout>
    

    可以在app中的每一个布局中引入title.xml,这样每个页面就都拥有我们自定义的标题栏了。

    另外,由于android系统自带有标题栏,所以要将自带的标题栏隐藏掉:

    public class MainActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            ActionBar titleBar = getSupportActionBar();
            if(titleBar != null)
                titleBar.hide();
        }
    }
    

    image.png

    back_bg.png edit_bg.png title_bg.png

    自定义控件

      上面的例子中,我们通过引入布局文件,创建了自定义的标题栏。但还是有一个问题,我们要在每个活动中为这些控件单独编写一次事件注册的代码。比如说标题栏返回按钮的Back功能,但事实上,这些功能在每个活动都是一样的实现逻辑。为了避免在每个活动中都注册一遍按钮的点击事件,我们最好使用自定义控件。

    新建java类TitleLayout,继承自LinearLayout类:

    image.png

    public class TitleLayout extends LinearLayout {
    
        public TitleLayout(Context context, AttributeSet attrs) {
            // 将xml中设置的一堆属性全部传给父类LinearLayout的构造函数
            super(context, attrs);
            // 使用from()加载布局title.xml,父布局是TitleLayout本身
            LayoutInflater.from(context).inflate(R.layout.title, this);
    
            Button titleBack = (Button) findViewById(R.id.title_back);
            Button titleEdit = (Button) findViewById(R.id.title_edit);
    
            // 为titleBack控件即返回按钮添加事件监听
            titleBack.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View view) {
                    ((Activity) getContext()).finish();     // 销毁当前上下文活动,退出应用
                }
            });
    
            titleEdit.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    Toast.makeText(getContext(), "You clicked Edit button", Toast.LENGTH_SHORT).show();
                }
            });
        }
    }
    

      然后在主布局activity_main.xml中使用TitleLayout布局,由于TitleLayout是我们自定义的,没有内置在SDK中,所以我们在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">
    
        <com.example.uiwidgetdemo.TitleLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
    
        </com.example.uiwidgetdemo.TitleLayout>
    
    </LinearLayout>
    

    image

  • 相关阅读:
    【MYSQL】某些有用的sql【持续更新中】
    【LDAP】什么时候需要使用LDAP?
    【LDAP】 objectClass 分类
    MySQL的锁机制
    spring的事务传播级别及场景
    @NotEmpty,@NotNull和@NotBlank的区别
    mysql的字段为bit时,插入数据报Data too long
    activeMQ启动报--找不到或无法加载主类
    【ListViewJson】【com.demo.app】【AppException】源码分析及其在工程中作用
    【ListViewJson】【com.demo.app】【AppConfig】源码分析及其在工程中作用
  • 原文地址:https://www.cnblogs.com/seeyoumiter/p/12488714.html
Copyright © 2020-2023  润新知