Gallery2源码阅读图片编辑

Gallery2源码阅读图片编辑

以下是从Gallery2的入口类FilterShowActivity (图片编辑功能)顺序的阅读,对看到的内容进行了一个简单的整理和记录。其中包括FilterShowActivity在创建时主要做的事情,界面的变换,历史记录的管理以及ProcessingService的功能。目前看到Gallery2的图片编辑基于renderscript实现,随后再做详细整理。

一,FilterShowActivity分析

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

        //是否只显示竖屏
        boolean onlyUsePortrait = getResources().getBoolean(R.bool.only_use_portrait);
        if (onlyUsePortrait) {
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
        }


        clearGalleryBitmapPool();
        doBindService();
        getWindow().setBackgroundDrawable(new ColorDrawable(Color.GRAY));
        setContentView(R.layout.filtershow_splashscreen);
    }

在OnCreate()方法里面会做下面几件事,
- clearGalleryBitmapPool() 清理GalleryBitmapPool中缓存的图片
- doBindService() 绑定一个图片操作的数组
- setContentView(R.layout.filtershow_splashscreen) 进入图片编辑页面时有一个圆形进度条的闪屏页面

doBindService()主要是去bind ProcessingService,用于在service里面处理一些图片的操作

    //bindService后的一些操作
    public void updateUIAfterServiceStarted() {
        MasterImage.setMaster(mMasterImage);
        ImageFilter.setActivityForMemoryToasts(this);
        mUserPresetsManager = new UserPresetsManager(this);
        mUserPresetsAdapter = new UserPresetsAdapter(this);

        setupMasterImage();  //处理MasterImage对象
        setupMenu();  //处理一些菜单 
        setDefaultValues();   //设置默认参数
        fillEditors();   //填充Editor
        getWindow().setBackgroundDrawable(new ColorDrawable(0));
        loadXML();    //出现设置视图

        fillCategories();   //设置编辑种类
        loadMainPanel();    //加载底部的面板
        extractXMPData();    
        processIntent();    //处理具体的Intent 区分是选择图片 还是加载图片
    }

setupMasterImage()中主要做了一些MasterImage初始化的操作,比如设置HistoryManager,设置StateAdapter。
setupMenu()是初始化菜单,比如undoItem,redoItem,resetItem ,printItem。
fillEditors()主要是添加一些Editor,比如EditorGrad,EditorRedEye,EditorCrop等等。
loadXML()将闪屏视图替换为新的编辑视图

        fillLooks();    //填充滤镜
        loadUserPresets();  //填充用户自定义
        fillBorders();  //填充边框
        fillTools();    //填充工具
        fillEffects();  //填充效果
        fillVersions(); //填充版本

fillCategories()是填充底部面板的一些操作。
loadMainPanel()就是用MainFragment替换底部的面板。

二,关于View的变换

整个图片显示都是基于自定义的ImageShow,和一开始想象的不太一样,以为图片编辑会用GLSurfaceView来显示,用OpenGl ES来做渲染,后来发现在编辑模块都是基于ImageShow,而这个控件是继承View。图片的滤镜等效果处理也是采用RenderScript,将处理好的图片通过ImageCache缓存起来,View只是负责单方面的显示。

ImageShow相关类的关系如下:
这里写图片描述
对于不同的滤镜或者特效处理,需要加载不同的Editor。

Editor

    protected View mView;  //topView
    protected ImageShow mImageShow;   //展示Image的View

EditorCrop中

    public EditorCrop() {
        super(ID);
        mChangesGeometry = true;
    }

    @Override
    public void createEditor(Context context, FrameLayout frameLayout) {
        super.createEditor(context, frameLayout);
        if (mImageCrop == null) {
            mImageCrop = new ImageCrop(context);
        }
        mView = mImageShow = mImageCrop;
        mImageCrop.setEditor(this);
    }

在EditorCrop中mView和mImageShow会被替换成ImageCrop。
而在EditorPlaceHolder会根据Editor进行相关控件的隐藏和显示,

/**
 * Editor 容器类
 */
public class EditorPlaceHolder {
    private static final String LOGTAG = "EditorPlaceHolder";

    private FilterShowActivity mActivity = null;
    private FrameLayout mContainer = null;
    private HashMap<Integer, Editor> mEditors = new HashMap<Integer, Editor>();
    private Vector<ImageShow> mOldViews = new Vector<ImageShow>();

    public EditorPlaceHolder(FilterShowActivity activity) {
        mActivity = activity;
    }

