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; //渲染任务
先简单标记一下,后面再细看。