2013/08/14

Example of using AppCompat

Actionbar 是 android 3.0 以後才有的, 所以之前都是使用 actionbarsherlock 來往前相容.
最近發現 google 也推出 actionbar compat 的版本, 所以就改使用 google.
以下是使用範例 :

要使用 actionbar, 首先在 menu layout 就必須做一些修正.
以下是使用的 layout, 可以看到在 showAsAction 的部份的不同. myapp 可以自訂成自己所要使用的字串
<menu xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:myapp="http://schemas.android.com/apk/res-auto">
    <item android:id="@+id/action_settings"
        android:title="@string/action_settings"
        android:orderInCategory="100"
        android:icon="@android:drawable/ic_menu_preferences"
        myapp:showAsAction="ifRoom" />
</menu>

以下是測試的 layout.
<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"
              tools:context=".MainActivity"
              android:orientation="vertical">

    <ListView
            android:id="@+id/list"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>
</LinearLayout>

而要使用 AppCompat 的話, activity 必須繼承 ActionBarActivty
package tw.clotai.appcompatexample;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v7.app.ActionBarActivity;
import android.view.Menu;
import android.widget.ArrayAdapter;
import android.widget.ListView;

public class MainActivity extends ActionBarActivity {

    final String[] data = {"1", "2", "3", "4", "5", "6"};

    ListView mList = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ArrayAdapter<String> adapter = new ArrayAdapter<String>(
                this, android.R.layout.simple_list_item_1, data);

        mList = (ListView)findViewById(R.id.list);
        mList.setAdapter(adapter);

    }


    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }
    
}


特別注意的是在 AndroidManifest.xml 裡面的 theme 必須使用以下幾種, 否則就會有問題.
Theme.AppCompat
Theme.AppCompat.Light
Theme.AppCompat.Light.DarkActionBar






而在我的應用中, 我通常會使用 fragment, 但是改用成 fragment 後就發現有些問題了.
以下是原本 fragment 的使用. MainActivity 改成以下
package tw.clotai.appcompatexample;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v7.app.ActionBarActivity;
import android.view.Menu;

public class MainActivity extends ActionBarActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Fragment frag = getSupportFragmentManager().findFragmentById(android.R.id.content);
        if (frag == null) {
            MainFrag m = new MainFrag();
            getSupportFragmentManager().beginTransaction().add(android.R.id.content, m).commit();
        }

    }


    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

}


MainFrag 如下
package tw.clotai.appcompatexample;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v7.app.ActionBarActivity;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ListView;

public class MainFrag extends Fragment {

final String[] data = {"1", "2", "3", "4", "5", "6"};

ListView mList = null;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.activity_main, container, false);

mList = (ListView)v.findViewById(R.id.list);

return super.onCreateView(inflater, container, savedInstanceState);
}

@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);

ArrayAdapter<String> adapter = new ArrayAdapter<String>(
getActivity(), android.R.layout.simple_list_item_1, data);

mList.setAdapter(adapter);

}
}

執行結果會發現如果是 android 3.0 以上就沒問題, 但以下則會什麼也沒有.
原因出在 AppCompat 會依據 android 版本來選擇使用不同的 content id.
如果是 android 3.0 以上, 就會使用 android.R.id.content, 所以當我測試時就沒問題
但是如果是以下, 則會使用 android.support.v7.appcompat.R.id.action_bar_activity_content
這也是為什麼會有問題的原因.

另外也要注意的是, setTitle 部份, 如果使用 AppCompat, 3.0 以下則必須也要使用 getSupportActionBar().setTitle()
來設定, 否則標題列就不會顯示你所設定的 Title 喔.

為了方便使用, 以下是我自己使用的 ActionBarActivty

package tw.clotai.appcompatexample;

import android.os.Build;
import android.support.v7.app.ActionBarActivity;

public class MyActionBarActivity extends ActionBarActivity {

@Override
public void setTitle(CharSequence title) {
getSupportActionBar().setTitle(title);
super.setTitle(title);
}

@Override
public void setTitle(int titleId) {
getSupportActionBar().setTitle(titleId);
super.setTitle(titleId);
}

protected final int getContentViewCompat() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
return android.R.id.content;
} else {
return android.support.v7.appcompat.R.id.action_bar_activity_content;
}
}

}


之後我要使用則繼承我自己所寫的這個 activity.


原始碼: AppCompatExampleProject.zip