    public void setContainer(FrameLayout container) {
        mContainer = container;
    }

    public void addEditor(Editor c) {
        mEditors.put(c.getID(), c);
    }

    public boolean contains(int type) {
        if (mEditors.get(type) != null) {
            return true;
        }
        return false;
    }

    public Editor showEditor(int type) {
        Editor editor = mEditors.get(type);
        if (editor == null) {
            return null;
        }

        editor.createEditor(mActivity, mContainer);
        editor.getImageShow().attach();
        mContainer.setVisibility(View.VISIBLE);
        mContainer.removeAllViews();
        View eview = editor.getTopLevelView();
        ViewParent parent = eview.getParent();

        if (parent != null && parent instanceof FrameLayout) {
            ((FrameLayout) parent).removeAllViews();
        }

        mContainer.addView(eview);
        hideOldViews();
        editor.setVisibility(View.VISIBLE);
        return editor;
    }

    public void setOldViews(Vector<ImageShow> views) {
        mOldViews = views;
    }

    public void hide() {
        mContainer.setVisibility(View.GONE);
    }

    public void hideOldViews() {
        for (View view : mOldViews) {
            view.setVisibility(View.GONE);
        }
    }

    public Editor getEditor(int editorId) {
        return mEditors.get(editorId);
    }
}

整个视图切换的流程就是需要不同的编辑效果,只需要替换不同的Editor,然后先移除ContainerView中的子view,隐藏oldViews,显示当前的editor即可。

三,HistoryManager

图片编辑中有一个撤销和恢复的操作,

    private Vector<HistoryItem> mHistoryItems = new Vector<HistoryItem>();  //逆序排列

原理就是维护一个HistoryItem的Vector,不过这里记录是逆序排列的,也就是最近的记录是在最前面。


    private void insert(HistoryItem preset, int position) {
        if (mCurrentPresetPosition != 0) {
            // in this case, let's discount the presets before the current one
            //这里进行判断,因为每次mCurrentPresetPosition之前的记录是无效的,所以需要舍弃掉。 有可能进行撤销操作了,之前的记录就不再需要了
            Vector<HistoryItem> oldItems = new Vector<HistoryItem>();
            for (int i = mCurrentPresetPosition; i < getCount(); i++) {
                oldItems.add(getItem(i));
            }
            clear();
            for (int i = 0; i < oldItems.size(); i++) {
                add(oldItems.elementAt(i));
            }
            mCurrentPresetPosition = position;
            notifyDataSetChanged();
        }
        mHistoryItems.insertElementAt(preset, position);
        mCurrentPresetPosition = position;
        notifyDataSetChanged();
    }

具体的撤销操作就是mCurrentPresetPosition指针加1,恢复就是减1。


    public int redo() {
        mCurrentPresetPosition--;
        if (mCurrentPresetPosition < 0) {
            mCurrentPresetPosition = 0;
        }
        notifyDataSetChanged();
        updateMenuItems();
        return mCurrentPresetPosition;
    }

    public int undo() {
        mCurrentPresetPosition++;
        if (mCurrentPresetPosition >= getCount()) {
            mCurrentPresetPosition = getCount() - 1;
        }
        notifyDataSetChanged();
        updateMenuItems();
        return mCurrentPresetPosition;
    }

但是需要注意边界条件的控制。

具体的一个HistoryItem主要包含三个对象,

    private ImagePreset mImagePreset;
    private FilterRepresentation mFilterRepresentation;
    private Bitmap mPreviewImage;

mImagePreset包含了一系列FilterRepresentation的叠加,维护了一个mFilterRepresentation的数组,也就是所有用过的效果参数保存起来,mFilterRepresentation就是当前效果的一些参数的保存。mPreviewImage好像没有用到。也就是说HistoryItem不保存具体的Bitamp对象,后面可以看到处理后的Bitmap缓存在BitmapCache里面。

四,ProcessingService

有这么几个重要的类

    private ProcessingTaskController mProcessingTaskController;  //装载任务的容器
    private ImageSavingTask mImageSavingTask;  //保存图片 任务
    private UpdatePreviewTask mUpdatePreviewTask;  //更新 预览图任务
    private HighresRenderingRequestTask mHighresRenderingRequestTask;  //目前还没看到
    private FullresRenderingRequestTask mFullresRenderingRequestTask;   //目前还没看到
    private RenderingRequestTask mRenderingRequestTask;   //渲染任务

先简单标记一下,后面再细看。

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值