Android绘图机制与处理技巧

参考资料


目录

  • 1)屏幕尺寸信息
  • 2)2D绘图基础
  • 3)XML绘图
  • 4)绘图技巧
  • 5)图像处理-色彩特效处理
  • 6)图像处理-图形特效处理
  • 7)图像处理-画笔特效处理
  • 8)SurfaceView

1)屏幕尺寸信息

ldpi mdpi hdpi xhdpi xxhdpi
120 160 240 320 480
240x320 320x480 480x800 720x1280 1080x1920
1dp=1px 1dp=1.5px 1dp=2px 1dp=3px
//单位转换工具
public class DisplayUtil {
    public static int px2dip(Context context, float pxValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (pxValue / scale + 0.5f);
    }

    public static int dip2px(Context context, float dipValue) {
//        final float scale = context.getResources().getDisplayMetrics().density;
//        return (int) (dipValue * scale + 0.5f);
        //或者使用系统的TypedValue类来转换
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dipValue, context.getResources().getDisplayMetrics());
    }

    public static int px2sp(Context context, float pxValue) {
        final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
        return (int) (pxValue / fontScale + 0.5f);
    }

    public static int sp2px(Context context, float spValue) {
        final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
        return (int) (spValue * fontScale + 0.5f);
    }
}

TypedValue applyDimension源码

  public static float applyDimension(int unit, float value,  
                                     DisplayMetrics metrics)  
  {  
      switch (unit) {  
      case COMPLEX_UNIT_PX:  
          return value;  
      case COMPLEX_UNIT_DIP:  
          return value * metrics.density;  
      case COMPLEX_UNIT_SP:  
          return value * metrics.scaledDensity;  
      case COMPLEX_UNIT_PT:  
          return value * metrics.xdpi * (1.0f/72);  
      case COMPLEX_UNIT_IN:  
          return value * metrics.xdpi;  
      case COMPLEX_UNIT_MM:  
          return value * metrics.xdpi * (1.0f/25.4f);  
      }  
      return 0;  
  }  

2)2D绘图基础(参见自定义View)


3)XML绘图

  • Bitmap,这样引用可以将图片直接转成Bitmap使用


  • Shape
    新建文件res/drawable/shape_test.xml

   
     //使用LevelListDrawable时就要设置为true。设为false时才有渐变效果  
      />
  
        //虚线的间隔  

//下面2个一般不怎么用,因为他们的功能控件本身也有
    
    

  • Layer图层

  
  

  • Selector可实现静态绘图的事件反馈,可针对事件设置不同图像。Selector中也可以使用Shape作为它的Item。

    
      
    
     
    
    
    
     
    
     
    
    


4)绘图技巧(参见自定义View)


5)图像处理-色彩特效处理

  • ColorMatrix

Android对于图片的处理,常使用数据结构是位图,即Bitmap,它包含了图片所有的数据。整个图片是由点阵和颜色值组成。每个点阵就是一个包含像素的矩阵。

ColorMatrix
public class MainActivity extends AppCompatActivity implements SeekBar.OnSeekBarChangeListener{
    private static final String TAG = "MainActivity";
    private ImageView img;
    private Bitmap bm;
    private int mHue=1,mSaturation=1,mLum=1;
    private SeekBar seek_hue,seek_saturation,seek_lum;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        seek_hue = (SeekBar) findViewById(R.id.seek_hue);
        seek_saturation = (SeekBar) findViewById(R.id.seek_saturation);
        seek_lum = (SeekBar) findViewById(R.id.seek_lum);
        seek_hue.setOnSeekBarChangeListener(this);
        seek_saturation.setOnSeekBarChangeListener(this);
        seek_lum.setOnSeekBarChangeListener(this);

