Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 第3章 UI開發

第3章 UI開發

編輯:關於Android編程

一、常見控件的使用方法

使用android:layout_width指定了控件的寬度,

使用android:layout_height指定了控件的高度

Android中所有的控件都具有這兩個屬性,可選值有三種match_parent、fill_parent和wrap_content,其中match_parent和fill_parent的意義相同,現在官方更加推薦使用match_parent。match_parent表示讓當前控件的大小和父布局的大小一樣,也就是由父布局來決定當前控件的大小。wrap_content表示讓當前控件的大小能夠剛好包含住裡面的內容,也就是由控件內容決定當前控件的大小。

Android控件的可見屬性。所有的Android控件都具有這個屬性,可以通過android:visibility進行指定,可選值有三種,visible、invisible和gone。visible表示控件是可見的,這個值是默認值,不指定android:visibility時,控件都是可見的。invisible表示控件不可見,但是它仍然占據著原來的位置和大小,可以理解成控件變成透明狀態了。gone則表示控件不僅不可見,而且不再占用任何屏幕空間。我們還可以通過代碼來設置控件的可見性,使用的是setVisibility()方法,可以傳入View.VISIBLE、View.INVISIBLE和View.GONE三種值。

android:background用於為布局或控件指定一個背景,可以使用顏色或圖片來進行填充。

1、TextView

主要用於在界面上顯示一段文本信息。
android:gravity=”center”//指定文字的對齊方式

使用android:gravity來指定文字的對齊方式,可選值有top、bottom、left、right、center等,可以用“|”來同時指定多個值,這裡我們指定的”center”,效果等同於”center_vertical|center_horizontal”,表示文字在垂直和水平方向都居中對齊。

android:textSize=”24sp”//指定文字的大小

android:textColor=”#00ff00”//指定文字的顏色

當然TextView中還有很多其他的屬性,這裡我就不再一一介紹了,需要用到的時候去查閱文檔就可以了。

2、Button

Button是程序用於和用戶進行交互的一個重要控件。

然後我們可以在MainActivity中為Button的點擊事件注冊一個監聽器,如下所示:

public class MainActivity extends Activity {

    private Button button;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        button = (Button) findViewById(R.id.button);
        button.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                // 在此處添加邏輯
            }
        });
    }


}

這樣每當點擊按鈕時,就會執行監聽器中的onClick()方法,我們只需要在這個方法中加入待處理的邏輯就行了。如果你不喜歡使用匿名類的方式來注冊監聽器,也可以使用實現接口的方式來進行注冊,代碼如下所示:

public class MainActivity extends Activity implements OnClickListener {

    private Button button;

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

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
        case R.id.button:
            // 在此處添加邏輯
            break;
        default:
            break;
        }
    }


}

這兩種寫法都可以實現對按鈕點擊事件的監聽,至於使用哪一種就全憑你喜好了。

3、EditText

EditText是程序用於和用戶進行交互的另一個重要控件,它允許用戶在控件裡輸入和編輯內容,並可以在程序中對這些內容進行處理。EditText的應用場景應該算是非常普遍了,發短信、發微博、聊QQ等等。

可以看到,EditText中顯示了一段提示性文本,然後當我們輸入任何內容時,這段文本就會自動消失。不過隨著輸入的內容不斷增多,EditText會被不斷地拉長。這時由於EditText的高度指定的是wrap_content,因此它總能包含住裡面的內容,但是當輸入的內容過多時,界面就會變得非常難看。我們可以使用android:maxLines屬性來解決這個問題。

android:maxLines=”2”//指定了EditText的最大行數為兩行,這樣當輸入的內容超過兩行時,文本就會向上滾動,而EditText則不會再繼續拉伸

4、ImageView

ImageView是用於在界面上展示圖片的一個控件,通過它可以讓我們的程序界面變得更加豐富多彩。

android:src=”@drawable/ic_launcher”//給ImageView指定了一張圖片

5、ProgressBar

ProgressBar用於在界面上顯示一個進度條,表示我們的程序正在加載一些數據。

style=”?android:attr/progressBarStyleHorizontal”//執行ProgressBar的樣式為水平進度條

android:max=”100”//設置水平進度條的最大值

ProgressBar還有幾種其他的樣式,你可以自己去嘗試一下。

6、AlertDialog

AlertDialog可以在當前的界面彈出一個對話框,這個對話框是置頂於所有界面元素之上的,能夠屏蔽掉其他控件的交互能力,因此一般AlertDialog都是用於提示一些非常重要的內容或者警告信息。比如為了防止用戶誤刪重要內容,在刪除前彈出一個確認對話框。

AlertDialog.Builder dialog = new AlertDialog.Builder (MainActivity.this);//通過AlertDialog.Builder創建出一個AlertDialog的實例
dialog.setTitle("This is 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();//顯示對話框

7、ProgressDialog

ProgressDialog和AlertDialog有點類似,都可以在界面上彈出一個對話框,都能夠屏蔽掉其他控件的交互能力。不同的是,ProgressDialog會在對話框中顯示一個進度條,一般是用於表示當前操作比較耗時,讓用戶耐心地等待。

ProgressDialog progressDialog = new ProgressDialog (MainActivity.this);//新建一個ProgressDialog對象
progressDialog.setTitle("This is ProgressDialog");//設置對話框的標題
progressDialog.setMessage("Loading...");//設置對話框的內容
progressDialog.setCancelable(true);//設置對話框是否可以取消
   progressDialog.show();//顯示對話框

二、詳解四種基本布局

布局是一種可用於放置很多控件的容器,它可以按照一定的規律調整內部控件的位置,從而編寫出精美的界面。當然,布局的內部除了放置控件外,也可以放置布局,通過多層布局的嵌套,我們就能夠完成一些比較復雜的界面實現,示意圖3.15很好地展示了它們之間的關系。
布局與控件的關系

1、LinearLayout

LinearLayout又稱作線性布局,是一種非常常用的布局。正如它名字所描述的一樣,這個布局會將它所包含的控件在線性方向上依次排列。

android:orientation屬性:指定布局的排列方向,有兩個值,分別是horizontal(水平)和vertical(垂直)

這裡需要注意,如果LinearLayout的排列方向是horizontal,內部的控件就絕對不能將寬度指定為match_parent,因為這樣的話單獨一個控件就會將整個水平方向占滿,其他的控件就沒有可放置的位置了。同樣的道理,如果LinearLayout的排列方向是vertical,內部的控件就不能將高度指定為match_parent。

首先來看android:layout_gravity屬性,它和我們上一節中學到的android:gravity屬性看起來有些相似,這兩個屬性有什麼區別呢?其實從名字上就可以看出,android:gravity是用於指定文字在控件中的對齊方式,而android:layout_gravity是用於指定控件在布局中的對齊方式。android:layout_gravity的可選值和android:gravity差不多,但是需要注意,當LinearLayout的排列方向是horizontal時,只有垂直方向上的對齊方式才會生效,因為此時水平方向上的長度是不固定的,每添加一個控件,水平方向上的長度都會改變,因而無法指定該方向上的對齊方式。同樣的道理,當LinearLayout的排列方向是vertical時,只有水平方向上的對齊方式才會生效。

接下來我們學習下LinearLayout中的另一個重要屬性,android:layout_weight。這個屬性允許我們使用比例的方式來指定控件的大小,它在手機屏幕的適配性方面可以起到非常重要的作用。
例子:



    

你會發現,這裡竟然將EditText和Button的寬度都指定成了0,這樣文本編輯框和按鈕還能顯示出來嗎?不用擔心,由於我們使用了android:layout_weight屬性,此時控件的寬度就不應該再由android:layout_width來決定,這裡指定成0是一種比較規范的寫法。

2、RelativeLayout

RelativeLayout又稱作相對布局,也是一種非常常用的布局。和LinearLayout的排列規則不同,RelativeLayout顯得更加隨意一些,它可以通過相對定位的方式讓控件出現在布局的任何位置。也正因為如此,RelativeLayout中的屬性非常多,不過這些屬性都是有規律可循的,其實並不難理解和記憶。

android:layout_alignParentLeft——讓一個控件 和父組件的左邊對齊
android:layout_alignParentTop——讓一個控件和父組件的頂部對齊
android:layout_alignParentRight——讓一個控件 和父組件的右邊對齊
android:layout_alignParentBottom——讓一個控件 和父組件的底部對齊
android:layout_centerInParent——讓一個控件 居中顯示
這幾個屬性都是相對於父布局進行定位的。
android:layout_above——讓一個控件位於另一個控件的上方
android:layout_below——讓一個控件位於另一個控件的下方
android:layout_toLeftOf——讓一個控件位於另一個控件的左側
android:layout_toRightOf——讓一個控件位於另一個控件的右側
這幾個屬性都是控件相對於空間進行定位的。
android:layout_alignLeft——讓一個控件的左邊緣和另一個控件的左邊緣對齊
android:layout_alignRight——讓一個控件的右邊緣和另一個控件的右邊緣對齊
android:layout_alignTop——讓一個控件的上邊緣和另一個控件的上邊緣對齊
android:layout_alignBottom——讓一個控件的下邊緣和另一個控件的下邊緣對齊
這幾個屬性都是相對於控件進行定位的。

3、FrameLayout

rameLayout相比於前面兩種布局就簡單太多了,因此它的應用場景也少了很多。這種布局沒有任何的定位方式,所有的控件都會擺放在布局的左上角。

4、TableLayout

TableLayout允許我們使用表格的方式來排列控件,這種布局也不是很常用,你只需要了解一下它的基本用法就可以了。既然是表格,那就一定會有行和列,在設計表格時我們盡量應該讓每一行都擁有相同的列數,這樣的表格也是最簡單的。不過有時候事情並非總會順從我們的心意,當表格的某行一定要有不相等的列數時,就需要通過合並單元格的方式來應對。

在TableLayout中每加入一個TableRow就表示在表格中添加了一行,然後在TableRow中每加入一個控件,就表示在該行中加入了一列,TableRow中的控件是不能指定寬度的。不過使用android:stretchColumns屬性就可以很好地解決這個問題,它允許將TableLayout中的某一列進行拉伸,以達到自動適應屏幕寬度的作用。

android:stretchColumns=”1”//這裡將android:stretchColumns的值指定為1,表示如果表格不能完全占滿屏幕寬度,就將第二列進行拉伸。沒錯!指定成1就是拉伸第二列,指定成0就是拉伸第一列,不要以為這裡我寫錯了哦。

Android中其實還有一個AbsoluteLayout,不過這個布局官方已經不推薦使用了,因此我們直接將它忽略就好。

三、系統控件不夠用?創建自定義控件

控件和布局的繼承結構
控件和布局的繼承結構vcq9wLS94r72oaM8L3A+DQo8cD7A/dfTo7o8YnIgLz4NCtDCvahUaXRsZUxheW91dLzMs9DX1ExpbmVhckxheW91dKOsyMPL/LPJzqrO0sPH19S2qNLltcSx6sziwLi/2Lz+o6y0+sLryOfPwsv5yr6jujwvcD4NCjxwcmUgY2xhc3M9"brush:java;"> public class TitleLayout extends LinearLayout { public TitleLayout(Context context, AttributeSet attrs) {//重寫了LinearLayout中的帶有兩個參數的構造函數,在布局中引入TitleLayout控件就會調用這個構造函數 super(context, attrs); LayoutInflater.from(context).inflate(R.layout.title, this); } }

在構造函數中需要對標題欄布局進行動態加載,這就要借助LayoutInflater來實現了。通過LayoutInflater的from()方法可以構建出一個LayoutInflater對象,然後調用inflate()方法就可以動態加載一個布局文件,inflate()方法接收兩個參數,第一個參數是要加載的布局文件的id,這裡我們傳入R.layout.title,第二個參數是給加載好的布局再添加一個父布局,這裡我們想要指定為TitleLayout,於是直接傳入this。

現在自定義控件已經創建好了,然後我們需要在布局文件中添加這個自定義控件,修改activity_main.xml中的代碼,如下所示:



    

添加自定義控件和添加普通控件的方式基本是一樣的,只不過在添加自定義控件的時候我們需要指明控件的完整類名,包名在這裡是不可以省略的。

重新運行程序,你會發現此時效果和使用引入布局方式的效果是一樣的。

然後我們來嘗試為標題欄中的按鈕注冊點擊事件,修改TitleLayout中的代碼,如下所示:

public class TitleLayout extends LinearLayout {

    public TitleLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        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.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                ((Activity) getContext()).finish();
            }
        });
        titleEdit.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(getContext(), "You clicked Edit button",

                         Toast.LENGTH_SHORT).show();
            }
        });
    }

}

