`

策略模式--从源码TabHost中看策略模式

阅读更多

什么是策略模式:

 

      针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换

      应当由客户端自己决定在什么情况下使用什么具体策略模式。

      策略模式不适合于处理同时嵌套多于一个算法的情形。

 

针对的设计原则:

 

封装变化的。

多用组合,少用继承。

针对接口编程,不针对实现编程。

 

优缺点:

 

优点:

1. 提供了管理相关的算法族的办法。

2. 提供了可以替换继承关系的办法。

3. 可以避免使用多重条件转移语句。

 

 缺点:

1. 客户端必须知道所有的策略类,并自行决定使用哪一个策略类。

2. 策略模式造成很多的策略类。

 

下面我们结合下源码中的TabHost的实现,来介绍下源码中如何使用策略模式

如果你在本地有下载源码的话,你可以在源码中找到这个类,在我的电脑中,它的位置如下:

F:\android-sdk\sources\android-15\android\widget\TabHost.java

 

这里引用林家男孩博客中的一张图:

这张图很好地说明了TabHost.java中的模式结构。

 

首先TabHost继承自FrameLayout,TabHost由两部分构成,一个是TabWidget,一个是TabSpace.

TabWidget用来指定和监听TabHost切换的,不是我们今天要讨论的范围。

TabSpec就是我们今天策略模式的主角了,下面对它进行分析。

 

TabHost可以看做是由一个个独立的用于点击来切换显示面板的Indicator和所对应的显示内容的面板Content组成。

也就是说一个Indicator对应一个Content,这在源码中对应着的是TabSpec这个类,换句话说:每个TabSpec对应着一个Indicator和相应的显示内容的面板Content。这两个地方也是经常需要变动的。因为可能需要定制各种各样的TabHost,比如,使用自定义的View来作为Indicator,当然显示内容的面板也需要是可以被定制的。

那么这时候我们就需要将变化的东西抽象出来。以便它可以被很方便地替换

 

举个例子来说:

我要定制一个TabSpec,需要满足两个基本条件:

1.一个Indicator

2.一个Content

这个时候我有可能仅仅想使用文字作为Indicator,那么我用LabelIndicatorStrategy来满足我的要求,假如这个时候,我想换成图片加文字的方式来作为Indicator,怎么办呢?很简单,我可以换成LabelAndIconIndicatorStrategy类来满足要求。显示面板Content也是一样道理。

当然,实现这些需要编程技巧和java的特性,针对接口编程,多态等。这个可以去查找相关资料。

 

 

下面直接上源码:

 

TabSpec:

 

 /**
     * A tab has a tab indicator, content, and a tag that is used to keep
     * track of it.  This builder helps choose among these options.
     *
     * For the tab indicator, your choices are:
     * 1) set a label
     * 2) set a label and an icon
     *
     * For the tab content, your choices are:
     * 1) the id of a {@link View}
     * 2) a {@link TabContentFactory} that creates the {@link View} content.
     * 3) an {@link Intent} that launches an {@link android.app.Activity}.
     */
    public class TabSpec {

        private String mTag;

        private IndicatorStrategy mIndicatorStrategy;
        private ContentStrategy mContentStrategy;

        private TabSpec(String tag) {
            mTag = tag;
        }

        /**
         * Specify a label as the tab indicator.
         */
        public TabSpec setIndicator(CharSequence label) {
            mIndicatorStrategy = new LabelIndicatorStrategy(label);
            return this;
        }

        /**
         * Specify a label and icon as the tab indicator.
         */
        public TabSpec setIndicator(CharSequence label, Drawable icon) {
            mIndicatorStrategy = new LabelAndIconIndicatorStrategy(label, icon);
            return this;
        }

        /**
         * Specify a view as the tab indicator.
         */
        public TabSpec setIndicator(View view) {
            mIndicatorStrategy = new ViewIndicatorStrategy(view);
            return this;
        }

        /**
         * Specify the id of the view that should be used as the content
         * of the tab.
         */
        public TabSpec setContent(int viewId) {
            mContentStrategy = new ViewIdContentStrategy(viewId);
            return this;
        }

        /**
         * Specify a {@link android.widget.TabHost.TabContentFactory} to use to
         * create the content of the tab.
         */
        public TabSpec setContent(TabContentFactory contentFactory) {
            mContentStrategy = new FactoryContentStrategy(mTag, contentFactory);
            return this;
        }

        /**
         * Specify an intent to use to launch an activity as the tab content.
         */
        public TabSpec setContent(Intent intent) {
            mContentStrategy = new IntentContentStrategy(mTag, intent);
            return this;
        }


        public String getTag() {
            return mTag;
        }
    }
 

Indicator接口:

IndicatorStrategy

 

 /**
     * Specifies what you do to create a tab indicator.
     */
    private static interface IndicatorStrategy {

        /**
         * Return the view for the indicator.
         */
        View createIndicatorView();
    }

 

Content接口:

ContentStrategy

 

/**
     * Specifies what you do to manage the tab content.
     */
    private static interface ContentStrategy {

        /**
         * Return the content view.  The view should may be cached locally.
         */
        View getContentView();

        /**
         * Perhaps do something when the tab associated with this content has
         * been closed (i.e make it invisible, or remove it).
         */
        void tabClosed();
    }
 

Indicator接口实现类:

LabelIndicatorStrategy

LabelAndIconIndicatorStrategy

ViewIndicatorStrategy

 

 

/**
     * How to create a tab indicator that just has a label.
     */
    private class LabelIndicatorStrategy implements IndicatorStrategy {

        private final CharSequence mLabel;

        private LabelIndicatorStrategy(CharSequence label) {
            mLabel = label;
        }

        public View createIndicatorView() {
            final Context context = getContext();
            LayoutInflater inflater =
                    (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            View tabIndicator = inflater.inflate(mTabLayoutId,
                    mTabWidget, // tab widget is the parent
                    false); // no inflate params

            final TextView tv = (TextView) tabIndicator.findViewById(R.id.title);
            tv.setText(mLabel);

            if (context.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.DONUT) {
                // Donut apps get old color scheme
                tabIndicator.setBackgroundResource(R.drawable.tab_indicator_v4);
                tv.setTextColor(context.getResources().getColorStateList(R.color.tab_indicator_text_v4));
            }

            return tabIndicator;
        }
    }

    /**
     * How we create a tab indicator that has a label and an icon
     */
    private class LabelAndIconIndicatorStrategy implements IndicatorStrategy {

        private final CharSequence mLabel;
        private final Drawable mIcon;

        private LabelAndIconIndicatorStrategy(CharSequence label, Drawable icon) {
            mLabel = label;
            mIcon = icon;
        }

        public View createIndicatorView() {
            final Context context = getContext();
            LayoutInflater inflater =
                    (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            View tabIndicator = inflater.inflate(mTabLayoutId,
                    mTabWidget, // tab widget is the parent
                    false); // no inflate params

            final TextView tv = (TextView) tabIndicator.findViewById(R.id.title);
            final ImageView iconView = (ImageView) tabIndicator.findViewById(R.id.icon);

            // when icon is gone by default, we're in exclusive mode
            final boolean exclusive = iconView.getVisibility() == View.GONE;
            final boolean bindIcon = !exclusive || TextUtils.isEmpty(mLabel);

            tv.setText(mLabel);

            if (bindIcon && mIcon != null) {
                iconView.setImageDrawable(mIcon);
                iconView.setVisibility(VISIBLE);
            }

            if (context.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.DONUT) {
                // Donut apps get old color scheme
                tabIndicator.setBackgroundResource(R.drawable.tab_indicator_v4);
                tv.setTextColor(context.getResources().getColorStateList(R.color.tab_indicator_text_v4));
            }

            return tabIndicator;
        }
    }

    /**
     * How to create a tab indicator by specifying a view.
     */
    private class ViewIndicatorStrategy implements IndicatorStrategy {

        private final View mView;

        private ViewIndicatorStrategy(View view) {
            mView = view;
        }

        public View createIndicatorView() {
            return mView;
        }
    }

 

Content接口实现类:

ViewIdContentStrategy

FactoryContentStrategy

IntentContentStrategy

 

 

    /**
     * How to create the tab content via a view id.
     */
    private class ViewIdContentStrategy implements ContentStrategy {

        private final View mView;

        private ViewIdContentStrategy(int viewId) {
            mView = mTabContent.findViewById(viewId);
            if (mView != null) {
                mView.setVisibility(View.GONE);
            } else {
                throw new RuntimeException("Could not create tab content because " +
                        "could not find view with id " + viewId);
            }
        }

        public View getContentView() {
            mView.setVisibility(View.VISIBLE);
            return mView;
        }

        public void tabClosed() {
            mView.setVisibility(View.GONE);
        }
    }

    /**
     * How tab content is managed using {@link TabContentFactory}.
     */
    private class FactoryContentStrategy implements ContentStrategy {
        private View mTabContent;
        private final CharSequence mTag;
        private TabContentFactory mFactory;

        public FactoryContentStrategy(CharSequence tag, TabContentFactory factory) {
            mTag = tag;
            mFactory = factory;
        }

        public View getContentView() {
            if (mTabContent == null) {
                mTabContent = mFactory.createTabContent(mTag.toString());
            }
            mTabContent.setVisibility(View.VISIBLE);
            return mTabContent;
        }

        public void tabClosed() {
            mTabContent.setVisibility(View.GONE);
        }
    }

    /**
     * How tab content is managed via an {@link Intent}: the content view is the
     * decorview of the launched activity.
     */
    private class IntentContentStrategy implements ContentStrategy {

        private final String mTag;
        private final Intent mIntent;

        private View mLaunchedView;

        private IntentContentStrategy(String tag, Intent intent) {
            mTag = tag;
            mIntent = intent;
        }

        public View getContentView() {
            if (mLocalActivityManager == null) {
                throw new IllegalStateException("Did you forget to call 'public void setup(LocalActivityManager activityGroup)'?");
            }
            final Window w = mLocalActivityManager.startActivity(
                    mTag, mIntent);
            final View wd = w != null ? w.getDecorView() : null;
            if (mLaunchedView != wd && mLaunchedView != null) {
                if (mLaunchedView.getParent() != null) {
                    mTabContent.removeView(mLaunchedView);
                }
            }
            mLaunchedView = wd;

            // XXX Set FOCUS_AFTER_DESCENDANTS on embedded activities for now so they can get
            // focus if none of their children have it. They need focus to be able to
            // display menu items.
            //
            // Replace this with something better when Bug 628886 is fixed...
            //
            if (mLaunchedView != null) {
                mLaunchedView.setVisibility(View.VISIBLE);
                mLaunchedView.setFocusableInTouchMode(true);
                ((ViewGroup) mLaunchedView).setDescendantFocusability(
                        FOCUS_AFTER_DESCENDANTS);
            }
            return mLaunchedView;
        }

        public void tabClosed() {
            if (mLaunchedView != null) {
                mLaunchedView.setVisibility(View.GONE);
            }
        }
    }
 

很容易看出

针对TabSpec来说,将接口定义成内部变量:

 

private IndicatorStrategy mIndicatorStrategy;
private ContentStrategy mContentStrategy;

 再通过setter方法很容易地控制它的Indicator和Content。

比如:

 

 /**
         * Specify a label as the tab indicator.
         */
        public TabSpec setIndicator(CharSequence label) {
            mIndicatorStrategy = new LabelIndicatorStrategy(label);
            return this;
        }

这就是策略模式。

 

 

为了达到动态地改变类的行为的目的,我们需要将变化的部分,从不变的部分中抽象出来。由于需要实现动态替换,就需要利用面向对象的多态特性。我们需要利用接口,也就是针对接口编程。也就是说,相关的算法,需要具有同样的一个接口。本例中

LabelIndicatorStrategy,LabelAndIconIndicatorStrategy,ViewIndicatorStrategy是相关的一组算法,它们实现了Indicator接口。

 

ViewIdContentStrategy,FactoryContentStrategy,IntentContentStrategy是相关的一组算法,它们实现了Content接口。

你就可以很方便地通过多态(接口调用)来实现算法的替换。

这样一来,算法的变化就独立于使用算法的客户了,客户自己决定在什么情况下使用什么具体策略模式。

 

以上仅是个人见解,如果有不足的地方希望指出,共同进步,感谢一下参考链接。

参考链接:

http://bj007.blog.51cto.com/1701577/649063

http://bj007.blog.51cto.com/1701577/643572

http://blog.csdn.net/icrs23/article/details/1913871

 

 

 

 

 

 

  • 大小: 37.7 KB
2
2
分享到:
评论
2 楼 michaelye1988 2012-10-31  
freezingsky 写道
如果是讲设计模式的话,这个示例就太长了。建议以简而短的例子说明。然后再结合一些实际的场景,会有更好的效果。个人意见!

谢谢你的意见,是不是代码太多了啊?我是觉得如果没有把代码完整的展示出来,怕大家看得不够清晰。其实如果理解这个模式的话,看那张图就足够了,代码只是图中的类的具体形式,用来展示这个模式在源码中的样子。不管怎样,谢谢你的回复。
1 楼 freezingsky 2012-10-31  
如果是讲设计模式的话,这个示例就太长了。建议以简而短的例子说明。然后再结合一些实际的场景,会有更好的效果。个人意见!

相关推荐

    安卓app开发项目-可以横向拖动的TabHost(源码).zip

    安卓app开发项目-可以横向拖动的TabHost(源码).zip安卓app开发项目-可以横向拖动的TabHost(源码).zip安卓app开发项目-可以横向拖动的TabHost(源码).zip安卓app开发项目-可以横向拖动的TabHost(源码).zip安卓app开发...

    Android-可以横向拖动的TabHost(源码).zip

    Android-可以横向拖动的TabHost(源码).zip

    Android高级应用源码-封装的一个tabhost框架.rar

    本资源提供了一个高级应用的源码,它封装了一个TabHost框架,对于深入理解Android多视图切换以及UI设计有极大的帮助。以下是关于这个主题的详细知识点: 1. **TabHost基础**: - TabHost是Android SDK中的一个类,...

    Android高级应用源码-封装的一个tabhost框架.zip

    本资源提供的"Android高级应用源码-封装的一个tabhost框架.zip"是一个针对TabHost进行封装的框架,方便开发者快速构建多标签页的应用。下面将详细解析这个框架及其相关知识点。 1. **TabHost基本概念**: - ...

    Android代码-tabhost--UI源码.zip

    本资源“tabhost--UI源码.zip”显然是一个包含有关如何使用TabHost实现用户界面的源代码示例。通过分析这个压缩包中的源码,我们可以深入理解TabHost的工作原理以及如何在实际项目中应用。 TabHost是Android SDK...

    安卓开发-封装的一个tabhost框架.zip.zip

    在Android应用开发中,TabHost是一个非常重要的组件,它用于创建多标签的界面,使得用户可以在不同的视图间切换,每个标签通常对应一个Activity或Fragment。这个“安卓开发-封装的一个tabhost框架.zip”文件应该包含...

    android tabhost --android UI源码.rar

    这个“android tabhost --android UI源码.rar”压缩包文件很可能是包含了一些示例代码或者详细讲解TabHost使用的源码项目。通过分析和学习这些源码,我们可以深入了解TabHost的工作原理以及如何在实际应用中有效利用...

    安卓FragmentTab选项卡相关-封装的一个tabhost框架.zip

    FragmentTabHost是Android提供的一个TabHost与Fragment结合的类,使得在Android 3.0(API级别11)及更高版本中可以方便地实现选项卡式布局。 在"安卓FragmentTab选项卡相关-封装的一个tabhost框架.zip"这个资源中,...

    Android源码——android tabhost --android UI源码.zip

    这个压缩包"Android源码——android tabhost --android UI源码.zip"可能包含了一些示例代码,图片资源以及关于如何使用TabHost的解释。 首先,我们来深入理解TabHost的工作原理。TabHost是一个容器,它管理着多个...

    android tabhost --android UI源码.zip项目安卓应用源码下载

    android tabhost --android UI源码.zip项目安卓应用源码下载android tabhost --android UI源码.zip项目安卓应用源码下载 1.适合学生毕业设计研究参考 2.适合个人学习研究参考 3.适合公司开发项目技术参考

    Android开发实战经典-020714-标签:TabHost源代码和视频教程.zip

    Android开发实战经典_020714_标签:TabHost源代码和视频教程.zip

    android tabhost --android UI源码-IT计算机-毕业设计.zip

    本项目是几年前的Android应用源码示例,非常适合学生进行毕业设计学习,理解并掌握Android TabHost的用法。 TabHost是一个容器,它允许你在一个布局中设置多个TabWidget(选项卡)和一个FrameLayout(帧布局),...

    android tabhost --android UI源码.zip

    本项目源码着重展示了如何利用TabHost构建复杂的Android UI。以下是对源码的详细解析: 1. **TabHost基本概念**:TabHost是Android SDK提供的一个容器,它允许你在同一个活动中展示多个Tab,并且每个Tab可以关联一...

    Androidtabhost--AndroidUI源码.zip

    这个压缩包“Androidtabhost--AndroidUI源码.zip”很可能包含了一个关于如何使用TabHost进行UI设计的示例代码。 TabHost在Android中是一个容器,它负责管理和显示多个Tab。它的工作原理是通过TabWidget来展示各个...

Global site tag (gtag.js) - Google Analytics