        bm = BitmapFactory.decodeResource(getResources(),R.drawable.def);
        img = (ImageView) findViewById(R.id.img);
    }

    private Bitmap handleImageEffect(Bitmap bm,float hue,float saturation,float lum){
        //原始图片是不能修改的,可根据原始图片生成新图进行修改
        Bitmap bmp = Bitmap.createBitmap(bm.getWidth(),bm.getHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bmp);
        Paint paint = new Paint();

        //色调
        ColorMatrix hueMatrix = new ColorMatrix();
        hueMatrix.setRotate(0,hue);
        hueMatrix.setRotate(1,hue);
        hueMatrix.setRotate(2,hue);

        //保饱和度
        ColorMatrix saturationMatrix = new ColorMatrix();
        saturationMatrix.setSaturation(saturation);

        //亮度
        ColorMatrix lumMatrix = new ColorMatrix();
        lumMatrix.setScale(lum,lum,lum,1);

        ColorMatrix imageMatrix = new ColorMatrix();
        imageMatrix.postConcat(hueMatrix);
        imageMatrix.postConcat(saturationMatrix);
        imageMatrix.postConcat(lumMatrix);

        paint.setColorFilter(new ColorMatrixColorFilter(imageMatrix));
        canvas.drawBitmap(bm,0,0,paint);
        return bmp;
    }

    @Override
    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
        Log.i(TAG, "============================ ");
        switch (seekBar.getId()){
            case R.id.seek_hue:
                mHue = progress;
                break;
            case R.id.seek_saturation:
                mSaturation = progress;
                break;
            case R.id.seek_lum:
                mLum = progress;
                break;
        }
        Log.i(TAG, "mHue= "+mHue+" mSaturation= "+mSaturation+" mLum= "+mLum);
        img.setImageBitmap(handleImageEffect(bm,mHue,mSaturation,mLum));
    }

    @Override
    public void onStartTrackingTouch(SeekBar seekBar) {
    }

    @Override
    public void onStopTrackingTouch(SeekBar seekBar) {
    }
}
  • 颜色矩阵

通过调整颜色矩阵可以改变一副图像的色彩效果,图像处理很大程度上就是寻找处理图像的颜色矩阵,除了使用系统提供的ColorMatrix,也可以精确的修改矩阵值来实现颜色效果处理。
4x5 最右侧为偏移量,左侧4x4为颜色系数RGBA


Android绘图机制与处理技巧_第1张图片
颜色矩阵
public class Main2Activity extends AppCompatActivity implements View.OnClickListener {
    private static final String TAG = "Main2Activity";
    private GridLayout mGroup;
    private EditText[] edts = new EditText[20];
    private Bitmap bitmap;
    private float[] mColorMatrix = new float[20];
    private ImageView img;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);
        bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.def);
        img = (ImageView) findViewById(R.id.img);
        img.setImageBitmap(bitmap);
        mGroup = (GridLayout) findViewById(R.id.group);

        Button btn1 = (Button) findViewById(R.id.btn1);
        Button btn2 = (Button) findViewById(R.id.btn2);
        btn1.setOnClickListener(this);
        btn2.setOnClickListener(this);

        initEdts();
        initMatrix();
    }

    //初始化EditText
    private void initEdts(){
        Log.i(TAG, "mGroup.getHeight(): "+mGroup.getHeight());
        for (int i=0;i<20;i++){
            EditText editText = new EditText(this);
            edts[i] = editText;
            mGroup.addView(editText,getResources().getDisplayMetrics().widthPixels/5,100);
        }
    }

    //初始化矩阵值
    private void initMatrix(){
        for (int i=0; i<20; i++){
            if (i%6 == 0){
                edts[i].setText("1");
            }else {
                edts[i].setText("0");
            }
        }
    }

    //获取矩阵值
    private void getMatrix(){
        for (int i=0;i<20;i++){
            mColorMatrix[i] = Float.parseFloat(edts[i].getText().toString());
        }
    }

    //将矩阵设置到图像
    private void setImageMatrix(){
        //原始图片是不能修改的,可根据原始图片生成新图进行修改
        Bitmap bmp = Bitmap.createBitmap(bitmap.getWidth(),bitmap.getHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bmp);
        Paint paint = new Paint();
        paint.setColorFilter(new ColorMatrixColorFilter(mColorMatrix));
        canvas.drawBitmap(bitmap,0,0,paint);
        img.setImageBitmap(bmp);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            //设置值
            case R.id.btn1:
                getMatrix();
                setImageMatrix();
                break;
            //重置
            case R.id.btn2:
                initMatrix();
                getMatrix();
                setImageMatrix();
                break;
        }
    }
}
  • 常用图像颜色矩阵处理效果

  • 灰度效果


    Android绘图机制与处理技巧_第2张图片
    灰度效果
  • 图像反转


    Android绘图机制与处理技巧_第3张图片
    图像反转
  • 怀旧效果


    Android绘图机制与处理技巧_第4张图片
    怀旧效果
  • 去色效果


    Android绘图机制与处理技巧_第5张图片
    去色效果
  • 高饱和度


    Android绘图机制与处理技巧_第6张图片
    高饱和度
  • 像素点分析

