您的位置:首页 > 移动开发 > Android开发

Android开发之图片处理框架(三)

2016-12-04 23:49 357 查看
这套框架终于到了完结篇了,这篇就算是整个框架的重点了,涉及的不仅是重做和撤销功能,更重要的是牵扯整个框架的架构。



之前我们实现的功能是添加图片和用画笔绘画,我们的做法是在一个自定义的PaintLayout上添加一个类似于自定义画布那样的去盛放画笔画出来的东西,再在PaintLayout上添加图片,这样做的不好之处在于各个类之间的功能不明确,现在把项目重新架构成把图片全部集成到另外一个自定义的ViewLayout上,把ViewLayout加载到PaintLayout上,再放上盛放画笔画出的View,也就是TopLayer,在PaintLayout上用两个链表去实现重做和撤销操作。

我们先看一下ViewLayout的实现。

这里的ViewLayout其实就是把第一节的PaintLayout稍微改改,但是在这里得通知外层修改链表,所有还得定义个接口,这里就直接贴代码了

public class ViewLayout extends FrameLayout{

public interface ViewListener{
void addView(PaintView paintView);
void deleteView(PaintView paintView);
}

private Context context;
private LayoutParams params;
private ViewListener viewListener;
private boolean canInit=false;

public ViewLayout(Context context,ViewListener viewListener) {
super(context);
this.context=context;
this.viewListener=viewListener;
params = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
}

private void addView(MotionEvent event){
PaintView paintView=new PaintView(context, (int) event.getX(), (int) event.getY(), new PaintView.OnEnableListener() {

@Override
public
4000
void callback(PaintView stampView) {
for (int i = 0; i < ViewLayout.this.getChildCount(); ++i) {
if (ViewLayout.this.getChildAt(i) != stampView) {
((PaintView) ViewLayout.this.getChildAt(i)).setViewEnable(false);
}
}
}

@Override
public void onDelete(PaintView stampView) {
stampView.setVisibility(GONE);
viewListener.deleteView(stampView);
}

});
this.addView(paintView,params);
viewListener.addView(paintView);
}

@Override
public boolean onTouchEvent(MotionEvent event) {
if(canInit&&event.getAction()==MotionEvent.ACTION_DOWN){
addView(event);
canInit=false;
}
return super.onTouchEvent(event);
}

public void setCanInit(boolean canInit) {
this.canInit = canInit;
}

public void deleteView(PaintView paintView){
ViewLayout.this.removeView(paintView);
}

public void allStop(){
for (int i = 0; i < ViewLayout.this.getChildCount(); ++i) {
((PaintView) ViewLayout.this.getChildAt(i)).setViewEnable(false);
}
}

}


再去修改TopLayer里的代码,我们需要把每次一次从手指触摸到屏幕到手指抬起之间画出来的Path保存起来,同样的,我们在接口中把这个path传到上层的PaintLayout,而这个Path在撤销和重做的时候是通过一个boolean值去判断是否显示,所以自定义一个ViPath类

public class ViPath {

private Path path;
private boolean visible;

public ViPath(){
visible=true;
path=new Path();
}

public void moveTo(float x,float y){
path.moveTo(x,y);
}

public void lineTo(float x,float y){
path.lineTo(x,y);
}

public Path getPath() {
return path;
}

public void setPath(Path path) {
this.path = path;
}

public boolean isVisible() {
return visible;
}

public void setVisible(boolean visible) {
this.visible = visible;
}

}


public interface PathListener{
void addPath(ViPath viPath);
}
private ViPath currentPath;
private PathListener pathListener;

private void touch_end(float eventX, float eventY){
touchPath.lineTo(eventX, eventY);
currentPath.lineTo(eventX,eventY);
}

@Override
public boolean onTouchEvent(MotionEvent event) {
if (enable) {
float eventX = event.getX();
float eventY = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
touch_start(eventX, eventY);
currentPath=new ViPath();
currentPath.moveTo(eventX,eventY);
touchPoints = 0;
break;
case MotionEvent.ACTION_MOVE:
touchPoints++;
if ((touchPoints % 3) == 0){
touch_end(eventX, eventY);
start_paint(touchPath, paint);
touch_start(eventX, eventY);
}
break;
case MotionEvent.ACTION_UP:
touch_end(eventX, eventY);
start_paint(touchPath, paint);
pathListener.addPath(currentPath);
break;
default:
break;
}
invalidate();
return true;
} else {
return false;
}
}


在这里还得写一个函数供外层调用,就是当重做和撤销的时候,重绘整个TopLayer

public void drawPath(List<Object> list){
layerBitmap = Bitmap.createBitmap(layerWidth, layerHeight, Bitmap.Config.ARGB_4444);
layerCanvas = new Canvas(layerBitmap);
for(Object o:list){
if(o instanceof ViPath){
ViPath viPath=(ViPath)o;
if(viPath.isVisible()){
layerCanvas.drawPath(viPath.getPath(),paint);
}
}
}
invalidate();
}


现在TopLayer和ViewLayout都有了,我们需要做的就是在主布局中去调用,这里的做法是定义两个盛放Object的链表saveOperate和restoreOperate,当做撤销操作的时候,将saveOperate的尾节点放到restoreOperate里面,并根据它是不同的类型进行响应,当做重做操作的时候,把saveOperate的尾节点放到restoreOperate里面,而一旦进行新的操作,比如添加图片或者绘图之类的,将restoreOperate链表清空,并释放相关资源

public class PaintLayout extends FrameLayout{

private TopLayer topLayer;
private ViewLayout viewLayout;
private LayoutParams params;
private List<Object> saveOperate;
private List<Object> restoreOperate;

public PaintLayout(Context context) {
this(context,null);
}

public PaintLayout(Context context, AttributeSet attrs) {
super(context, attrs);
saveOperate=new ArrayList<>();
restoreOperate=new ArrayList<>();
viewLayout=new ViewLayout(context, new ViewLayout.ViewListener() {

@Override
public void addView(PaintView paintView) {
saveOperate.add(paintView);
restoreClear();
}

@Override
public void deleteView(PaintView paintView) {
for(int i=0;i<saveOperate.size();++i){
if(saveOperate.get(i)==paintView){
saveOperate.remove(i);
}
}
}

});
topLayer=new TopLayer(context, new TopLayer.PathListener() {

@Override
public void addPath(ViPath viPath) {
saveOperate.add(viPath);
restoreClear();
}

});
params = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
this.addView(viewLayout);
this.addView(topLayer,params);
}

public void setCanInit(boolean canInit) {
viewLayout.setCanInit(canInit);
}

public boolean paintToggle(){
boolean b=topLayer.paintToggle();
if(b){
viewLayout.allStop();
}
return b;
}

private void restoreClear(){
for(Object o:restoreOperate){
if(o instanceof PaintView){
viewLayout.deleteView((PaintView)o);
}
}
restoreOperate.clear();
}

public void undo(){
Object o=saveOperate.remove(saveOperate.size()-1);
restoreOperate.add(o);
if(o instanceof PaintView){
((PaintView) o).setVisibility(GONE);
}else{
((ViPath)o).setVisible(false);
topLayer.drawPath(saveOperate);
}
}

public void redo(){
Object o=restoreOperate.remove(restoreOperate.size()-1);
saveOperate.add(o);
if(o instanceof PaintView){
((PaintView) o).setVisibility(VISIBLE);
}else{
((ViPath)o).setVisible(true);
topLayer.drawPath(saveOperate);
}
}

public boolean canUndo(){
if(saveOperate.size()==0){
return false;
}else{
return true;
}
}

public boolean canRedo(){
if(restoreOperate.size()==0){
return false;
}else{
return true;
}
}

}


现在只要稍微修改主Activity里面的代码就可以了

private Button undoBtn;
private Button redoBtn;
redoBtn.setOnClickListener(new View.OnClickListener() {

@Override
public void onClick(View view) {
if(paintLayout.canRedo()){
paintLayout.redo();
}else{
tips("不可重做");
}
}

});
undoBtn.setOnClickListener(new View.OnClickListener() {

@Override
public void onClick(View view) {
if(paintLayout.canUndo()){
paintLayout.undo();
}else{
tips("不可撤销");
}
}

});


这套框架可以解决我们公司对于图片处理这块的需求,而且代码之间耦合性很低,独立拆分开去用没有毛病。

Demo在Git上的地址

https://github.com/coderCjm/CustomView.git

(完)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: