【Android Gallery源码】学习笔记

AndroidManifest.xml

主程序:com.android.gallery3d.app.Gallery,其中在activity-alias中定义了其targetActivity为GalleryActivity,所以入口为GalleryActivity.java

图库

显示界面:AlbumSetPage(相册缩略图)、AlbumPage(单个相册的照片缩略图)、PhotoPage(单个相片缩略图)
界面父类:ActivityState.java
界面切换:StateManager.java
数据管理:DataManager.java

GalleryActivity.java

布局
main.xml

<include layout="@layout/gl_root_group">
<!--头部和底部的Fragment设为隐藏-->
<Fragment 
    android:id="@+id/header"/>
<Fragment
    android:id="@+id/footer"/>

gl_root_group.xml

<com.android.gallery3d.ui.GLRootView/>
<!--View,隐藏-->
<View
    android:id="@+id/gl_root_cover"/>

onCreate()

gl_root_cover是否可见
//如果是从桌面小部件或者Launcher启动的时候,设置可见
Intent intent = getIntent();
        if (intent != null
                && (intent.getBooleanExtra(EXTRA_FROM_WIDGET, false) || (intent
                        .getAction() != null && intent.getAction().equals(intent.ACTION_MAIN)))) {
            View view = findViewById(R.id.gl_root_cover);
            if (view != null) {
                view.setVisibility(View.VISIBLE);
                Log.d(TAG, "<onCreate> from widget or launcher, set gl_root_cover VISIBLE");
            }
}

通过PermissionHelper(位置:vendor/proprietary/packages/apps/Gallery2/src/com/mediatek/gallery3d/util)的checkAndRequestForGallery方法获取读写的权限,再进行判断后调用initializeByIntent()

boolean granted = PermissionHelper.checkAndRequestForGallery(this);
if (granted) {
    if (savedInstanceState != null) {
        getStateManager().restoreFromState(savedInstanceState);
    } else {
        initializeByIntent();
    }
} else {
    mSaveInstanceState = savedInstanceState;
}

initializeByIntent()