Android中还提供了Bitmao.getPixels()方法来帮我们提取整个Bitmap的像素点。

Android绘图机制与处理技巧_第7张图片
底片效果

Android绘图机制与处理技巧_第8张图片
怀旧效果

Android绘图机制与处理技巧_第9张图片
浮雕效果
public class PixelsActivity extends AppCompatActivity {

    private  Bitmap bitmap;
    private ImageView img;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_pixels);
        bitmap= BitmapFactory.decodeResource(getResources(),R.drawable.def);
        img = (ImageView) findViewById(R.id.img);

        handleImage();
    }

    private void handleImage(){
        //创建临时bitmap
        Bitmap bmp = Bitmap.createBitmap(bitmap.getWidth(),bitmap.getHeight(), Bitmap.Config.ARGB_8888);

        int[] oldPx = new int[bitmap.getWidth()*bitmap.getHeight()];
        int[] newPx = new int[bitmap.getWidth()*bitmap.getHeight()];

        //pixels - 接收位图颜色值的数组
        //offset - 写入到pixels[]中的第一个像素索引值
        //stride - pixels[]中的行间距
        // x     - 从位图中读取的第一个像素x坐标值
        // y     - 从位图中读取的第一个像素y坐标值
        //width  - 从每一行中读取的像素宽度
        //height - 读取的行数
        bitmap.getPixels(oldPx,0,bitmap.getWidth(),0,0,bitmap.getWidth(),bitmap.getHeight());

        for (int i=1;i255){
                r=255;
            }else if (r<0){
                r=0;
            }
            if (g>255){
                g=255;
            }else if (g<0){
                g=0;
            }
            if (b>255){
                b=255;
            }else if (b<0){
                b=0;
            }
            newPx[i] = Color.argb(a,r,g,b);
        }
        bmp.setPixels(newPx,0,bitmap.getWidth(),0,0,bitmap.getWidth(),bitmap.getHeight());
        img.setImageBitmap(bmp);
    }
}

6)图像处理-图形特效处理

7)图像处理-画笔特效处理

  • PorterDuffXfermode
    PorterDuffXfermode设置的是两个图层交集区域的显示方式,dst是先画的图形,而src为后画的图形。
    先用一个普通画笔画一个mask遮罩层,再用带PorterDuffXfermode的画笔将图像花在遮罩层上。
  1. 例:使用PorterDuffXfermode实现的圆头像


    Android绘图机制与处理技巧_第10张图片
    PorterDuff.Mode.SRC_IN
        ImageView img = (ImageView) findViewById(R.id.img);
        Bitmap mBitmap = BitmapFactory.decodeResource(getResources(),R.drawable.def);
        Bitmap bmp = Bitmap.createBitmap(mBitmap.getWidth(),mBitmap.getHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bmp);
        Paint mPaint = new Paint();
        mPaint.setAntiAlias(true);

        RectF rectF = new RectF(0,0,mBitmap.getWidth(),mBitmap.getHeight());
        //画圆
        canvas.drawRoundRect(rectF,mBitmap.getWidth()/2,mBitmap.getHeight()/2,mPaint);

        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        canvas.drawBitmap(mBitmap,0,0,mPaint);

        img.setImageBitmap(bmp);

