티스토리 뷰


[안드로이드] 안드로이드 이전 버전도 호환가능한 액션바 만들기


이전에 올렸던 안드로이드 인터페이스 디자인 패턴 영상에(참고: [Android] UI Design patterns) 나왔던 액션 바 (Action Bar)를 만드는 방법에 대해서 알아보자. 


- 참고: 액션 바(ActionBar)는 안드로이드 SDK 3.0 (HoneyComb) 이후 추가되어 보다 쉽게 사용이 가능하다. 하지만 3.0 이전 버전에서는 ActionBar가 존재하지 않는다.


하지만 그 이전의 안드로이드 버전에 호환이 불가능하기 때문에 무턱대고 사용했다간 호환성문제에 부딪칠 것이다. 따라서 안드로이드 개발자 사이트에서는 호환가능한 예제를 제공해주어서 이 문제를 해결하도록 도와주고 있다.(참고: http://developer.android.com/resources/samples/ActionBarCompat/index.html)


여기서 사용하는 방법은 ActionBar의 생성을 버전별로 다르게 도와주는 Helper를 두어서 버전마다 다르게 설정하는 방식을 이용하고 있다.


즉, 3.0 이후의 버전에서는 그냥 ActionBar를 이용하고, 그 이전의 버전에서는 지금 ActionBar에서 하고 있는 것을 하드코딩으로 넣도록 도와주고 있다. 그 이전에는 MenuInflater를 이용해서 타이틀바 대신 액션바를 만들어서 넣어주는 방식을 취하고 있다.


그럼 예제를 따라가면서 3.0 이전 버전의 액션바를 한번 만들어보자.

* 3.0 이후의 버전은 위의 참고 사이트에서 ActionBarHelperICS와 ActionBarHelperHoneyComb 을 참고.





0. 이후에 만들 파일들

- res/menu/event_list.xml
- res/layout/actionbar_compat.xml
- res/drawable/actionbar_compat_item.xml
- res/drawable/actionbar_compat_item_pressed.xml

- res/drawable/actionbar_compat_item_focused.xml
- ActionBarHelper.java

- ActionBarHelperBase.java
- SimpleMenu.java
- SimpleMenuItem.java
- ActionBarActivity.java
+ 기타 xml 파일들



1. res/menu/event_list.xml 만들기

: 사용할 메뉴의 xml을 만드는 작업이다. res 폴더에 가서 우클릭 > New > Android XML file 선택하고 아래와 같이 Resource Type을 Menu로 선택하고 Root Element를 menu로 선택하고 파일명을 입력하고 Finish를 누르자.

* 이후에도 여러개의 xml파일들을 만들어야한다.



: 만들고나서 다음과 같이 보여줄 아이템들을 설정하자.


:xml 파일을 텍스트로 살펴보면 다음과 같다. 위의 인터페이스를 사용하는 것이 불편하면 아래처럼 직접 xml 파일을 입력하면 된다.

<?xml version="1.0" encoding="utf-8"?>

<menu xmlns:android="http://schemas.android.com/apk/res/android" >


    <item android:id="@+id/menu_invite"

        android:title="@string/invite"

        android:icon="@android:drawable/ic_menu_agenda"

        android:orderInCategory="1"

        android:showAsAction="always" />

    <item android:id="@+id/menu_create_event"

        android:title="@string/create_event"

        android:icon="@android:drawable/ic_menu_save"

        android:orderInCategory="0"

        android:showAsAction="always" />


</menu>



2. ActionBarHelper 클래스 만들기

: 이 클래스는 abstract 클래스로 다른 안드로이드 버전에 따라 다른 ActionBarHelper를 리턴하게 될 것이다. 각 함수 설명은 영어로 된 주석이 있으니 참고하자. 여기서 static 함수인 createInstance에서 안드로이드의 버전에 따라 다르게 리턴하는 것을 볼 수 있다. 일단은 3.0 이전 버전에 맞는 액션바를 ActionBarHelperBase만 리턴하도록 했다. 생성자에 Activity를 받음으로써 해당하는 Activity에 액션바를 추가하는 방식으로 되어있다.

public abstract class ActionBarHelper {


    protected Activity mActivity;


    /**

     * Factory method for creating {@link ActionBarHelper} objects for a

     * given activity. Depending on which device the app is running, either a basic helper or

     * Honeycomb-specific helper will be returned.

     */

    public static ActionBarHelper createInstance(Activity activity) {

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {

//            return new ActionBarHelperICS(activity);

        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.DONUT) {

//            return new ActionBarHelperHoneycomb(activity);

        } else {

        return new ActionBarHelperBase(activity);

        }

    return new ActionBarHelperBase(activity);

    }


    protected ActionBarHelper(Activity activity) {

        mActivity = activity;

    }


    /**

     * Action bar helper code to be run in {@link Activity#onCreate(android.os.Bundle)}.

     */

    public void onCreate(Bundle savedInstanceState) {

    }


    /**

     * Action bar helper code to be run in {@link Activity#onPostCreate(android.os.Bundle)}.

     */

    public void onPostCreate(Bundle savedInstanceState) {

    }


    /**

     * Action bar helper code to be run in {@link Activity#onCreateOptionsMenu(android.view.Menu)}.

     *

     * NOTE: Setting the visibility of menu items in <em>menu</em> is not currently supported.

     */

    public boolean onCreateOptionsMenu(Menu menu) {

        return true;

    }


    /**

     * Action bar helper code to be run in {@link Activity#onTitleChanged(CharSequence, int)}.

     */

    protected void onTitleChanged(CharSequence title, int color) {

    }


    /**

     * Sets the indeterminate loading state of the item with ID {@link R.id.menu_refresh}.

     * (where the item ID was menu_refresh).

     */

    public abstract void setRefreshActionItemState(boolean refreshing);


    /**

     * Returns a {@link MenuInflater} for use when inflating menus. The implementation of this

     * method in {@link ActionBarHelperBase} returns a wrapped menu inflater that can read

     * action bar metadata from a menu resource pre-Honeycomb.

     */

    public MenuInflater getMenuInflater(MenuInflater superMenuInflater) {

        return superMenuInflater;

    }

}



3. ActionBarHelperBase 클래스 만들기

: 이 클래스가 Activity에 액션바를 추가하는 루틴을 만들게 되는 클래스이다. 여기서 getMenuInflater에서 내부 클래스인 WrappedMenuInflater 클래스를 보면 메뉴에 해당하는 xml파일을 읽어와서 각 아이템을 추가하는데, showAsAction의 추가적인 속성을 처리하는 것을 볼 수 있다. 보여주는 속성으로 되어있으면 xml 안에 있는 item을 메뉴에 추가하는 방식이다.


: 액션바의 추가는 onPostCreate함수에서 이루어진다. 여기서 사용하고 있는 SimpleMenuSimpleMenuItem을 간단하게 구현을 해서 추가해야한다.


- 이 소스에는 불필요한 소스가 꽤 많이 포함되어있다. 특히 새로고침을 할때 액션바에 프로그래스를 띄우는 소스도 포함되어있는데 참고할만할거 같아서 일단 놔두었다. 봐두면 요긴하게 사용할 수 있을 것이다.(setRefreshActionItemState 함수 등)

public class ActionBarHelperBase extends ActionBarHelper {


    private static final String MENU_RES_NAMESPACE = "http://schemas.android.com/apk/res/android";

    private static final String MENU_ATTR_ID = "id";

    private static final String MENU_ATTR_SHOW_AS_ACTION = "showAsAction";


    protected Set<Integer> mActionItemIds = new HashSet<Integer>();


    protected ActionBarHelperBase(Activity activity) {

        super(activity);

    }


    /**{@inheritDoc}*/

    @Override

    public void onCreate(Bundle savedInstanceState) {

        mActivity.requestWindowFeature(Window.FEATURE_CUSTOM_TITLE);

    }


    /**{@inheritDoc}*/

    @Override

    public void onPostCreate(Bundle savedInstanceState) {

        mActivity.getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE,

                R.layout.actionbar_compat);

        setupActionBar();


        SimpleMenu menu = new SimpleMenu(mActivity);

        mActivity.onCreatePanelMenu(Window.FEATURE_OPTIONS_PANEL, menu);

        mActivity.onPrepareOptionsMenu(menu);

        for (int i = 0; i < menu.size(); i++) {

            MenuItem item = menu.getItem(i);

            if (mActionItemIds.contains(item.getItemId())) {

                addActionItemCompatFromMenuItem(item);

            }

        }

    }


    /**

     * Sets up the compatibility action bar with the given title.

     */

    private void setupActionBar() {

        final ViewGroup actionBarCompat = getActionBarCompat();

        if (actionBarCompat == null) {

            return;

        }


        LinearLayout.LayoutParams springLayoutParams = new LinearLayout.LayoutParams(

                0, ViewGroup.LayoutParams.FILL_PARENT);

        springLayoutParams.weight = 1;


        // Add Home button

        SimpleMenu tempMenu = new SimpleMenu(mActivity);

        SimpleMenuItem homeItem = new SimpleMenuItem(

                tempMenu, android.R.id.home, 0, mActivity.getString(R.string.app_name));

        homeItem.setIcon(android.R.drawable.ic_menu_revert);

        addActionItemCompatFromMenuItem(homeItem);


        // Add title text

        TextView titleText = new TextView(mActivity, null, R.attr.actionbarCompatTitleStyle);

        titleText.setLayoutParams(springLayoutParams);

        titleText.setText(mActivity.getTitle());

        actionBarCompat.addView(titleText);

    }


    /**{@inheritDoc}*/

    @Override

    public void setRefreshActionItemState(boolean refreshing) {

        View refreshButton = mActivity.findViewById(R.id.actionbar_compat_item_refresh);

        View refreshIndicator = mActivity.findViewById(

                R.id.actionbar_compat_item_refresh_progress);


        if (refreshButton != null) {

            refreshButton.setVisibility(refreshing ? View.GONE : View.VISIBLE);

        }

        if (refreshIndicator != null) {

            refreshIndicator.setVisibility(refreshing ? View.VISIBLE : View.GONE);

        }

    }


    /**

     * Action bar helper code to be run in {@link Activity#onCreateOptionsMenu(android.view.Menu)}.

     *

     * NOTE: This code will mark on-screen menu items as invisible.

     */

    @Override

    public boolean onCreateOptionsMenu(Menu menu) {

        // Hides on-screen action items from the options menu.

        for (Integer id : mActionItemIds) {

            menu.findItem(id).setVisible(false);

        }

        return true;

    }


    /**{@inheritDoc}*/

    @Override

    protected void onTitleChanged(CharSequence title, int color) {

        TextView titleView = (TextView) mActivity.findViewById(R.id.actionbar_compat_title);

        if (titleView != null) {

            titleView.setText(title);

        }

    }


    /**

     * Returns a {@link android.view.MenuInflater} that can read action bar metadata on

     * pre-Honeycomb devices.

     */

    public MenuInflater getMenuInflater(MenuInflater superMenuInflater) {

        return new WrappedMenuInflater(mActivity, superMenuInflater);

    }


    /**

     * Returns the {@link android.view.ViewGroup} for the action bar on phones (compatibility action

     * bar). Can return null, and will return null on Honeycomb.

     */

    private ViewGroup getActionBarCompat() {

        return (ViewGroup) mActivity.findViewById(R.id.actionbar_compat);

    }


    /**

     * Adds an action button to the compatibility action bar, using menu information from a {@link

     * android.view.MenuItem}. If the menu item ID is <code>menu_refresh</code>, the menu item's

     * state can be changed to show a loading spinner using

     * {@link com.example.android.actionbarcompat.ActionBarHelperBase#setRefreshActionItemState(boolean)}.

     */

    private View addActionItemCompatFromMenuItem(final MenuItem item) {

        final int itemId = item.getItemId();


        final ViewGroup actionBar = getActionBarCompat();

        if (actionBar == null) {

            return null;

        }


        // Create the button

        ImageButton actionButton = new ImageButton(mActivity, null,

                itemId == android.R.id.home

                        ? R.attr.actionbarCompatItemHomeStyle

                        : R.attr.actionbarCompatItemStyle);

        actionButton.setLayoutParams(new ViewGroup.LayoutParams(

                (int) mActivity.getResources().getDimension(

                        itemId == android.R.id.home

                                ? R.dimen.actionbar_compat_button_home_width

                                : R.dimen.actionbar_compat_button_width),

                ViewGroup.LayoutParams.FILL_PARENT));

        if (itemId == R.id.menu_my_peeps) {

            actionButton.setId(R.id.actionbar_compat_item_refresh);

        }

        actionButton.setImageDrawable(item.getIcon());

        actionButton.setScaleType(ImageView.ScaleType.CENTER);

        actionButton.setContentDescription(item.getTitle());

        actionButton.setOnClickListener(new View.OnClickListener() {

            public void onClick(View view) {

                mActivity.onMenuItemSelected(Window.FEATURE_OPTIONS_PANEL, item);

            }

        });


        actionBar.addView(actionButton);


  /*      if (item.getItemId() == R.id.menu_my_peeps) {

            // Refresh buttons should be stateful, and allow for indeterminate progress indicators,

            // so add those.

            ProgressBar indicator = new ProgressBar(mActivity, null,

                    R.attr.actionbarCompatProgressIndicatorStyle);


            final int buttonWidth = mActivity.getResources().getDimensionPixelSize(

                    R.dimen.actionbar_compat_button_width);

            final int buttonHeight = mActivity.getResources().getDimensionPixelSize(

                    R.dimen.actionbar_compat_height);

            final int progressIndicatorWidth = buttonWidth / 2;


            LinearLayout.LayoutParams indicatorLayoutParams = new LinearLayout.LayoutParams(

                    progressIndicatorWidth, progressIndicatorWidth);

            indicatorLayoutParams.setMargins(

                    (buttonWidth - progressIndicatorWidth) / 2,

                    (buttonHeight - progressIndicatorWidth) / 2,

                    (buttonWidth - progressIndicatorWidth) / 2,

                    0);

            indicator.setLayoutParams(indicatorLayoutParams);

            indicator.setVisibility(View.GONE);

            indicator.setId(R.id.actionbar_compat_item_refresh_progress);

            actionBar.addView(indicator);

        }

*/

        return actionButton;

    }


    /**

     * A {@link android.view.MenuInflater} that reads action bar metadata.

     */

    private class WrappedMenuInflater extends MenuInflater {

        MenuInflater mInflater;


        public WrappedMenuInflater(Context context, MenuInflater inflater) {

            super(context);

            mInflater = inflater;

        }


        @Override

        public void inflate(int menuRes, Menu menu) {

            loadActionBarMetadata(menuRes);

            mInflater.inflate(menuRes, menu);

        }


        /**

         * Loads action bar metadata from a menu resource, storing a list of menu item IDs that

         * should be shown on-screen (i.e. those with showAsAction set to always or ifRoom).

         * @param menuResId

         */

        private void loadActionBarMetadata(int menuResId) {

            XmlResourceParser parser = null;

            try {

                parser = mActivity.getResources().getXml(menuResId);


                int eventType = parser.getEventType();

                int itemId;

                int showAsAction;


                boolean eof = false;

                while (!eof) {

                    switch (eventType) {

                        case XmlPullParser.START_TAG:

                            if (!parser.getName().equals("item")) {

                                break;

                            }


                            itemId = parser.getAttributeResourceValue(MENU_RES_NAMESPACE,

                                    MENU_ATTR_ID, 0);

                            if (itemId == 0) {

                                break;

                            }


                            showAsAction = parser.getAttributeIntValue(MENU_RES_NAMESPACE,

                                    MENU_ATTR_SHOW_AS_ACTION, -1);

                            if (showAsAction == MenuItem.SHOW_AS_ACTION_ALWAYS ||

                                    showAsAction == MenuItem.SHOW_AS_ACTION_IF_ROOM) {

                                mActionItemIds.add(itemId);

                            }

                            break;


                        case XmlPullParser.END_DOCUMENT:

                            eof = true;

                            break;

                    }


                    eventType = parser.next();

                }

            } catch (XmlPullParserException e) {

                throw new InflateException("Error inflating menu XML", e);

            } catch (IOException e) {

                throw new InflateException("Error inflating menu XML", e);

            } finally {

                if (parser != null) {

                    parser.close();

                }

            }

        }


    }

}