//通过intent的行为来进行判断是以何种方式打开的,从而调用对应的方法来初始化
.....
if(Intent.ACTION_GET_CONTENT.equalsIgnoreCase(action)){//获取所有本地图片
    startGetContent(intent);
}else if(Intent.ACTION_PICK.equalsIgnoreCase(action)){//获取相册中的图片}
else{
    startDefaultPage();//默认的方式启动
}
startDefaultPage()

默认方式,一般处理

PicasaSource.showSignInReminder(this);//静态内部方法,do nothing
Bundle data = new Bundle();
//KEY_MEDIA_PATH = "media-path"
//INCLUDE_ALL = 1|2(图片|视频)
//通过getTopSetPath获取的路径赋值给AlbumSetPage的KEY_MEDIA_PATH
data.putString(AlbumSetPage.KEY_MEDIA_PATH,getDataManager().getTopSetPath(DataManager.INCLUDE_ALL));
DataManager.java\getTopSetPath()

data数据的来源追踪

//根据传入的值进行判断,返回不同的路径
switch(typeBits){
    ....
    case INCLUDE_ALL:return TOP_SET_PATH;//TOP_SET_PATH = "/combo/{/local/all,/picasa/all}"
}
getStateManager().startState(AlbumSetPage.class, data);//页面切换,进入AlbumSetPage
StateManager.java\startState()
public void startState(Class<? extends ActivityState> klass,Bundle data) {
    Log.v(TAG, "startState " + klass);
    ActivityState state = null;
    try {
        state = klass.newInstance();//创建传入过来的AlbumSetPage的新实例,并赋值给state
    } catch (Exception e) {
        throw new AssertionError(e);
    }
    //stack栈来管理页面逻辑切换
    if (!mStack.isEmpty()) {//初次启动,mStack为空,初始化
        ActivityState top = getTopState();//获取栈顶的ActivityState=null
        top.transitionOnNextPause(top.getClass(), klass,
                StateTransitionAnimation.Transition.Incoming);//mNextTransition=hint=StateTransitionAnimation.Transition.Incoming
        if (mIsResumed) top.onPause();//false不执行
    }
    //什么都没处理,跳过
    UsageStatistics.onContentViewChanged(
            UsageStatistics.COMPONENT_GALLERY,
            klass.getSimpleName());
    //初始化state
    state.initialize(mActivity, data);
    mStack.push(new StateEntry(data, state));//入栈,保存的是AlbumSetPage
    state.onCreate(data, null);//执行AlbumSetPage.onCreate()
    if (mIsResumed) state.resume();//false,不执行
}
mVersionCheckDialog = PicasaSource.getVersionCheckDialog(this);//null
if (mVersionCheckDialog != null) {//不执行
    mVersionCheckDialog.setOnCancelListener(this);
}

AlbumSetPage.java

图库首页页面

onCreate()

initializeViews();
initializeData(data);//由GalleryActivity传入的数据,携带路径字符串
initializeViews()
//mActivity=AlbumSetPage
mSelectionManager = new SelectionManager(mActivity, true);
mSelectionManager.setSelectionListener(this);//设置监听,mListener!=null
//初始化SlotView并设置参数,由于FancyHelper.isFancyLayoutSupported()返回的是false,因此SlotView采用的是默认的布局DefaultLayout
mSlotView = new SlotView(mActivity, mConfig.slotViewSpec,
        FancyHelper.isFancyLayoutSupported());
//渲染器,控制mSlotView的显示
mAlbumSetView = new AlbumSetSlotRenderer(
        mActivity, mSelectionManager, mSlotView, mConfig.labelSpec,
        mConfig.placeholderColor);//实例化
mSlotView.setSlotRenderer(mAlbumSetView);//设置SlotView和渲染器的连接
mSlotView.setListener(new SlotView.SimpleListener(){//设置监听
    //根据行为进行相应的操作,onDOWN/onUP/onSingleTapUp/onLongTap
    @Override
    public void onDown(int index){
        AlbumSetPage.this.onDown(index);    
    }
    ......
}));
onDown()
//AlbumSetSlotRenderer mAlbumSetView
//调用AlbumSetSlotRenderer中的setPressedIndex方法
private void onDown(int index) {
    mAlbumSetView.setPressedIndex(index);
}
setPressedIndex()

AlbumSetSlotRenderer.java