首先還是通過findViewById()方法得到按鈕的實例,然後分別調用setOnClickListener()方法給兩個按鈕注冊了點擊事件,當點擊返回按鈕時銷毀掉當前的活動,當點擊編輯按鈕時彈出一段文本。這樣的話,每當我們在一個布局中引入TitleLayout,返回按鈕和編輯按鈕的點擊事件就已經自動實現好了,也是省去了很多編寫重復代碼的工作。

四、最常用和最難用的控件——ListView

ListView絕對可以稱得上是Android中最常用的控件之一,幾乎所有的應用程序都會用到它。由於手機屏幕空間都比較有限,能夠一次性在屏幕上顯示的內容並不多,當我們的程序中有大量的數據需要展示的時候,就可以借助ListView來實現。ListView允許用戶通過手指上下滑動的方式將屏幕外的數據滾動到屏幕內,同時屏幕上原有的數據則會滾動出屏幕。相信你其實每天都在使用這個控件,比如查看手機聯系人列表,翻閱微博的最新消息等等。

1、ListView的簡單用法

public class MainActivity extends Activity {

    private String[] data = { "Apple", "Banana", "Orange", "Watermelon",
            "Pear", "Grape", "Pineapple", "Strawberry", "Cherry", "Mango" };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ArrayAdapter adapter = new ArrayAdapter(
                MainActivity.this, android.R.layout.simple_list_item_1, data);
        ListView listView = (ListView) findViewById(R.id.list_view);
        listView.setAdapter(adapter);
    }

}