还有其他类型可以自己尝试:

SRC,SRC_IN,SRC_OVER,SRC_OUT,SRC_ATOP
DST,DST_IN,DST_OVER,DST_OUT,DST_ATOP
XOR,DARKEN,LIGHTEN,MULTIPLY,SCREEN

2)例:实现刮刮卡效果


刮刮卡
public class XfermodeView extends View {
    private Paint mPaint;
    private Canvas canvas;
    private Bitmap mBgBitmap, mFgBitmap;
    private Path mPath;
    public XfermodeView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private void init() {
        mPaint = new Paint();

        //重要! 用于绘制透明path
        mPaint.setAlpha(0);
        //遮罩层类型
        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));

        mPaint.setStrokeWidth(50);
        mPaint.setStrokeCap(Paint.Cap.ROUND); //笔触风格
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeJoin(Paint.Join.ROUND); // 接合处形态

        mBgBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.def);
        mFgBitmap = Bitmap.createBitmap(mBgBitmap.getWidth(), mBgBitmap.getHeight(), Bitmap.Config.ARGB_8888);
        canvas = new Canvas(mFgBitmap);
        canvas.drawColor(Color.GRAY);

        mPath = new Path();
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                mPath.moveTo(event.getX(),event.getY());
                break;
            case MotionEvent.ACTION_MOVE:
                mPath.lineTo(event.getX(),event.getY());
                break;
        }

        //其实就是绘制透明路径, 因mPaint设置的Alpha为0
        canvas.drawPath(mPath,mPaint);
        invalidate();
        return true;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawBitmap(mBgBitmap,0,0,null);
        canvas.drawBitmap(mFgBitmap,0,0,null);
    }
}
  • Shader
说明
BitmapShader 位图
LinearGradient 线性
RadialGradient 光束
SweepGradient 梯度
ComposeShader 混合
Shader.TileMode 说明
Shader.TileMode.CLAMP 拉伸。的是图片最后的那一个元素,不断重复
REPEAT 重复。横向、纵向不断重复
MIRROR 镜像。横向不断翻转重复,纵向不断翻转重复

1)例:线性渐变着色器


线性渐变着色器
public class MyTextView1 extends TextView {
    private Paint mPaint = new Paint();
    private int mViewWidth;
    private LinearGradient mLinearGradient;
    private Matrix mGradientMatrix;
    private int mTranslate;
    public MyTextView1(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        if (mViewWidth == 0){
            mViewWidth = getMeasuredWidth();
            if (mViewWidth > 0){
                //重要!获取当前绘制用的Paint对象
                mPaint = getPaint();
                //线性渐变着色器
                mLinearGradient = new LinearGradient(0,0,mViewWidth,0,new int[]{Color.RED,Color.GREEN,Color.CYAN},null, Shader.TileMode.CLAMP);
                mPaint.setShader(mLinearGradient);
                mGradientMatrix= new Matrix();
            }
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (mGradientMatrix != null){
            mTranslate += mViewWidth/5;
            if (mTranslate >2*mViewWidth){
                mTranslate = -mViewWidth;
            }
            mGradientMatrix.setTranslate(mTranslate,0);
            mLinearGradient.setLocalMatrix(mGradientMatrix);
            postInvalidateDelayed(100);
        }
    }
}

2)例:使用Shader实现圆形头像


Android绘图机制与处理技巧_第11张图片
BitmapShader
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.def);
Bitmap bmp = Bitmap.createBitmap(bitmap.getWidth(),bitmap.getHeight(), Bitmap.Config.ARGB_8888);
BitmapShader bitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP,Shader.TileMode.CLAMP);
Paint mPaint = new Paint();
mPaint.setShader(bitmapShader);
canvas.drawCircle(100,100,100,mPaint);

8)SurfaceView

View通过刷新来重绘视图,刷新的间隔为16ms,如果在16ms内完成了需要操作的逻辑,则用户在视觉上不会产生卡顿的感觉。但逻辑复杂会卡顿,阻塞主线程。

View SurfaceView
适用主动刷新 适用被动更新
主线程中刷新 子线程中刷新
无双缓冲 有双缓冲

若自定义View需要频繁刷新,或刷新时数据量大,可以用SurfaceView取代View。
Android 屏幕刷新机制
android屏幕刷新显示机制

  • 例1)正弦曲线


    Android绘图机制与处理技巧_第12张图片
    正弦曲线
public class SurfaceViewDemo extends SurfaceView implements SurfaceHolder.Callback,Runnable {
    private int x;
    private int y;
    private SurfaceHolder mHolder;
    private Canvas mCanvas;
    private boolean mIsDrawing; //控制子线程
    private Path mPath;
    private Paint mPaint;

    public SurfaceViewDemo(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    //初始化
    private void init(){
        mHolder = getHolder(); //初始化SurfaceHolder
        mHolder.addCallback(this); //注册SurfaceHolder的回调方法
        mPath = new Path();
        mPaint = new Paint();
        mPaint.setColor(Color.BLUE);
        mPaint.setStyle(Paint.Style.STROKE);
    }

    //开启子线程进行绘制
    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        mIsDrawing = true;
        new Thread(this).start();
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        mIsDrawing = false;
    }

    @Override
    public void run() {
        //子线程中通过while不断绘制
        while (mIsDrawing){
            draw();
            x += 1;
            y = (int) (Math.sin(x*2*Math.PI/180)*100)+400;
            mPath.lineTo(x,y);
        }
    }

    private void draw(){
        try {
            mCanvas = mHolder.lockCanvas(); //获得canvas对象
            mCanvas.drawColor(Color.WHITE);
            mCanvas.drawPath(mPath,mPaint);
        }catch (Exception e){

        }finally {
            if (mCanvas != null){
                mHolder.unlockCanvasAndPost(mCanvas); //提交画布内容
            }
        }
    }
}
  • 例2)绘图板


    Android绘图机制与处理技巧_第13张图片
    绘图板
public class SurfaceViewDemo2 extends SurfaceView implements SurfaceHolder.Callback,Runnable {
    private static final String TAG = "SurfaceViewDemo2";
    private SurfaceHolder mHolder;
    private Canvas mCanvas;
    private boolean mIsDrawing;
    private Path mPath;
    private Paint mPaint;
    public SurfaceViewDemo2(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private void init(){
        mHolder = getHolder();
        mHolder.addCallback(this);
        mPath = new Path();
        mPaint = new Paint();
        mPaint.setColor(Color.BLUE);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(6);
//        setFocusable(true);
//        setFocusableInTouchMode(true);
//        this.setKeepScreenOn(true);
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        mIsDrawing = true;
        new Thread(this).start();
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        mIsDrawing = false;
    }

    @Override
    public void run() {
        while(mIsDrawing){
            draw();
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int x = (int) event.getX();
        int y = (int) event.getY();
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                mPath.moveTo(x,y);
                break;
            case MotionEvent.ACTION_MOVE:
                mPath.lineTo(x,y);
                break;
        }
        return true;
    }

    private void draw(){
        try {
            mCanvas = mHolder.lockCanvas();
            mCanvas.drawColor(Color.WHITE);
            mCanvas.drawPath(mPath,mPaint);
        }catch (Exception e){

        }finally {
            mHolder.unlockCanvasAndPost(mCanvas);
        }
    }
}

你可能感兴趣的:(Android绘图机制与处理技巧)