4. SimpleMenu와 SimpleMenuItem 클래스 구현

: 액션바에서 사용할 메뉴 클래스와 메뉴 아이템 클래스이다. 이 클래스에서는 Menu 인터페이스를 구현하여 메뉴의 순서와 추가/삭제를 도와주는 함수들을 구현하면 된다. 액션바에서 다른 추가적인 함수들은 사용안하므로 구현을 안해도 된다.

: 먼저 SimpleMenu 클래스

public class SimpleMenu implements Menu {


private Context mContext;

private Resources mResources;


private ArrayList<SimpleMenuItem> mItems;

public SimpleMenu(Context context) {

    mContext = context;

    mResources = context.getResources();

    mItems = new ArrayList<SimpleMenuItem>();

}

public Context getContext() {

    return mContext;

}

public Resources getResources() {

    return mResources;

}

public MenuItem add(CharSequence title) {

    return addInternal(0, 0, title);

}

public MenuItem add(int titleRes) {

    return addInternal(0, 0, mResources.getString(titleRes));

}

public MenuItem add(int groupId, int itemId, int order, CharSequence title) {

    return addInternal(itemId, order, title);

}

public MenuItem add(int groupId, int itemId, int order, int titleRes) {

    return addInternal(itemId, order, mResources.getString(titleRes));

}

/**

* Adds an item to the menu.  The other add methods funnel to this.

*/

private MenuItem addInternal(int itemId, int order, CharSequence title) {

    final SimpleMenuItem item = new SimpleMenuItem(this, itemId, order, title);

    mItems.add(findInsertIndex(mItems, order), item);

    return item;

}

private static int findInsertIndex(ArrayList<? extends MenuItem> items, int order) {

    for (int i = items.size() - 1; i >= 0; i--) {

        MenuItem item = items.get(i);

        if (item.getOrder() <= order) {

            return i + 1;

        }

    }

    return 0;

}

public int findItemIndex(int id) {

    final int size = size();

    for (int i = 0; i < size; i++) {

        SimpleMenuItem item = mItems.get(i);

        if (item.getItemId() == id) {

            return i;

        }

    }

    return -1;

}

public void removeItem(int itemId) {

    removeItemAtInt(findItemIndex(itemId));

}

private void removeItemAtInt(int index) {

    if ((index < 0) || (index >= mItems.size())) {

        return;

    }

    mItems.remove(index);

}

public void clear() {

    mItems.clear();

}

public MenuItem findItem(int id) {

    final int size = size();

    for (int i = 0; i < size; i++) {

        SimpleMenuItem item = mItems.get(i);

        if (item.getItemId() == id) {

            return item;

        }

    }

    return null;

}

public int size() {

    return mItems.size();

}

public MenuItem getItem(int index) {

    return mItems.get(index);

}

// Unsupported operations.

public SubMenu addSubMenu(CharSequence charSequence) {

    throw new UnsupportedOperationException("This operation is not supported for SimpleMenu");

}

public SubMenu addSubMenu(int titleRes) {

    throw new UnsupportedOperationException("This operation is not supported for SimpleMenu");

}

public SubMenu addSubMenu(int groupId, int itemId, int order, CharSequence title) {

    throw new UnsupportedOperationException("This operation is not supported for SimpleMenu");

}

public SubMenu addSubMenu(int groupId, int itemId, int order, int titleRes) {

    throw new UnsupportedOperationException("This operation is not supported for SimpleMenu");

}

public int addIntentOptions(int i, int i1, int i2, ComponentName componentName,

        Intent[] intents, Intent intent, int i3, MenuItem[] menuItems) {

    throw new UnsupportedOperationException("This operation is not supported for SimpleMenu");

}

public void removeGroup(int i) {

    throw new UnsupportedOperationException("This operation is not supported for SimpleMenu");

}

public void setGroupCheckable(int i, boolean b, boolean b1) {

    throw new UnsupportedOperationException("This operation is not supported for SimpleMenu");

}

public void setGroupVisible(int i, boolean b) {

    throw new UnsupportedOperationException("This operation is not supported for SimpleMenu");

}

public void setGroupEnabled(int i, boolean b) {

    throw new UnsupportedOperationException("This operation is not supported for SimpleMenu");

}

public boolean hasVisibleItems() {

    throw new UnsupportedOperationException("This operation is not supported for SimpleMenu");

}

public void close() {

    throw new UnsupportedOperationException("This operation is not supported for SimpleMenu");

}

public boolean performShortcut(int i, KeyEvent keyEvent, int i1) {

    throw new UnsupportedOperationException("This operation is not supported for SimpleMenu");

}

public boolean isShortcutKey(int i, KeyEvent keyEvent) {

    throw new UnsupportedOperationException("This operation is not supported for SimpleMenu");

}

public boolean performIdentifierAction(int i, int i1) {

    throw new UnsupportedOperationException("This operation is not supported for SimpleMenu");

}

public void setQwertyMode(boolean b) {

    throw new UnsupportedOperationException("This operation is not supported for SimpleMenu");

}

}

