1. 程式人生 > >Android原生控制元件---ActionBar詳解

Android原生控制元件---ActionBar詳解

昨天去面試了,第一次出去面試,被問到好幾個我不好回答上來的題,例如OOM的處理啊,AndroidStudio的gradle配置,actionBar的使用,ViewPager 巢狀ViewPager等等.每次面試都是一個自己查漏補缺的好機會,現在來學習一下自己不太熟的東西,今天就記錄一下actionBar的學習.

1.ActionBar是什麼?

  ActionBar是android3.0(API 11)以後新增的元件,主要用於標示應用程式以及使用者所處的位置並提供相關操作以及全域性的導航功能,所以只要targetSdkVersion的值不低於11,建立的Activity中預設都會帶有ActionBar

  它提供了幾個關鍵的功能:

  1.使得重要的動作明顯且可以通過可預測的方式獲得(比如New和Search)。

  2.提供了app中一致的導航和View轉換。

  3.通過提供action流,減少了雜亂,尤其是對很少使用的動作來說。

  4.為你的app內容提供了足夠多的空間

2.ActionBar的結構

     真正使用之前,我們應該首先了解一下ActionBar的結構

        根據官方文件,我們看到整個ActionBar可以分為4個部分,具體如下圖:

      Android之官方導航欄ActionBar

1、  App icon:主要用於展示App的Logo,如果當前介面不是一級介面,還可以展示返回導航。

2、  View Control:用於切換不同的檢視或者展示非互動資訊如app標題等。

3、  Action Buttons:用於展示app中最重要的操作按鈕,如果過多actionbar中放不下則會轉移到Action overflow中,長按會展示操作名稱。根據文件說明,Action Buttons的總寬度不會超過ActionBar的50%。

4、  Action overflow:用於存放展示相對較少使用的操作按鈕

3.ActionBar的簡單使用

      瞭解了ActionBar的基本結構後,下面我們一起看看如何使用ActionBar,首先我們來實現一下上圖的所呈現的actionBar結構

        1.res/menu/standard.xml檔案,這裡要注意一下showAsAction屬性

          showAsAction屬性用來定義每個Action是如何顯示的,有5個設定值:

             (1)always :表示永遠顯示在ActionBar中,,如果螢幕空間不夠則無法顯示
             (2)ifRoom :表示螢幕空間夠的情況下顯示在ActionBar中,不夠的話就顯示在overflow中

             (3)never :則表示永遠顯示在overflow中。

             (4)withText :表示actionBar 要顯示文字標題,但是如果圖示有效並且受到actionBar空間的限制,文字標題有可能顯示不全.

             (5)collapseActionView :  聲明瞭這個操作視窗應該被摺疊到一個按鈕中,當用戶選擇這個按鈕時,這個操作視窗展開.否則這個操作視窗在預設的情況下是可見的,

                 並且即便在用於不適用的時候,也要佔據操作欄的有效空間

<span style="font-size:24px;"><span style="font-size:18px;color:#333333;">     <?xml version="1.0" encoding="utf-8"?>
     <menu xmlns:android="http://schemas.android.com/apk/res/android" >

    <item
        android:icon="@android:drawable/ic_menu_delete"
        android:showAsAction="ifRoom"
        android:title="標題1"
        android:titleCondensed="副標題1"/>
    <item
        android:icon="@android:drawable/ic_menu_camera"
        android:showAsAction="ifRoom"
        android:title="標題2"
        android:titleCondensed="副標題2"/>
    <item
        android:icon="@android:drawable/ic_menu_compass"
        android:showAsAction="ifRoom"
        android:title="標題3"
        android:titleCondensed="副標題3"/>
    <item
        android:icon="@android:drawable/ic_menu_day"
        android:showAsAction="ifRoom"
        android:title="標題4"
        android:titleCondensed="副標題4"/>

   </menu></span></span>

   2.activity中 onCreateOptionsMenu方法中去呼叫我們定義的選單樣式,這裡說 一下,因為我做測試的手機系統是android4.4,所以選單中的圖示是預設不顯示的,所以我加上了一個反射,去強制呼叫menu的方法,讓選單圖示顯示

<font size="5"><span style="font-size:18px;color:#333333;"><span style="font-size:24px;"><span style="font-size:18px;color:#333333;">package com.example.dr_actionbar;

import java.lang.reflect.Method;

import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuInflater;

public class StandardActionBar extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {

		super.onCreate(savedInstanceState);
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// 使選單圖片可見
		setIconEnable(menu, true);
		// 載入選單檔案
		MenuInflater inflater = getMenuInflater();
		inflater.inflate(R.menu.standard, menu);