既然ListView是用於展示大量數據的,那我們就應該先將數據提供好。這些數據可以是從網上下載的,也可以是從數據庫中讀取的,應該視具體的應用程序場景來決定。
不過,數組中的數據是無法直接傳遞給ListView的,我們還需要借助適配器來完成。Android中提供了很多適配器的實現類,其中我認為最好用的就是ArrayAdapter。它可以通過泛型來指定要適配的數據類型,然後在構造函數中把要適配的數據傳入即可。ArrayAdapter有多個構造函數的重載,你應該根據實際情況選擇最合適的一種。這裡由於我們提供的數據都是字符串,因此將ArrayAdapter的泛型指定為String,然後在ArrayAdapter的構造函數中依次傳入當前上下文、ListView子項布局的id,以及要適配的數據。注意我們使用了android.R.layout.simple_list_item_1作為ListView子項布局的id,這是一個Android內置的布局文件,裡面只有一個TextView,可用於簡單地顯示一段文本。這樣適配器對象就構建好了。
最後,還需要調用ListView的setAdapter()方法,將構建好的適配器對象傳遞進去,這樣ListView和數據之間的關聯就建立完成了。
可以通過滾動的方式來查看屏幕外的數據。

2、定制ListView的界面

——例子見原書3.5.2——

3、提升ListView的運行效率

之所以說ListView這個控件很難用,就是因為它有很多的細節可以優化,其中運行效率就是很重要的一點。目前我們ListView的運行效率是很低的,因為在FruitAdapter的getView()方法中每次都將布局重新加載了一遍,當ListView快速滾動的時候這就會成為性能的瓶頸。
仔細觀察,getView()方法中還有一個convertView參數,這個參數用於將之前加載好的布局進行緩存,以便之後可以進行重用。

View view;
        if (convertView == null) {
            view = LayoutInflater.from(getContext()).inflate(resourceId, null);
        } else {
            view = convertView;
        }

可以看到,現在我們在getView()方法中進行了判斷,如果convertView為空,則使用LayoutInflater去加載布局,如果不為空則直接對convertView進行重用。這樣就大大提高了ListView的運行效率,在快速滾動的時候也可以表現出更好的性能。
不過,目前我們的這份代碼還是可以繼續優化的,雖然現在已經不會再重復去加載布局,但是每次在getView()方法中還是會調用View的findViewById()方法來獲取一次控件的實例。我們可以借助一個ViewHolder來對這部分性能進行優化,修改FruitAdapter中的代碼,如下所示:

public class FruitAdapter extends ArrayAdapter {
    ……
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        Fruit fruit = getItem(position);
        View view;
        ViewHolder viewHolder;
        if (convertView == null) {
            view = LayoutInflater.from(getContext()).inflate(resourceId, null);
            viewHolder = new ViewHolder();
            viewHolder.fruitImage = (ImageView) view.findViewById (R.id.fruit_image);
            viewHolder.fruitName = (TextView) view.findViewById (R.id.fruit_name);
            view.setTag(viewHolder); // 將ViewHolder存儲在View中
        } else {
            view = convertView;
            viewHolder = (ViewHolder) view.getTag(); // 重新獲取ViewHolder
        }
        viewHolder.fruitImage.setImageResource(fruit.getImageId());
        viewHolder.fruitName.setText(fruit.getName());
        return view;
    }

    class ViewHolder {
        ImageView fruitImage;
        TextView fruitName;
    }
}

我們新增了一個內部類ViewHolder,用於對控件的實例進行緩存。當 convertView為空的時候,創建一個ViewHolder對象,並將控件的實例都存放在ViewHolder裡,然後調用View的setTag()方法,將ViewHolder對象存儲在View中。當 convertView不為空的時候則調用View的getTag()方法,把ViewHolder重新取出。這樣所有控件的實例都緩存在了ViewHolder裡,就沒有必要每次都通過 findViewById()方法來獲取控件實例了。
通過這兩步的優化之後,我們ListView的運行效率就已經非常不錯了。