: 다음은 SimpleMenuItem 클래스

: MenuItem을 구현하며 아이콘을 그려주는 부분이 중요

public class SimpleMenuItem implements MenuItem {



    private SimpleMenu mMenu;


    private final int mId;

    private final int mOrder;

    private CharSequence mTitle;

    private CharSequence mTitleCondensed;

    private Drawable mIconDrawable;

    private int mIconResId = 0;

    private boolean mEnabled = true;


    public SimpleMenuItem(SimpleMenu menu, int id, int order, CharSequence title) {

        mMenu = menu;

        mId = id;

        mOrder = order;

        mTitle = title;

    }


    public int getItemId() {

        return mId;

    }


    public int getOrder() {

        return mOrder;

    }


    public MenuItem setTitle(CharSequence title) {

        mTitle = title;

        return this;

    }


    public MenuItem setTitle(int titleRes) {

        return setTitle(mMenu.getContext().getString(titleRes));

    }


    public CharSequence getTitle() {

        return mTitle;

    }


    public MenuItem setTitleCondensed(CharSequence title) {

        mTitleCondensed = title;

        return this;

    }


    public CharSequence getTitleCondensed() {

        return mTitleCondensed != null ? mTitleCondensed : mTitle;

    }


    public MenuItem setIcon(Drawable icon) {

        mIconResId = 0;

        mIconDrawable = icon;

        return this;

    }


    public MenuItem setIcon(int iconResId) {

        mIconDrawable = null;

        mIconResId = iconResId;

        return this;

    }


    public Drawable getIcon() {

        if (mIconDrawable != null) {

            return mIconDrawable;

        }


        if (mIconResId != 0) {

            return mMenu.getResources().getDrawable(mIconResId);

        }


        return null;

    }


    public MenuItem setEnabled(boolean enabled) {

        mEnabled = enabled;

        return this;

    }


    public boolean isEnabled() {

        return mEnabled;

    }


    // No-op operations. We use no-ops to allow inflation from menu XML.


    public int getGroupId() {

        // Noop

        return 0;

    }


    public View getActionView() {

        // Noop

        return null;

    }


    public MenuItem setActionProvider(ActionProvider actionProvider) {

        // Noop

        return this;

    }


    public ActionProvider getActionProvider() {

        // Noop

        return null;

    }


    public boolean expandActionView() {

        // Noop

        return false;

    }


    public boolean collapseActionView() {

        // Noop

        return false;

    }


    public boolean isActionViewExpanded() {

        // Noop

        return false;

    }


    @Override

    public MenuItem setOnActionExpandListener(OnActionExpandListener onActionExpandListener) {

        // Noop

        return this;

    }


    public MenuItem setIntent(Intent intent) {

        // Noop

        return this;

    }


    public Intent getIntent() {

        // Noop

        return null;

    }


    public MenuItem setShortcut(char c, char c1) {

        // Noop

        return this;

    }


    public MenuItem setNumericShortcut(char c) {

        // Noop

        return this;

    }


    public char getNumericShortcut() {

        // Noop

        return 0;

    }


    public MenuItem setAlphabeticShortcut(char c) {

        // Noop

        return this;

    }


    public char getAlphabeticShortcut() {

        // Noop

        return 0;

    }


    public MenuItem setCheckable(boolean b) {

        // Noop

        return this;

    }


    public boolean isCheckable() {

        // Noop

        return false;

    }


    public MenuItem setChecked(boolean b) {

        // Noop

        return this;

    }


    public boolean isChecked() {

        // Noop

        return false;

    }


    public MenuItem setVisible(boolean b) {

        // Noop

        return this;

    }


    public boolean isVisible() {

        // Noop

        return true;

    }


    public boolean hasSubMenu() {

        // Noop

        return false;

    }


    public SubMenu getSubMenu() {

        // Noop

        return null;

    }


    public MenuItem setOnMenuItemClickListener(OnMenuItemClickListener onMenuItemClickListener) {

        // Noop

        return this;

    }


    public ContextMenu.ContextMenuInfo getMenuInfo() {

        // Noop

        return null;

    }


    public void setShowAsAction(int i) {

        // Noop

    }


    public MenuItem setShowAsActionFlags(int i) {

        // Noop

        return null;

    }


    public MenuItem setActionView(View view) {

        // Noop

        return this;

    }


    public MenuItem setActionView(int i) {

        // Noop

        return this;

    }

}




5. ActionBarActivity 클래스 구현

: 이 클래스는 Activity를 확장하는 클래스로 액션바를 사용하고자하는 Activity는 이 클래스를 확장하면 된다. 위에서 구현한 ActionBarHelper를 가지고 onCreate나 onPostCreate 이벤트때 액션바를 초기화해준다.

public class ActionBarActivity extends Activity {

    final ActionBarHelper mActionBarHelper = ActionBarHelper.createInstance(this);


    /**

     * Returns the {@link ActionBarHelper} for this activity.

     */

    protected ActionBarHelper getActionBarHelper() {

        return mActionBarHelper;

    }


    /**{@inheritDoc}*/

    @Override

    public MenuInflater getMenuInflater() {

        return mActionBarHelper.getMenuInflater(super.getMenuInflater());

    }


    /**{@inheritDoc}*/

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        mActionBarHelper.onCreate(savedInstanceState);

    }


    /**{@inheritDoc}*/

    @Override

    protected void onPostCreate(Bundle savedInstanceState) {

        super.onPostCreate(savedInstanceState);

        mActionBarHelper.onPostCreate(savedInstanceState);

    }


    /**

     * Base action bar-aware implementation for

     * {@link Activity#onCreateOptionsMenu(android.view.Menu)}.

     *

     * Note: marking menu items as invisible/visible is not currently supported.

     */

    @Override

    public boolean onCreateOptionsMenu(Menu menu) {

        boolean retValue = false;

        retValue |= mActionBarHelper.onCreateOptionsMenu(menu);

        retValue |= super.onCreateOptionsMenu(menu);

        return retValue;

    }


    /**{@inheritDoc}*/

    @Override

    protected void onTitleChanged(CharSequence title, int color) {

        mActionBarHelper.onTitleChanged(title, color);

        super.onTitleChanged(title, color);

    }

}



6. 액션바가 들어갈 Activity 구현

:아래와 같이 ActionBarActivity를 확장하고

public class EventListActivity extends ActionBarActivity{



: 아래와 같이 함수를 오버라이드해서 메뉴를 초기화 및 메뉴를 눌렀을때의 이벤트 처리를 하면 된다.


    @Override

    public boolean onCreateOptionsMenu(Menu menu) {

        MenuInflater menuInflater = getMenuInflater();

        menuInflater.inflate(R.menu.event_list, menu);  // 위에서 만든 xml로 초기화


        // Calling super after populating the menu is necessary here to ensure that the

        // action bar helpers have a chance to handle this event.

        return super.onCreateOptionsMenu(menu);

    }

    @Override

    public boolean onOptionsItemSelected(MenuItem item) {

        switch (item.getItemId()) {

            case android.R.id.home:

                this.finish();

                break;


            case R.id.menu_my_peeps:

             //menu_my_peeps 처리

             break;


            case R.id.menu_search:

                this.onSearchRequested();

                break;


            case R.id.menu_create_event:

                   // 액션바 menu_create_event 처리

        return super.onOptionsItemSelected(item);

    }



7. ActionBar에서 사용할 xml 파일들 설정하기

- actionbar_compat.xml

<?xml version="1.0" encoding="utf-8"?>

    

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

    android:id="@id/actionbar_compat"

    android:layout_width="fill_parent"

    android:layout_height="fill_parent"

    android:orientation="horizontal">

</LinearLayout>

- actionbar_compat_item.xml

<?xml version="1.0" encoding="utf-8"?>

<selector xmlns:android="http://schemas.android.com/apk/res/android" >

    <item android:drawable="@drawable/actionbar_compat_item_pressed"

        android:state_pressed="true" />

    <item android:drawable="@drawable/actionbar_compat_item_focused"

        android:state_focused="true" />

    <item android:drawable="@android:color/transparent" />

</selector>

- actionbar_compat_item_pressed.xml

<?xml version="1.0" encoding="utf-8"?>

<shape xmlns:android="http://schemas.android.com/apk/res/android" >

    <solid android:color="#eda700" />

</shape>

- actionbar_compat_item_focused.xml

<?xml version="1.0" encoding="utf-8"?>

<shape xmlns:android="http://schemas.android.com/apk/res/android" >

    <solid android:color="#ef7000" />

</shape>


8. 기타 xml 파일 설정하기

http://developer.android.com/resources/samples/ActionBarCompat/index.html에 나와있는 xml파일들을 가져와도 되고, 리소스를 이용하는 부분부분들을 수정해도 된다. 아래의 파일 내용들은 위의 사이트에 있는대로 가져온것이다. 스타일/설정에 따라 바꿔주자.


- res/values/ids.xml

<?xml version="1.0" encoding="utf-8"?>

<resources>

    <item name="actionbar_compat" type="id"/>

    <item type="id" name="actionbar_compat_title" />

    <item type="id" name="actionbar_compat_item_refresh_progress" />

    <item type="id" name="actionbar_compat_item_refresh" />

</resources>


- res/values/dimens.xml

<?xml version="1.0" encoding="utf-8"?>

<resources>

    <dimen name="actionbar_compat_height">48dp</dimen>

    <dimen name="actionbar_compat_button_width">48dp</dimen>

    <dimen name="actionbar_compat_button_home_width">56dp</dimen>    

</resources>



- res/values/attrs.xml

<?xml version="1.0" encoding="utf-8"?>

<resources>

    <declare-styleable name="AppTheme">

        <attr name="actionbarCompatTitleStyle" format="reference" />

        <attr name="actionbarCompatItemStyle" format="reference" />

        <attr name="actionbarCompatItemHomeStyle" format="reference" />

        <attr name="actionbarCompatProgressIndicatorStyle" format="reference" />

    </declare-styleable>


    <declare-styleable name="BezelImageView">

        <attr name="maskDrawable" format="reference" />

        <attr name="borderDrawable" format="reference" />

    </declare-styleable>

    

</resources>



- res/values/styles.xml

<?xml version="1.0" encoding="utf-8"?>

<resources>


    <style name="AppTheme" parent="android:style/Theme.Light">

        <item name="android:windowTitleSize">@dimen/actionbar_compat_height</item>

        <item name="android:windowTitleBackgroundStyle">@style/ActionBarCompat</item>

        <item name="android:windowContentOverlay">@drawable/actionbar_shadow</item>


        <!-- for programmatic instantiation -->

        <item name="actionbarCompatTitleStyle">@style/ActionBarCompatTitle</item>

        <item name="actionbarCompatItemStyle">@style/ActionBarCompatItem</item>

        <item name="actionbarCompatItemHomeStyle">@style/ActionBarCompatHomeItem</item>

        <item name="actionbarCompatProgressIndicatorStyle">@style/ActionBarCompatProgressIndicator</item>

    </style>


    <style name="ActionBarCompat">

        <item name="android:background">#eee</item>

    </style>


    <style name="ActionBarCompatItemBase">

        <!-- layout_width/height must be set in code -->

        <item name="android:scaleType">center</item>

        <item name="android:background">@drawable/actionbar_compat_item</item>

    </style>


    <style name="ActionBarCompatProgressIndicator" parent="android:style/Widget.ProgressBar.Large">

        <item name="android:indeterminate">true</item>

    </style>


    <style name="ActionBarCompatTitleBase">

        <item name="android:id">@id/actionbar_compat_title</item>

        <!-- layout_width/height/weight must be set in code -->

        <item name="android:gravity">center_vertical</item>

        <item name="android:textSize">14sp</item>

        <item name="android:paddingLeft">6dp</item>

        <item name="android:paddingRight">6dp</item>

        <item name="android:singleLine">true</item>

        <item name="android:ellipsize">marquee</item>

    </style>


    <style name="ActionBarCompatTitle" parent="style/ActionBarCompatTitleBase">

        <item name="android:textColor">@color/actionbar_title_color</item>

    </style>


    <style name="ActionBarCompatItem" parent="style/ActionBarCompatItemBase">

    </style>


    <style name="ActionBarCompatHomeItem" parent="style/ActionBarCompatItemBase">

    </style>    

</resources>



- res/values/colors.xml

<?xml version="1.0" encoding="utf-8"?>

<resources>

    <color name="actionbar_title_color">#224894</color>

</resources>



8. AndroidManifest.xml 에 스타일 설정하기
: 액션바를 사용하고자하는 Activity에 위에서 설정한 스타일을 적용하자.
android:theme="@style/AppTheme"


- 3.0 이전의 SDK에서의 액션바 구현 끝!



* 결과


- 이제 아이콘과 스타일을 자기 입맛에 맞게 수정하면 된다.



* 앞으로 다른 Activity에 액션바(ActionBar)를 추가하려면


a. 위의 1번의 액션바에 해당하는 xml을 만들고,

b. 6번 ActionBarActivity를 확장하고 초기화에서 위 a의 xml을 설정하고 이벤트들 처리해주고,

c. 8번 AndroidManifest.xml 에서 액션바 스타일을 설정해주면 된다.



처음 단계만 성공했으면 그뒤 적용하는거는 간단하다.


끝.

* 2012-11-01 추가: 예전에 여기서 ActionBarActivity를 만들어서 썼던 간단한 프로젝트 소스를 첨부합니다. 안드로이드 앱을 잘 모를때 만들었던거라 다른것보다는 그냥 ActionBarActivity를 어떻게 추가했는지 보시는게 좋을것 같습니다.


공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함