		return true;
	}

	/**
	 * 使選單可見 isEnable為true時,選單新增圖示有效,isEnable為false時無效。4.0系統預設無效
	 * 
	 * @param menu
	 * @param isEnable
	 */
	private void setIconEnable(Menu menu, boolean isEnable) {

		try {
			// 得到類
			Class<?> clazz = Class
					.forName("com.android.internal.view.menu.MenuBuilder");
			// 得到方法
			Method method = clazz.getDeclaredMethod("setOptionalIconsVisible",
					boolean.class);
			// 修改訪問修飾符,讓方法可以被訪問.
			method.setAccessible(true);
			// MenuBuilder實現Menu介面,建立選單時,傳進來的menu其實就是MenuBuilder物件(java的多型特徵)
			method.invoke(menu, isEnable);

		} catch (Exception exception) {
			exception.printStackTrace();

		}

	}
}</span></span></span></font>

4.ActionBar的navigation Up功能

     ActionBar 支援 Navigation Up的功能,Navigation UP 是指返回 邏輯上的上一頁,它和Back鍵的返回的含義是不同的。

      (1)Back鍵是根據使用者瀏覽頁面的順序進行返回的,返回的是上一個瀏覽的頁面的,也就是棧中的順序。

      (2)Navigation Up 所說的邏輯上的上一頁,是根據軟體的頁面層次來決定的,是邏輯上的上一頁。比如頁面1顯示 列表,頁面2 顯示 列表項的詳情,頁面3 顯示的是列 表項詳情裡的某一項的具體詳情。我們可以利用Navigation UP 讓頁面3 返回頁面1,因為頁面3 返回 頁面 1 ,這在頁面的邏輯功能上是需要的。為什麼這麼說呢,如果我們的頁面的層次太多,使用者要返回第一頁,只能通過Back鍵從第N 頁逐頁返回。從應用的邏輯功能的角度來講,我們需要 讓頁面從第N頁直接返回第一頁,這樣可以提高應用的使用者體驗。實際應用中,Navigation UP 返回的頁面有可能是上一個瀏覽的頁面,也有可能不是,我們需要根據軟體的邏輯功能來進行設計。

   1.activity:onCreate中設定getActionBar().setDisplayHomeAsUpEnable(true);

<span style="font-size:24px;"><span style="font-size:18px;color:#333333;">package com.example.dr_actionbar;

import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;

public class NavigationUpActionBar extends Activity{
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		
		super.onCreate(savedInstanceState);
		getActionBar().setDisplayHomeAsUpEnabled(true); 
	}
	
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		return super.onCreateOptionsMenu(menu);
	}

}</span></span>

   2.AndroidManifest.xml中設定parentActivityName屬性

<span style="font-size:24px;"><span style="font-size:18px;color:#333333;">         <activity android:name="com.example.dr_actionbar.NavigationUpActionBar"
             android:parentActivityName="com.example.dr_actionbar.NavigationUpParentActivity">
         </activity></span></span>

  這個我測試的時候用著還是有點不順手的,等下再研究研究

5.ActionBar的tab導航--fragment

  使用actionBar的tab做導航條,可以做類似RadioGroup+Fragment的標籤效果,而且橫屏和豎屏的顯示效果不同.如下圖所示.這就是actionBar的一個特點

 系統會調整操作欄選項標籤來適應不同尺寸的螢幕的需要---在螢幕足夠寬的時候,導航選項標籤會被放到主操作欄中;當螢幕太窄的時候,選項標籤會被放到一個分離的橫條中.

    橫屏狀態下:

            豎屏狀態下:
      
   
   我們實現的邏輯是
   1.新建一個activity,同時擁有actionbar和碎片Fragment
   2.ActionBar新增標籤,並且實現標籤的監聽事件TabListener
   3.TabListener的監聽事件中控制Fragment 的顯示
   
  1.activity的佈局,佈局中新增framlayout
  注意畫紅線的地方,我們添加了兩個屬性,至於為什麼要新增這兩個屬性呢?我等一下在說
 
<span style="font-size:18px;"><?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    <span style="color:#FF0000;"> xmlns:tools="http://schemas.android.com/tools"</span>
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" 
    <span style="color:#FF0000;">tools:context="com.example.dr_actionbar.TabActionBarViewPager"
    tools:ignore="MergeRootFrame"</span>>
    
    <FrameLayout
    android:id="@+id/frameLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
        
    </FrameLayout>
    

</LinearLayout></span>
     2.Activity中設定actionBar屬性引數  標籤模式ActionBar.NAVIGATION_MODE_TABS和新增tab標籤和其監聽事件

<span style="font-family:Arial;font-size:18px;">     </span><span style="font-size:18px;">ActionBar actionBar = getActionBar();
<span style="font-family:Arial;">    </span>actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
<span style="font-family:Arial;">    </span>actionBar.addTab(actionBar.newTab().setText("tab1")
</span><pre name="code" class="java"><span style="font-size:18px;">				.setTabListener(this));
<span style="font-family:Arial;">    </span>actionBar.addTab(actionBar.newTab().setText("tab2")
				.setTabListener(this));
<span style="font-family:Arial;">    </span>actionBar.addTab(actionBar.newTab().setText("tab3")
				.setTabListener(this));</span>
      3.tabListener中控制碎片的顯示