4、ListView的點擊事件

public class MainActivity extends Activity {

    private List fruitList = new ArrayList();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initFruits();
        FruitAdapter adapter = new FruitAdapter(MainActivity.this, R.layout.fruit_item, fruitList);
        ListView listView = (ListView) findViewById(R.id.list_view);
        listView.setAdapter(adapter);
        listView.setOnItemClickListener(new OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView parent, View view,
                    int position, long id) {
                Fruit fruit = fruitList.get(position);
                Toast.makeText(MainActivity.this, fruit.getName(),
                        Toast.LENGTH_SHORT).show();
            }
        });
    }
     ……
}

可以看到,我們使用了setOnItemClickListener()方法來為ListView注冊了一個監聽器,當用戶點擊了ListView中的任何一個子項時就會回調onItemClick()方法,在這個方法中可以通過position參數判斷出用戶點擊的是哪一個子項,然後獲取到相應的水果,並通過Toast將水果的名字顯示出來。

五、單位和尺寸

在布局文件中指定寬高的固定大小有以下常用單位可供選擇:px、pt、dp和sp。

px和pt的窘境
px是像素的意思,即屏幕中可以顯示的最小元素單元,我們應用裡任何可見的東西都是由一個個像素點組成的。單獨一個像素點非常的微小,肉眼是無法看見的,可是當許許多多的像素點聚集到一起時,就可以拼接成五彩缤紛的圖案。

pt是磅數的意思,1磅等於1/72英寸,一般pt都會作為字體的單位來使用。

過去在PC上使用px和pt的時候可以說是非常得心應手,能把程序打扮得漂漂亮亮。可是現在到了手機上,這兩個單位就顯得有些力不從心了,因為手機的分辨率各不相同,一個200px寬的按鈕在低分辨率的手機上可能將近占據滿屏,而到了高分辨率的手機上可能只占據屏幕的一半。

dp和sp來幫忙
谷歌當然也意識到了這個令人頭疼了問題,於是為Android引入了一套新的單位dp和sp。

dp是密度無關像素的意思,也被稱作dip,和px相比,它在不同密度的屏幕中的顯示比例將保持一致。

sp是可伸縮像素的意思,它采用了和dp同樣的設計理念,解決了文字大小的適配問題。

什麼叫密度?Android中的密度就是屏幕每英寸所包含的像素數,通常以dpi為單位。比如一個手機屏幕的寬是2英寸長是3英寸,如果它的分辨率是320*480像素,那這個屏幕的密度就是160dpi,如果它的分辨率是640*960,那這個屏幕的密度就是320dpi,因此密度值越高的屏幕顯示的效果就越精細。我們可以通過代碼來得知當前屏幕的密度值是多少,修改MainActivity中的代碼,如下所示:

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        float xdpi = getResources().getDisplayMetrics().xdpi;
        float ydpi = getResources().getDisplayMetrics().ydpi;
//動態獲取到了當前屏幕的密度值
        Log.d("MainActivity", "xdpi is " + xdpi);
        Log.d("MainActivity", "ydpi is " + ydpi);
    }

}

根據Android的規定,在160dpi的屏幕上,1dp等於1px,而在320dpi的屏幕上,1dp就等於2px。因此,使用dp來指定控件的寬和高,就可以保證控件在不同密度的屏幕中的顯示比例保持一致。

sp的原理和dp是一樣的,它主要是用於指定文字的大小,這裡就不再進行介紹了。
總結一下,在編寫Android程序的時候,盡量將控件或布局的大小指定成match_parent或wrap_content,如果必須要指定一個固定值,則使用dp來作為單位,指定文字大小的時候使用sp作為單位。

六、制作Nine-Patch圖片

.9圖片是一種被特殊處理過的png圖片,能夠指定哪些區域可以被拉伸而哪些區域不可以。

在Android sdk目錄下有一個tools文件夾,在這個文件夾中找到draw9patch.bat文件,我們就是使用它來制作Nine-Patch圖片的。

我們可以在圖片的四個邊框繪制一個個的小黑點,在上邊框和左邊框繪制的部分就表示當圖片需要拉伸時就拉伸黑點標記的區域,在下邊框和右邊框繪制的部分則表示內容會被放置的區域。

左側和頂部是控制拉伸區域,右側和底部是可能控制顯示的內容區域。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved