Gallery3d 学习笔记(14)

上次我们研究了新的代码和原来2.3的代码的不同处,视频播放器的控制条从Framework中改变到了本地代码中,但是Framework中的控制条并没有删除,因为什么?

为了向下兼容。

另外我们还研究了触摸消息的分发,发现新的触摸分发更为规范一些,并且也没有像以前那样分层了,而是分页面,这样也更好一些,一旦有问题,可以到指定的Page中去找,而不是在透明的层里面到处找代码了。


既然研究完触摸消息,我们再来找下按键消息的处理。


还记得2.3的代码里面是如何处理按键消息的么?

Activity->RenderView->RootLayer->GridInputProcess

里面转了很多次才处理的,我们看下新的代码,找了半天只找到back 按键的处理,在AbstractGalleryActivity里面

    @Override
    public void onBackPressed() {
        // send the back event to the top sub-state
        GLRoot root = getGLRoot();
        root.lockRenderThread();
        try {
            getStateManager().onBackPressed();
        } finally {
            root.unlockRenderThread();
        }
    }


在StateManager里面我们看到了返回按键的处理,我们先不管通过getTopState是如何拿到当前的页面的

    public void onBackPressed() {
        if (!mStack.isEmpty()) {
            getTopState().onBackPressed();
        }
    }

以AlbumSetPage页面为例子来看

    @Override
    public void onBackPressed() {
        if (mShowDetails) {
            hideDetails();
        } else if (mSelectionManager.inSelectionMode()) {
            mSelectionManager.leaveSelectionMode();
        } else {
            super.onBackPressed();
        }
    }

最后会调用到super.onBackPressed,当然也可能是退出选择状态,我们先看Super是谁?我们说过页面的父类都是ActivityState,看下他的处理

    protected void onBackPressed() {
        mActivity.getStateManager().finishState(this);
    }

mActivity就是自己,看下AbstactGalleryActivity的实现

    public synchronized StateManager getStateManager() {
        if (mStateManager == null) {
            mStateManager = new StateManager(this);
        }
        return mStateManager;
    }

我们又遇到了StateManager.看起来状态管理器必须弄清楚,否则没法看,稍等。

现在只是知道back的处理被转来转去,最后在Page页面中处理,流程大致如下

AbstractGalleryActivity->StateManager->找到顶部的那个页面处理,这个页面里面要处理状态就->ActivityState->StateManager处理状态

其实看起来StateManager就是管理的各个页面的状态的。我们详细看下。


   private Stack<StateEntry> mStack = new Stack<StateEntry>();

定义了一个范型的栈,范型的类使用的StateEntry,我们先看下这个类

    private static class StateEntry {
        public Bundle data;
        public ActivityState activityState;

        public StateEntry(Bundle data, ActivityState state) {
            this.data = data;
            this.activityState = state;
        }
    }

其实就是连个成员,一个是Activity的状态,一个是Bundle数据

我们看下mStack是如何管理这些Page的

    public void startState(Class<? extends ActivityState> klass,
            Bundle data) {
        Log.v(TAG, "startState " + klass);
        ActivityState state = null;
        try {
            state = klass.newInstance();
        } catch (Exception e) {
            throw new AssertionError(e);
        }
        if (!mStack.isEmpty()) {
            ActivityState top = getTopState();
            if (mIsResumed) top.onPause();
        }
        state.initialize(mContext, data);

        mStack.push(new StateEntry(data, state));
        state.onCreate(data, null);
        if (mIsResumed) state.resume();
    }

这段代码比较麻烦,传进来一个继承于ActivityState的类,当然就是一个Page了,这个Page现在叫做klass,因为每个Page只是一个类,需要实例化,所以调用了一个实例化的静态方法 klass.newInstance,生产出一个Page的实例出来,叫做state,这个state经过初始化后,被压到这个范型的栈中了。

mStack.push(new StateEntry(data,state));

最后通过state.onCreate(date,null),将数据传到了页面中去,


突然发现整个不就是工厂模式么?比起2.3里面使用层的成员变量来生产一个个的层,是不是更好一些呢?


我写了一个简化版本的类大家理解下

public class Factory{
    public static Product startProduct(Class<? extends Product> klass) {
        try {
            return  klass.newInstance();
        } catch (Exception e) {
            throw new AssertionError(e);
        }
    }
}



然后,我们需要找到最上面的那个Page(就是当前使用的Page,因为Page不透明),我们只要知道栈顶是什么就可以了

    public ActivityState getTopState() {
        Utils.assertTrue(!mStack.isEmpty());
        return mStack.peek().activityState;
    }

我们明白整个系统中都是用这个范型的栈生产管理Page,那第一个生产出来的页面是什么,又是怎样贴到Activity上去的?我们看Gallery.java

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_ACTION_BAR);
        requestWindowFeature(Window.FEATURE_ACTION_BAR_OVERLAY);

        setContentView(R.layout.main);

        if (savedInstanceState != null) {
            getStateManager().restoreFromState(savedInstanceState);
        } else {
            initializeByIntent();
        }
    }


saveInstanceState是上次退出的状态,我们先不管,先看第一次走的initalizeByIntent()

    private void initializeByIntent() {
        Intent intent = getIntent();
        String action = intent.getAction();

        if (Intent.ACTION_GET_CONTENT.equalsIgnoreCase(action)) {
            startGetContent(intent);
        } else if (Intent.ACTION_PICK.equalsIgnoreCase(action)) {
            // We do NOT really support the PICK intent. Handle it as
            // the GET_CONTENT. However, we need to translate the type
            // in the intent here.
            Log.w(TAG, "action PICK is not supported");
            String type = Utils.ensureNotNull(intent.getType());
            if (type.startsWith("vnd.android.cursor.dir/")) {
                if (type.endsWith("/image")) intent.setType("image/*");
                if (type.endsWith("/video")) intent.setType("video/*");
            }
            startGetContent(intent);
        } else if (Intent.ACTION_VIEW.equalsIgnoreCase(action)
                || ACTION_REVIEW.equalsIgnoreCase(action)){
            startViewAction(intent);
        } else {
            startDefaultPage();
        }
    }

先不考虑intent发过来处理饿情况,没有Intent就会走最后的startDefaultPage


    public void startDefaultPage() {
        PicasaSource.showSignInReminder(this);
        Bundle data = new Bundle();
        data.putString(AlbumSetPage.KEY_MEDIA_PATH,
                getDataManager().getTopSetPath(DataManager.INCLUDE_ALL));
        getStateManager().startState(AlbumSetPage.class, data);
        mVersionCheckDialog = PicasaSource.getVersionCheckDialog(this);
        if (mVersionCheckDialog != null) {
            mVersionCheckDialog.setOnCancelListener(this);
        }
    }

可以看到第一个实例化的页面是AlbumSetPage这个类

我们看到一个页面是怎么进栈的,那么一个页面是如何出栈的呢?


    void finishState(ActivityState state) {
        // The finish() request could be rejected (only happens under Monkey),
        // If it is rejected, we won't close the last page.
        if (mStack.size() == 1) {
            Activity activity = (Activity) mContext.getAndroidContext();
            if (mResult != null) {
                activity.setResult(mResult.resultCode, mResult.resultData);
            }
            activity.finish();
            if (!activity.isFinishing()) {
                Log.w(TAG, "finish is rejected, keep the last state");
                return;
            }
            Log.v(TAG, "no more state, finish activity");
        }

        Log.v(TAG, "finishState " + state);
        if (state != mStack.peek().activityState) {
            if (state.isDestroyed()) {
                Log.d(TAG, "The state is already destroyed");
                return;
            } else {
                throw new IllegalArgumentException("The stateview to be finished"
                        + " is not at the top of the stack: " + state + ", "
                        + mStack.peek().activityState);
            }
        }

        // Remove the top state.
        mStack.pop();
        state.mIsFinishing = true;
        if (mIsResumed) state.onPause();
        mContext.getGLRoot().setContentPane(null);
        state.onDestroy();

        if (!mStack.isEmpty()) {
            // Restore the immediately previous state
            ActivityState top = mStack.peek().activityState;
            if (mIsResumed) top.resume();
        }
    }

如果是最后一个页面,不光要出栈,还要退出应用,

然后判断要出栈的是否栈顶,必须是栈顶,否则有问题了

最后才是出栈,

然后才是重新设置当前页面。

进和出我们都看到了,页面和页面之间的切换实际上是一进一出,只要调用如下接口即可,比如AlbumPage-->PhotoPage

            mActivity.getStateManager().startStateForResult(
                    PhotoPage.class, REQUEST_PHOTO, data);


主要是下面三个页面,相册集页面   相册页面     单张图片页面

    + ------------  +        + -------  +     + ----------  +

    | Fullscreen |--->   | Album |--->| AlbumSet |

    |   Photo       |          | Page  |       |   Page       |

    + ------------  +         + ------- +    +  ----------    +





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值