public void setPressedIndex(int index) {
    if (mPressedIndex == index) return;
    mPressedIndex = index;
    mSlotView.invalidate();//重绘view/初始化
}
onUp()
public void onUp(boolean followedByLongPress) {
    AlbumSetPage.this.onUp(followedByLongPress);
}
AlbumSetPage.onUp()
private void onUp(boolean followedByLongPress) {
    if (followedByLongPress) {
        // Avoid showing press-up animations for long-press.
        mAlbumSetView.setPressedIndex(-1);
    } else {
        mAlbumSetView.setPressedUp();
    }
}
onSingleTapUp()
public void onSingleTapUp(int slotIndex) {
    AlbumSetPage.this.onSingleTapUp(slotIndex);
}
AlbumSetPage.onSingleTapUp()
public void onSingleTapUp(int slotIndex) {
    if (!mIsActive) return;
    if (mSelectionManager.inSelectionMode()) {
        MediaSet targetSet = mAlbumSetDataAdapter.getMediaSet(slotIndex);
        if (targetSet == null) return; // Content is dirty, we shall reload soon
        if (mRestoreSelectionDone) {
            /// M: [BUG.ADD] fix menu display abnormal @{
            if (mActionModeHandler != null) {
                mActionModeHandler.closeMenu();
            }
            /// @}
            mSelectionManager.toggle(targetSet.getPath());
            mSlotView.invalidate();
        } else {
            if (mWaitToast == null) {
                mWaitToast = Toast.makeText(mActivity, R.string.wait, Toast.LENGTH_SHORT);
            }
            mWaitToast.show();
        }
    } else {
        /// M: [BUG.ADD] check slotIndex valid or not @{
        if (!mAlbumSetDataAdapter.isActive(slotIndex)) {
            Log.d(TAG, "<onSingleTapUp> slotIndex " + slotIndex
                    + " is not active, return!");
            return;
        }
        /// @}
        // Show pressed-up animation for the single-tap.
        mAlbumSetView.setPressedIndex(slotIndex);
        mAlbumSetView.setPressedUp();
        /// M: [PERF.MODIFY] send Message without 180ms delay for improving performance. @{
        //mHandler.sendMessageDelayed(mHandler.obtainMessage(
        //        MSG_PICK_ALBUM, slotIndex, 0), FadeTexture.DURATION);
        Log.d(TAG, "onSingleTapUp() at " + System.currentTimeMillis());
        mHandler.sendMessage(mHandler.obtainMessage(MSG_PICK_ALBUM, slotIndex, 0));
        mAlbumSetView.setPressedIndex(-1);
        /// @}
    }
}
//选取后根据不同的行为进行处理的handler
mActionModeHandler = new ActionModeHandler(mActivity, mSelectionManager);
mActionModeHandler.setActionModeListener(new ActionModeListener() {
    @Override
    public boolean onActionItemClicked(MenuItem item) {
        return onItemSelected(item);
    }
    /// M: [BEHAVIOR.ADD] @{
    public boolean onPopUpItemClicked(int itemId) {
        // return if restoreSelection has done
        return mRestoreSelectionDone;
    }
    /// @}
});
//mRootPane是一个GLView实例,把创建好的SlotView添加进去
mRootPane.addComponent(mSlotView);
onItemSelected()
@Override
protected boolean onItemSelected(MenuItem item){
    Activity activity = mActivity;
    switch(item.getItemId()){
        case R.id.action_cancel://取消
        //setResult()将数据传回启动页,要在finish()之前执行
            activity.setResult(Activity.RESULT_CANCELED);
            activity.finish();
            return true;
       case R.id.action_select:
           mSelectionManager.setAutoLeaveSelectionMode(false);
           mSelectionManager.enterSelectionMode();
    }
}
SelectionManager
public void setAutoLeaveSelectionMode(boolean enable) {//当选中的项目减少到0的时候,是否会自动离开选择模式,设置为false,不会自动离开
    mAutoLeave = enable;
}
//进入选择模式
public void enterSelectionMode() {
    if (mInSelectionMode) return;//只有声明,没有初始化,不执行
    Log.d(TAG, "<enterSelectionMode>");
    mInSelectionMode = true;
    if (mListener != null) mListener.onSelectionModeChange(ENTER_SELECTION_MODE);//initializeViews时mSelectionManager进行过监听绑定,不为空,执行
}
onSelectionModeChange

定义在SelectionManager中的接口中的方法

//SelectionManager.java
public interface SelectionListener {
    public void onSelectionModeChange(int mode);
    public void onSelectionChange(Path path, boolean selected);
    /// M: [BEHAVIOR.ADD] @{
    public void onSelectionRestoreDone();
    /// @}
}

AlbumSetPage.java实现

@Override
public void onSelectionModeChange(int mode) {
    switch (mode) {
        case SelectionManager.ENTER_SELECTION_MODE: {//进入选择模式
            mActionBar.disableClusterMenu(true);
            mActionModeHandler.startActionMode();
            performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
            break;
        }
        case SelectionManager.LEAVE_SELECTION_MODE: {
            mActionModeHandler.finishActionMode();
            if (mShowClusterMenu) {
                mActionBar.enableClusterMenu(mSelectedAction, this);
            }
            mRootPane.invalidate();
            break;
        }
        /// M: [BEHAVIOR.ADD] @{
        // when click deselect all in menu, not leave selection mode
        case SelectionManager.DESELECT_ALL_MODE:
        /// @}
        case SelectionManager.SELECT_ALL_MODE: {
            mActionModeHandler.updateSupportedOperation();
            mRootPane.invalidate();
            break;
        }
    }
}
disableClusterMenu(boolean)
public void disableClusterMenu(boolean hideMenu) {
    if (mActionBar != null) {
        mClusterRunner = null;
        if (hideMenu) {
        //设置ationbar的导航模式:STANDARD(标准)、TAB(标签切换模式)、LIST(下拉列表模式)
            mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
        }
    }
}
startActionMode()

