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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM