Apidemons-颜色矩阵控制(照片特效)

来源:互联网 发布:centos 6.9 发布日期 编辑:程序博客网 时间:2024/05/18 14:25

在一些常用的View,无法满足我们小小的心愿时,我们考虑自定义一个View 

自定义View  流程 及其简单 只要知道 View 的绘制流程 就ok ,重点 部分在于 如何实现各自的需求,如何编写自己任务的相应算法 及数据结构的组织。

下面是一个对颜色控制的小例子,apidemons-colorMatrix;

铺垫0:

引用:《Android内核剖析》第13章----View工作原理总结而成的

感谢:http://blog.csdn.net/qinjuning/article/details/7110211

 整个View树的绘图流程是在ViewRoot.java类的performTraversals()函数展开的,该函数做的执行过程可简单概况为

 根之前状态,判断是否需要重新计算视图大小(measure)、是否重新需要安置视图的位置(layout)、以及是否需要重绘

 (draw),其框架过程如下:



铺垫1:

接下来温习一下整个View树的结构,对每个具体View对象的操作,其实就是个递归的实现。

 

                   

正题:

public class MainActivity extends GraphicsActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(new SampleView(this));
    }
    private static class SampleView extends View {
        private static final String TAG = "SampleView";
private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
/**
 * 颜色矩阵 {r,g,b,a}
 * */        
        private ColorMatrix mCM = new ColorMatrix();
        private Bitmap mBitmap;
        private float mSaturation;
        private float mAngle;
        
        public SampleView(Context context) {
            super(context);
            mBitmap = BitmapFactory.decodeResource(context.getResources(),
                                                   R.drawable.balloons);
        }
        private static void setTranslate(ColorMatrix cm, float dr, float dg,
                                         float db, float da) {
            cm.set(new float[] {
                   2, 0, 0, 0, dr,
                   0, 2, 0, 0, dg,
                   0, 0, 2, 0, db,
                   0, 0, 0, 1, da });
        }
        
        private static void setContrast(ColorMatrix cm, float contrast) {
            float scale = contrast + 1.f;
               float translate = (-.5f * scale + .5f) * 255.f;
            cm.set(new float[] {
                   scale, 0, 0, 0, translate,
                   0, scale, 0, 0, translate,
                   0, 0, scale, 0, translate,
                   0, 0, 0, 1, 0 });
        }
        
        private static void setContrastTranslateOnly(ColorMatrix cm, float contrast) {
            float scale = contrast + 1.f;
               float translate = (-.5f * scale + .5f) * 255.f;
            cm.set(new float[] {
                   1, 0, 0, 0, translate,
                   0, 1, 0, 0, translate,
                   0, 0, 1, 0, translate,
                   0, 0, 0, 1, 0 });
        }
        
        private static void setContrastScaleOnly(ColorMatrix cm, float contrast) {
            float scale = contrast + 1.f;
               float translate = (-.5f * scale + .5f) * 255.f;
            cm.set(new float[] {
                   scale, 0, 0, 0, 0,
                   0, scale, 0, 0, 0,
                   0, 0, scale, 0, 0,
                   0, 0, 0, 1, 0 });
        }
        
        @Override protected void onDraw(Canvas canvas) {
        Log.i(TAG,"onDraw");
            Paint paint = mPaint;
            float x = 100;
            float y = 140;
            canvas.drawColor(Color.WHITE);
            
            paint.setColorFilter(null);
            canvas.drawBitmap(mBitmap, x, y, paint);
            
            ColorMatrix cm = new ColorMatrix();
            
            mAngle += 2;
            if (mAngle > 180) {
                mAngle = 0;
            }
            
            //convert our animated angle [-180...180] to a contrast value of [-1..1]
            float contrast = mAngle / 180.f;
            setContrast(cm, contrast);
            paint.setColorFilter(new ColorMatrixColorFilter(cm));
            canvas.drawBitmap(mBitmap, x + mBitmap.getWidth() + 10, y, paint);
            
            setContrastScaleOnly(cm, contrast);
            paint.setColorFilter(new ColorMatrixColorFilter(cm));
            canvas.drawBitmap(mBitmap, x, y + mBitmap.getHeight() + 10, paint);
            
            setContrastTranslateOnly(cm, contrast);
            paint.setColorFilter(new ColorMatrixColorFilter(cm));
            canvas.drawBitmap(mBitmap, x, y + 2*(mBitmap.getHeight() + 10),
                              paint);
            invalidate();
        }
        @Override
        protected void onLayout(boolean changed, int left, int top, int right,
        int bottom) {
        // TODO Auto-generated method stub
        Log.i(TAG,"onlayout");
        super.onLayout(changed, left, top, right, bottom);
        }
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // TODO Auto-generated method stub
        Log.i(TAG,"onMeasure");
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }
    }
}


package com.example.mycolomatrix;


import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Picture;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.view.ViewGroup.LayoutParams;
/**
 * onMeasure 属于View的方法,用来测量自己及其子元素来确定宽度和高度
 * onMeasure中必须确定 measured width and height of this view,
 * 
 *onlayout 属于ViewGroup的方法:用来为当前ViewGroup的子元素的位置和大小(大小是layout分配给本元素的显示空间大小,与本元素的大小可能不一样)
 *
 * onMeasure在onLayout之前调用
 * 设置background后,会重新调用onMeasure和onLayout
 * */
public class PictureLayout extends ViewGroup {


private String TAG="PictureLayout";
private final Picture mPicture = new Picture();


    public PictureLayout(Context context) {
        super(context);
    Log.i(TAG, "PictureLayout");
    }


    public PictureLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        Log.i(TAG, "PictureLayout2");
    }    


    @Override
    public void addView(View child) {
    Log.i(TAG, "addView1");
        if (getChildCount() > 1) {
            throw new IllegalStateException("PictureLayout can host only one direct child");
        }
        super.addView(child);
    }


    @Override
    public void addView(View child, int index) {
    Log.i(TAG, "addView2");
        if (getChildCount() > 1) {
            throw new IllegalStateException("PictureLayout can host only one direct child");
        }
        super.addView(child, index);
    }


    @Override
    public void addView(View child, LayoutParams params) {
    Log.i(TAG, "addView2p");
        if (getChildCount() > 1) {
            throw new IllegalStateException("PictureLayout can host only one direct child");
        }
        super.addView(child, params);
    }


    @Override
    public void addView(View child, int index, LayoutParams params) {
    Log.i(TAG, "addView3p");
        if (getChildCount() > 1) {
            throw new IllegalStateException("PictureLayout can host only one direct child");
        }
        super.addView(child, index, params);
    }


/**
 * a set of default layout parameters or null 
 * 设置一个默认的 布局属性 
 *  返回一组宽度为WRAP_CONTENT,高度为WRAP_CONTENT,坐标是(0,0)的布局参数
 * */    
    @Override
    protected LayoutParams generateDefaultLayoutParams() {
        Log.i(TAG, "generateDefaultLayoutParams");
        return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
    }
/**
 *测量视图以确定其内容宽度和高度。此方法被measure(int, int)调用。需要被子类重写以提供对其内容准确高效的测量。
 *约定:当重写此方法时,你必须调用setMeasuredDimension(int, int)来保存当前视图view的宽度和高度。不成功调用此方法将会导致一个IllegalStateException异常,
 *是由measure(int, int)抛出。所以调用父类的onMeasure(int, int)方法是必须的。
 *父类的实现是以背景大小为默认大小,除非MeasureSpec(测量细则)允许更大的背景。子类可以重写onMeasure(int,int)以对其内容提供更佳的尺寸。
 *如果此方法被重写,那么子类的责任是确认测量高度和测量宽度要大于视图view的最小宽度和最小高度(getSuggestedMinimumHeight() and getSuggestedMinimumWidth()),
 *使用这两个方法可以取得最小宽度和最小高度。
 *参数
                   widthMeasureSpec          强加于父节点的横向空间要求。要求是使用View.MeasureSpec进行编码。
                   heightMeasureSpec         强加于父节点的纵向空间要求。要求是使用View.MeasureSpec进行编码。
 * */    
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    Log.i(TAG, "onMeasure");
        final int count = getChildCount();
        int maxHeight = 0;
        int maxWidth = 0;
        for (int i = 0; i < count; i++) {
            final View child = getChildAt(i);
            if (child.getVisibility() != GONE) {
                measureChild(child, widthMeasureSpec, heightMeasureSpec);
                Log.i(TAG,"widthMeasureSpec"+widthMeasureSpec+"heightMeasureSpec"+heightMeasureSpec);
            }
        }
        maxWidth += getPaddingLeft() + getPaddingRight();
        maxHeight += getPaddingTop() + getPaddingBottom();
        Drawable drawable = getBackground();
        if (drawable != null) {
            maxHeight = Math.max(maxHeight, drawable.getMinimumHeight());
            maxWidth = Math.max(maxWidth, drawable.getMinimumWidth());
        }
        setMeasuredDimension(resolveSize(maxWidth, widthMeasureSpec),
                resolveSize(maxHeight, heightMeasureSpec));
    }
    
    private void drawPict(Canvas canvas, int x, int y, int w, int h,
                          float sx, float sy) {
        canvas.save();
        canvas.translate(x, y);
        canvas.clipRect(0, 0, w, h);
        canvas.scale(0.5f, 0.5f);
        canvas.scale(sx, sy, w, h);
        canvas.drawPicture(mPicture);
        canvas.restore();
    }


    @Override
    protected void dispatchDraw(Canvas canvas) {
    Log.i(TAG, "dispatchDraw");
        super.dispatchDraw(mPicture.beginRecording(getWidth(), getHeight()));
        mPicture.endRecording();
        
        int x = getWidth()/2;
        int y = getHeight()/2;
        
        if (false) {
            canvas.drawPicture(mPicture);
        } else {
            drawPict(canvas, 0, 0, x, y,  1,  1);
            drawPict(canvas, x, 0, x, y, -1,  1);
            drawPict(canvas, 0, y, x, y,  1, -1);
            drawPict(canvas, x, y, x, y, -1, -1);
        }
    }


    @Override
    public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
    Log.i(TAG, "invalidateChildInParent");
        location[0] = getLeft();
        location[1] = getTop();
        dirty.set(0, 0, getWidth(), getHeight());
        return getParent();
    }
//    在此视图view给他的每一个子元素分配大小和位置时调用。 派生类可以重写此方法并且重新安排他们子类的布局。
//    changed  这是当前视图view的一个新的大小或位置
//   l        相对于父节点的左边位置
//    t        相对于父节点的顶点位置
//    r        相对于父节点的右边位置
//    b       相对于父节点的底部位置
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
      Log.i(TAG, "onLayout");
        final int count = super.getChildCount();
        for (int i = 0; i < count; i++) {
            final View child = getChildAt(i);
            if (child.getVisibility() != GONE) {
                final int childLeft = getPaddingLeft();
                final int childTop = getPaddingTop();
                child.layout(childLeft, childTop,
                        childLeft + child.getMeasuredWidth(),
                        childTop + child.getMeasuredHeight());
            }
        }
    }
    @Override
    protected void onDraw(Canvas canvas) {
    // TODO Auto-generated method stub
    super.onDraw(canvas);
      Log.i(TAG, "onDraw");
    }
    @Override
    public void draw(Canvas canvas) {
    // TODO Auto-generated method stub
    super.draw(canvas);
    Log.i(TAG, "draw");
    }
}


package com.example.mycolomatrix;


import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;




public class GraphicsActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }


    @Override
    public void setContentView(View view) {
        if (false) { // set to true to test Picture
            ViewGroup vg = new PictureLayout(this);
            vg.addView(view);
            view = vg;
        }
        super.setContentView(view);
    }
}