ActionModeHandler.java
进入ActionMode模式,临时占用ActionBar,并为它设置layout布局

public void startActionMode() {
    Activity a = mActivity;
    mActionMode = a.startActionMode(this);
    View customView = LayoutInflater.from(a).inflate(
            R.layout.action_mode, null);//加载action_mode视图,一个imageview和button组成,相册左上角导航栏的图标
    mActionMode.setCustomView(customView);//为ActionMode加载布局
    mSelectionMenu = new SelectionMenu(a,
            (Button) customView.findViewById(R.id.selection_menu), this);
    updateSelectionMenu();
}

SelectionMenu()
SelectionMenu.java
选择菜单的初始化

public SelectionMenu(Context context, Button button, OnPopupItemClickListener listener) {
    mContext = context;
    mButton = button;
    mPopupList = new PopupList(context, mButton);
    mPopupList.addItem(R.id.action_select_all,
            context.getString(R.string.select_all));//全选item
    mPopupList.setOnPopupItemClickListener(listener);
    mButton.setOnClickListener(this);
}

updateSelectionMenu()
ActionModeHandler.java

public void updateSelectionMenu() {
/// @}
    // update title
    int count = mSelectionManager.getSelectedCount();
    Log.d(TAG, "<updateSelectionMenu> select count is " + count);
    /// M: [BUG.MODIFY] @{
    /*
     String format = mActivity.getResources().getQuantityString(
     R.plurals.number_of_items_selected, count);
     setTitle(String.format(format, count));
    */
    /// @}
    // M: if current state is AlbumSetPage, title maybe albums/groups,
    // so getSelectedString from AlbumSetPage
    String title = null;
    ActivityState topState = null;
    // add empty state check to avoid JE
    if (mActivity.getStateManager().getStateCount() != 0) {//mStack.size()!=0,进入AlbumSetPage时入栈了
        topState = mActivity.getStateManager().getTopState();//获取顶部的ActivityState
    }
    if (topState != null && topState instanceof AlbumSetPage) {//对topState进行判断,是否为AlbumSetPage
        title = ((AlbumSetPage) topState).getSelectedString();
    } else {
        String format = mActivity.getResources().getQuantityString(
                R.plurals.number_of_items_selected, count);
        title = String.format(format, count);
    }
    setTitle(title);//设置标题
    // For clients who call SelectionManager.selectAll() directly, we need to ensure the
    // menu status is consistent with selection manager.
    mSelectionMenu.updateSelectAllMode(mSelectionManager.inSelectAllMode());
}

getSelectedCount()
SelectionManager.java

public int getSelectedCount() {
    int count = mClickedSet.size();
    if (mInverseSelection) {
        count = getTotalCount() - count;
    }
    return count;
}
initializeData
//DATA_CACHE_SIZE=256
private void initializeData(Bundle data) {
    String mediaPath = data.getString(AlbumSetPage.KEY_MEDIA_PATH);//获取媒体路径赋值给mediaPath
    mMediaSet = mActivity.getDataManager().getMediaSet(mediaPath);//通过DataManager的getMediaSet方法将字符串转为path后返回MediaObject
    mSelectionManager.setSourceMediaSet(mMediaSet);
    mAlbumSetDataAdapter = new AlbumSetDataLoader(
            mActivity, mMediaSet, DATA_CACHE_SIZE);//初始化一些数据,并添加handler对加载、结束以及运行做处理
    mAlbumSetDataAdapter.setLoadingListener(new MyLoadingListener());
    mAlbumSetView.setModel(mAlbumSetDataAdapter);//适配器与SlotView渲染器建立联系
    /// M: [FEATURE.ADD] fancy layout @{
    if (FancyHelper.isFancyLayoutSupported()) {//return ENABLE_FANCY=FeatureConfig.SUPPORT_FANCY_HOMEPAGE=false;不执行
        mAlbumSetDataAdapter.setFancyDataChangeListener((DataChangeListener) mSlotView);
        // initialize fancy thumbnail size with DisplayMetrics
        FancyHelper.initializeFancyThumbnailSizes(getDisplayMetrics());
    }
    /// @}
}
AlbumSetDataLoader(AbstractGalleryActivity,MediaSet,int)

AlbumSetDataLoader.java

public AlbumSetDataLoader(AbstractGalleryActivity activity, MediaSet albumSet, int cacheSize) {
    mSource = Utils.checkNotNull(albumSet);
    mCoverItem = new MediaItem[cacheSize];
    mData = new MediaSet[cacheSize];
    mTotalCount = new int[cacheSize];
    mItemVersion = new long[cacheSize];
    mSetVersion = new long[cacheSize];
    Arrays.fill(mItemVersion, MediaObject.INVALID_DATA_VERSION);//INVALID_VERSION=-1;
    Arrays.fill(mSetVersion, MediaObject.INVALID_DATA_VERSION);
    //处理消息----->发送者?
    mMainHandler = new SynchronizedHandler(activity.getGLRoot()) {
        @Override
        public void handleMessage(Message message) {
            switch (message.what) {
                case MSG_RUN_OBJECT://3
                    ((Runnable) message.obj).run();
                    return;
                case MSG_LOAD_START://1
                    if (mLoadingListener != null) mLoadingListener.onLoadingStarted();
                    return;
                case MSG_LOAD_FINISH://2
                    if (mLoadingListener != null) mLoadingListener.onLoadingFinished(false);
                    return;
            }
        }
    };
}
MyLoadingListener

AlbumSetPage.java内部类

private class MyLoadingListener implements LoadingListener {
    @Override
    public void onLoadingStarted() {
        /// M: [PERF.ADD] for performance auto test@{
        mLoadingFinished = false;
        /// @}
        setLoadingBit(BIT_LOADING_RELOAD);//mLoadingBits = 1
    }

    @Override
    public void onLoadingFinished(boolean loadingFailed) {
        /// M: [PERF.ADD] for performance auto test@{
        mLoadingFinished = true;
        /// @}
        clearLoadingBit(BIT_LOADING_RELOAD);
        /// M: [BEHAVIOR.ADD] @{
        // We have to notify SelectionManager about data change,
        // and this is the most proper place we could find till now
        boolean inSelectionMode = (mSelectionManager != null && mSelectionManager
                .inSelectionMode());
        int setCount = mMediaSet != null ? mMediaSet.getSubMediaSetCount() : 0;
        Log.d(TAG, "<onLoadingFinished> set count=" + setCount);
        Log.d(TAG, "<onLoadingFinished> inSelectionMode=" + inSelectionMode);
        mSelectionManager.onSourceContentChanged();
        boolean restore = false;
        if (setCount > 0 && inSelectionMode) {
            if (mNeedUpdateSelection) {
                mNeedUpdateSelection = false;
                restore = true;
                mSelectionManager.restoreSelection();
            }
            mActionModeHandler.updateSupportedOperation();
            mActionModeHandler.updateSelectionMenu();
        }
        if (!restore) {
            mRestoreSelectionDone = true;
        }
        /// @}
        /// M: [BUG.ADD] @{
        // ClusterAlbumSet name is designed to be localized, and the
        // localized name is calculated in reload(). Therefore we may obtain
        // a miss-localized name which is corresponding to the obsolete
        // (previous) Locale from ClusterAlbumSet if it is not finished
        // reloading. Here we re-get its name after it finished reloading,
        // and set the title of action bar to be the obtained name.
        if (mTitle != null && mActionBar != null && !inSelectionMode
                && (mMediaSet instanceof ClusterAlbumSet)) {
            String title = mMediaSet.getName();
            String subtitle =
                    GalleryActionBar.getClusterByTypeString(mActivity, mClusterType);
            if (!mTitle.equalsIgnoreCase(title)
                    || (subtitle != null && !subtitle.equalsIgnoreCase(mSubtitle))) {
                mTitle = title;
                mSubtitle = subtitle;
                mActionBar.setTitle(mTitle);
                mActionBar.setSubtitle(mSubtitle);
                Log.d(TAG, "<onLoadingFinished> mTitle:" + mTitle + "mSubtitle = "
                        + mSubtitle);
                mActionBar.notifyDataSetChanged();
            }
        }
        /// @}
    }
}
mHandler = new SynchronizedHandler(mActivity.getGLRoot()) {
    @Override
    public void handleMessage(Message message) {
        switch (message.what) {
            case MSG_PICK_ALBUM: {
                pickAlbum(message.arg1);//接收onSingleTapup()发送的消息,跳转至pickAlbum()进行处理
                break;
            }
            default: throw new AssertionError(message.what);
        }
    }
};
pickAlbum()
private void pickAlbum(int slotIndex) {
    if (!mIsActive) return;
    /// M: [BUG.ADD] check if slotIndex is active before getMediaSet @{
    if (!mAlbumSetDataAdapter.isActive(slotIndex)) {
        return;
    }
    /// @}
    MediaSet targetSet = mAlbumSetDataAdapter.getMediaSet(slotIndex);//相册集标识
    if (targetSet == null) return; // Content is dirty, we shall reload soon
    if (targetSet.getTotalMediaItemCount() == 0) {//提示为空
        showEmptyAlbumToast(Toast.LENGTH_SHORT);
        return;
    }
    hideEmptyAlbumToast();
    String mediaPath = targetSet.getPath().toString();//得到相册的路径
    Bundle data = new Bundle(getData());
    int[] center = new int[2];
    getSlotCenter(slotIndex, center);
    data.putIntArray(AlbumPage.KEY_SET_CENTER, center);
    if (mGetAlbum && targetSet.isLeafAlbum()) {
        Activity activity = mActivity;
        Intent result = new Intent()
                .putExtra(AlbumPicker.KEY_ALBUM_PATH, targetSet.getPath().toString());
        activity.setResult(Activity.RESULT_OK, result);
        activity.finish();
    } else if (targetSet.getSubMediaSetCount() > 0) {
        data.putString(AlbumSetPage.KEY_MEDIA_PATH, mediaPath);
        mActivity.getStateManager().startStateForResult(
                AlbumSetPage.class, REQUEST_DO_ANIMATION, data);
    } else {
        if (!mGetContent && albumShouldOpenInFilmstrip(targetSet)) {
            data.putParcelable(PhotoPage.KEY_OPEN_ANIMATION_RECT,
                    mSlotView.getSlotRect(slotIndex, mRootPane));
            data.putInt(PhotoPage.KEY_INDEX_HINT, 0);
            data.putString(PhotoPage.KEY_MEDIA_SET_PATH,
                    mediaPath);
            data.putBoolean(PhotoPage.KEY_START_IN_FILMSTRIP, true);
            data.putBoolean(PhotoPage.KEY_IN_CAMERA_ROLL, targetSet.isCameraRoll());
            /// M: [BUG.ADD] @{
            data.putString(PhotoPage.KEY_MEDIA_ITEM_PATH, null);
            /// @}
            mActivity.getStateManager().startStateForResult(
                    FilmstripPage.class, AlbumPage.REQUEST_PHOTO, data);
            /// M: [BUG.ADD] avoid show selected icon when back from album page @{
            mAlbumSetView.setPressedIndex(-1);
            /// @}
            return;
        }
        data.putString(AlbumPage.KEY_MEDIA_PATH, mediaPath);

        // We only show cluster menu in the first AlbumPage in stack
        boolean inAlbum = mActivity.getStateManager().hasStateClass(AlbumPage.class);
        data.putBoolean(AlbumPage.KEY_SHOW_CLUSTER_MENU, !inAlbum);
        mActivity.getStateManager().startStateForResult(
                AlbumPage.class, REQUEST_DO_ANIMATION, data);//进入AlbumPage
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值