<span style="font-size:18px;">public class TabActionBarFragment extends FragmentActivity implements
		TabListener {......</span>

<span style="font-size:18px;">    @Override
    public void onTabSelected(Tab tab, android.app.FragmentTransaction ft) {
        //控制碎片的顯示
        showFragment_transtion(tab.getPosition());

        
    }

    @Override
    public void onTabUnselected(Tab tab, android.app.FragmentTransaction ft) {
        // TODO Auto-generated method stub
        
    }

    @Override
    public void onTabReselected(Tab tab, android.app.FragmentTransaction ft) {
        // TODO Auto-generated method stub
        
    }


      private void showFragment_transtion(int position) {
        Fragment fragment = null;
        switch (position) {
        case 0:
            fragment = new Fragment1();
            break;
        case 1:
            fragment = new Fragment2();
            break;
        case 2:
            fragment = new Fragment3();
            break;

        default:
            break;
        }
        
        //用於向fragment傳入引數
        Bundle argsBundle = new Bundle();
        argsBundle.putInt("number", position+1);
        //向fragment傳入引數
        fragment.setArguments(argsBundle);
        
        FragmentManager fm = getSupportFragmentManager();
        FragmentTransaction ft1 = fm.beginTransaction();
        ft1.replace(R.id.frameLayout, fragment); 
        ft1.commit();

    }</span>
    

   4.Fragment佈局

<span style="font-size:18px;"><?xml version="1.0" encoding="utf-8"?>
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    android:layout_width="match_parent"  
    android:layout_height="match_parent"  
    android:orientation="vertical"   >  
      
    <TextView  
        android:id="@+id/txt"
        android:layout_width="match_parent"  
        android:layout_height="match_parent"  
        android:layout_gravity="center"  
        android:gravity="center"  
        android:textSize="30dp"  
        android:textStyle="bold"  
        android:textColor="#FFFFFF" />  
  
</LinearLayout> </span>
    

  5.Fragment類的程式碼
    
<span style="font-size:18px;">package fragment;

import com.example.dr_actionbar.R;

import android.graphics.Color;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

public class Fragment1 extends Fragment {
    View view;
    TextView txt;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        view = inflater.inflate(R.layout.fragment, null);
        txt = (TextView) view.findViewById(R.id.txt);
        // 獲取建立該fragment時傳入的引數bundle
        Bundle args = getArguments();
        if (args != null) {
            txt.setText("碎片" + args.getInt("number"));
        } else {
            txt.setText("Fragment" + 1);
        }

        // 隨機變換顏色
        int bg = Color.rgb((int) Math.floor(Math.random() * 128) + 64,
                (int) Math.floor(Math.random() * 128) + 64,
                (int) Math.floor(Math.random() * 128) + 64);
        view.setBackgroundColor(bg);
        return view;
    }

}</span>





6.ActionBar的tab導航--Viewpager
 ViewPager 經常和 Fragment 一起使用,結合ActionBar 的 Tab,實現 tab 頁面的左右滑動。 實現 tab 頁的左右滑動的好處是,由於我們經常習慣單手操作手機,而單手切換 tab 頁是非常困難的,而 tab 頁面的左右滑動功能正好可以解決這個問題,提供更加方便的互動。
 
   我們實現的邏輯是
   1.新建一個activity,同時擁有actionbar和ViewPager
   2.ViewPager中裝載Fragment
   3.ActionBar新增標籤,並且實現標籤的監聽事件TabListener
   4.TabListener的監聽事件中控制ViewPager的顯示

  可以看出Viewpager+Tab上面的使用的道理是相同的,變化之處在於是Tab和viewPager之間的轉換了,所以我們直接關注變化的程式碼就好了

  1.activity的佈局,我們可以注意到只是把Fragment用的Framlayout換成ViewPager了

<font size="5"><font color="#333333" size="4"><font style=" line-height: 26px; " face="Arial" color="#333333"><font size="4"><font size="5"><font color="#333333" size="4"><span style="font-family:Arial;color:#333333;line-height: 26px; "><span style="font-size:24px;"><span style="font-size:18px;"><?xml version="1.0" encoding="utf-8"?>
<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:orientation="vertical" 
    tools:context="com.example.dr_actionbar.TabActionBarViewPager"
    tools:ignore="MergeRootFrame">
    
    <android.support.v4.view.ViewPager
    android:id="@+id/viewPager"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
        
    </android.support.v4.view.ViewPager>
    

</LinearLayout>
</span></span></span></font></font></font></font></font></font>

  2.tabListener中控制viewPager的顯示

	@Override
	public void onTabSelected(Tab tab, FragmentTransaction ft) {
		//控制ViewPager的顯示
	    viewPager.setCurrentItem(tab.getPosition());
		
	}

	@Override
	public void onTabUnselected(Tab tab, FragmentTransaction ft) {
		
	}

	@Override
	public void onTabReselected(Tab tab, FragmentTransaction ft) {
		
	}