2018-07-13 15:00:07 xiaoxiaofeng001 阅读数 2831

Android端图像处理方法

在Android机中进行图像处理,常用的方式有两种:
一种是单纯使用JAVA语言进行图形处理,相当于你将C或者C++编写的图像处理方法,又重新用JAVA编写了一遍。这种开发方法需要你在opencv官网,首先下载好Opencv的Android的版本,然后将它配置到你的项目中,这样你进行图像处理时就可以直接调用一些现成的方法,比如灰度化、边缘化处理、图像增强、以及Bitmap与mat之间的转换等。
相对来说这种方式比较简单,对于C++不好的同学以及Android端处理图像的初学者非常实用,能够满足基本的需求,但毕竟是通过JAVA进行图像处理,其效率和处理效果,不如直接采用C++进行处理。

第二种方式是直接将Opencv的C++源码编译成Android SDK库,但由于本人C++非常渣,所以这种方法就直接跳过了,,,网上这部分有很多资料可以去百度一下。

这篇博客对于移动端的图像处理方法介绍的比较详细,可以看一下:[https://www.cnblogs.com/xiaoxiaoqingyi/p/6676096.html]

此外,51CTO学院里有一门Opencv for Android 基础入门课可以看一下, 作者是贾志刚。

2015-06-09 00:16:44 jia20003 阅读数 8543

大概是四月底的时候,有人加我QQ问我是否做能做一些基于图像皮肤检测的算法,

主要是实现对皮肤六项指标:

1.      水分

2.      有份

3.      痤疮与痘痘

4.      色斑与肤色

5.      皱纹

6.      毛孔

听到这些内容,我稍微思考了一下,心里有了点思路,跟他交流了一下,关于怎么

做的思路就更加清晰起来,于是我先基于JAVA语言实现了第一版,然后把它改到

android上面,发现android代码改动很小,就可以在android上运行我的测试程序

了,但是感觉速度很慢,大概要十几秒才能出结果,发给了那个哥们,他测试了一

下也很郁闷,说速度太慢了,准确率也不是很高,怎么办,于是我仔细对照了一下

,发现我用Java语言实现时,在桌面版都是基于int来处理RGB像素的每个通道数

据,android上面DVM这么玩就有点卡,于是我就全部改成了每个通道基于BYTE

来实现处理,然后增加了皮肤检测,这样过滤非SKIN像素,再降采样处理整个

Bitmap数据,果然速度很快了,只要三秒左右就可以出结果,连我自己都觉得

自己是大神了,然后就想把android的人脸检测加上去,实现分块检测,这样

可以更好的检测人脸皱纹类型,经过一番各种坑之后,android上的人脸检测

API我学会了使用,但是发现速度不给力,加上去等于作茧自缚。于是我又放

弃了。又经过一番各种调试,别人帮忙把相机功能加上了,这样就实现了拍

照直接检测出结果,于是第二个问题出现了,经常出现APP闪退,我哭,经

过一番测试,发现低分辨率时候才会出现闪退,后来我才明白问题出在我对

低分辨率进行降采样,已经超出了像素范围,我哭!解决了之后闪退问题就

不见了,终于可以正常运行了,也可以正常测试了:

测试程序中的效果截屏:


这个就是拍照检测的结果,

原理都是常见的图像处理算法,可以说本人的博客文章已经包含了该应用的所有

图像处理算法知识,只是如何活学活用取决于大家都图像处理问题的认知与理解。

 通过该算法的开发,我第一次将我学到的图像处理知识应用到实践之中用于解决

实际问题,同时也发现真实图像处理问题,不是一步算法就可以实现的,而是一

系列图像处理算法的综合运用。



2012-08-10 19:56:46 NUPTboyZHB 阅读数 14824
android 学习之图像处理系统(一)
源代码:android图像处理系统1.0

下图是软件运行的截图:


本文只做了图像的打开和简单处理算法(图像的变亮、中值滤波、平滑滤波)的功能。其中,当软件运行时,首先调用软件内的lenna图像,显示在ImageView上。用户也可以选择媒体库中的图像。点击选择算法按钮,选择相应的处理算法(目前仅有3种),然后进行处理。图像处理的所有算法实现均由ImageProcess类管理。SystemMain.java是系统的主函数,负责调用其他Activity等。SelectAlgActivity.java是显示算法列表的,供用户选择。转载请声明:http://blog.csdn.net/nuptboyzhb/article/details/7852999


SystemMain.java
package com.example.njupt.zhb.imageprocesssystem;
import java.io.File;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

public class SystemMain extends Activity {
    private Button selectImgBtn;
    private Button selectAlgBtn;
    private Button aboutBtn;
    private TextView filepathView;
    private TextView aboutView;
    private OnClickListener seleImgBtnListener=null;
    private OnClickListener seleAlgBtnListener=null;
    private OnClickListener aboutBtnListener=null;
    private static int RESULT_LOAD_IMAGE = 123;
    private String picturePath=null;
    private Bitmap myBitmap;
    private ImageView myImageView;
    private ImageProcess myImageProcess=new ImageProcess();
    private static final String DYNAMICACTION_Broadcast = "Broadcast.njupt.zhb.selectAlg";
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setTitle("ImageProcessing Made by ZhengHaibo");
        setContentView(R.layout.activity_system_main);
        seleImgBtnListener= new View.OnClickListener() {
            @Override
            public void onClick(View arg0) {
                Intent i = new Intent(
                        Intent.ACTION_PICK,
                        android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
                startActivityForResult(i, RESULT_LOAD_IMAGE);
            }
        };
        seleAlgBtnListener=new OnClickListener() {
			@Override
			public void onClick(View v) {
				Intent intent=new Intent(SystemMain.this,SelectAlgActivity.class);
				startActivity(intent);
			}
		};
		aboutBtnListener=new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
				Intent intent=new Intent(SystemMain.this,ActivityAbout.class);
				startActivity(intent);
			}
		};
		SetControl();
		ShowImage(null);
    }
    private void SetControl(){
    	selectAlgBtn=(Button)findViewById(R.id.processBtn);
    	selectImgBtn=(Button)findViewById(R.id.SelectBtn);
    	aboutBtn=(Button)findViewById(R.id.AboutBtn);
    	filepathView=(TextView)findViewById(R.id.ImagePath);
    	aboutView=(TextView)findViewById(R.id.AboutTextView);
    	myImageView=(ImageView)findViewById(R.id.imageshow);
    	selectAlgBtn.setOnClickListener(seleAlgBtnListener);
    	selectImgBtn.setOnClickListener(seleImgBtnListener);
    	aboutBtn.setOnClickListener(aboutBtnListener);
		IntentFilter filter_dynamic = new IntentFilter();
		filter_dynamic.addAction(DYNAMICACTION_Broadcast);
		registerReceiver(dynamicReceiver, filter_dynamic);
    }
    // 2 自定义动态广播接收器,内部类,接收选择的算法
  	private BroadcastReceiver dynamicReceiver = new BroadcastReceiver() {
  		@Override
  		public void onReceive(Context context, Intent intent) {
  			if(intent.getAction().equals(DYNAMICACTION_Broadcast)){
  				Toast.makeText(SystemMain.this, "Please wait ...", Toast.LENGTH_SHORT).show();
  				String seleFlag = intent.getStringExtra("selectFlag");
                int ch=Integer.parseInt(seleFlag);
                switch(ch){
                case 0:
                	ShowImage(myImageProcess.brighten(10, myBitmap));
                	break;
                case 1:
                	ShowImage(myImageProcess.averageFilter(3,3,myBitmap));
                	break;
                case 2:
                	ShowImage(myImageProcess.averageFilter(3,3,myBitmap));
                	break;
                default:
                Toast.makeText(SystemMain.this, "Wrong!", Toast.LENGTH_SHORT).show();
                		break;
                }
                Toast.makeText(SystemMain.this, "Processing finished!", Toast.LENGTH_SHORT).show();
  			}
  		}
  	};
    private Bitmap FilesToBitmap(String filename){
    	Bitmap temp=null;
    	if(filename!=null){
        	File imageFile = new File(filename);
            if (imageFile.exists())
            {
            	// Load the image from file
            	temp = BitmapFactory.decodeFile(filename);
            }
        	
    	}
    	return temp;
    }
    public void ShowImage(Bitmap bitmap){
    	if (bitmap!=null) {
    		myImageView.setImageBitmap(bitmap);
		}
    	else {
		   bitmap=BitmapFactory.decodeResource(getResources(), R.drawable.lenna);
		   myImageView.setImageBitmap(bitmap);
		   myBitmap=bitmap;
		}
    }
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
 
        if (requestCode == RESULT_LOAD_IMAGE && resultCode == RESULT_OK && null != data) {
            Uri selectedImage = data.getData();
            String[] filePathColumn = { MediaStore.Images.Media.DATA };
 
            Cursor cursor = getContentResolver().query(selectedImage,
                    filePathColumn, null, null, null);
            cursor.moveToFirst();
            int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
            picturePath = cursor.getString(columnIndex);
            cursor.close();
            filepathView.setText(picturePath);
            //imageView.setImageBitmap(BitmapFactory.decodeFile(picturePath));
            myBitmap=FilesToBitmap(picturePath);
            ShowImage(myBitmap);
        }
    }
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.activity_system_main, menu);
        return true;
    }
}


SelectAlgActivity.java
package com.example.njupt.zhb.imageprocesssystem;

import java.util.ArrayList;
import java.util.List;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.AdapterView.OnItemClickListener;
public class SelectAlgActivity extends Activity implements OnItemClickListener{
	private static final String DYNAMICACTION_Broadcast = "Broadcast.njupt.zhb.selectAlg";
	private ListView listView;
	public void sendFlagToActivity(String flag){
		Intent intent = new Intent();
		intent.setAction(DYNAMICACTION_Broadcast);
		intent.putExtra("selectFlag", flag);
		sendBroadcast(intent);
	}
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setTitle("Choose Image processing type!");
		listView = new ListView(this);
		List<String> list=getData();
		ArrayAdapter<String> adapter=new ArrayAdapter<String>(this, android.R.layout.simple_expandable_list_item_1,list);
		listView.setAdapter(adapter);
		setContentView(listView);
		listView.setOnItemClickListener(this);//绑定监听接口	
	}
	private List<String> getData(){
		List<String> data = new ArrayList<String>();
		data.add("图像变亮");
		data.add("中值滤波");
		data.add("平滑滤波");
		return data;
	}
	/*实现OnItemClickListener接口*/
	@Override
	public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
		//finish();
		String posString=Integer.toString(position);
		sendFlagToActivity(posString);
		finish();
	}
}


ImageProcess.java
package com.example.njupt.zhb.imageprocesssystem;

import android.graphics.Bitmap;

public class ImageProcess {
	public ImageProcess() {
		// TODO Auto-generated constructor stub
	}
	public Bitmap brighten(int brightenOffset,Bitmap myBitmap)
    {
    	// Create new array
    	int width = myBitmap.getWidth();
    	int height = myBitmap.getHeight();
    	int[] pix = new int[width * height];
    	myBitmap.getPixels(pix, 0, width, 0, 0, width, height);
    	
    	// Apply pixel-by-pixel change
    	int index = 0;
    	for (int y = 0; y < height; y++)
    	{
    		for (int x = 0; x < width; x++)
    		{
    			int r = (pix[index] >> 16) & 0xff;
    			int g = (pix[index] >> 8) & 0xff;
    			int b = pix[index] & 0xff;
    			r = Math.max(0, Math.min(255, r + brightenOffset));
    			g = Math.max(0, Math.min(255, g + brightenOffset));
    			b = Math.max(0, Math.min(255, b + brightenOffset));
    			pix[index] = 0xff000000 | (r << 16) | (g << 8) | b;
    			index++;
    		} // x
    	} // y
    	
    	// Change bitmap to use new array
    	Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
    	bitmap.setPixels(pix, 0, width, 0, 0, width, height);    	
    	myBitmap = null;
    	pix = null;
    	return bitmap;
    }
    
    // filterWidth and filterHeight must be odd numbers
    public Bitmap averageFilter(int filterWidth, int filterHeight,Bitmap myBitmap)
    {
    	// Create new array
    	int width = myBitmap.getWidth();
    	int height = myBitmap.getHeight();
    	int[] pixNew = new int[width * height];
    	int[] pixOld = new int[width * height];
    	myBitmap.getPixels(pixNew, 0, width, 0, 0, width, height);
    	myBitmap.getPixels(pixOld, 0, width, 0, 0, width, height);
    	
    	// Apply pixel-by-pixel change
    	int filterHalfWidth = filterWidth/2;
    	int filterHalfHeight = filterHeight/2;
    	int filterArea = filterWidth * filterHeight;
    	for (int y = filterHalfHeight; y < height-filterHalfHeight; y++)
    	{
    		for (int x = filterHalfWidth; x < width-filterHalfWidth; x++)
    		{
    			// Accumulate values in neighborhood
    			int accumR = 0, accumG = 0, accumB = 0;
    			for (int dy = -filterHalfHeight; dy <= filterHalfHeight; dy++)
    			{
    				for (int dx = -filterHalfWidth; dx <= filterHalfWidth; dx++)
    				{
    					int index = (y+dy)*width + (x+dx);
    					accumR += (pixOld[index] >> 16) & 0xff;
    					accumG += (pixOld[index] >> 8) & 0xff;
    					accumB += pixOld[index] & 0xff;
    				} // dx
    			} // dy
    			
    			// Normalize
    			accumR /= filterArea;
    			accumG /= filterArea;
    			accumB /= filterArea;
    			int index = y*width + x;
    			pixNew[index] = 0xff000000 | (accumR << 16) | (accumG << 8) | accumB;
    		} // x
    	} // y
    	
    	// Change bitmap to use new array
    	Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
    	bitmap.setPixels(pixNew, 0, width, 0, 0, width, height);
    	myBitmap = null;
    	pixOld = null;
    	pixNew = null;
    	return bitmap;
    }
    
    // filterWidth and filterHeight must be odd numbers
    public Bitmap medianFilter(int filterWidth, int filterHeight,Bitmap myBitmap)
    {
    	// Create new array
    	int width = myBitmap.getWidth();
    	int height = myBitmap.getHeight();
    	int[] pixNew = new int[width * height];
    	int[] pixOld = new int[width * height];
    	myBitmap.getPixels(pixNew, 0, width, 0, 0, width, height);
    	myBitmap.getPixels(pixOld, 0, width, 0, 0, width, height);
    	
    	// Apply pixel-by-pixel change
    	int filterHalfWidth = filterWidth/2;
    	int filterHalfHeight = filterHeight/2;
    	int filterArea = filterWidth * filterHeight;
    	for (int y = filterHalfHeight; y < height-filterHalfHeight; y++)
    	{
    		for (int x = filterHalfWidth; x < width-filterHalfWidth; x++)
    		{
    			// Accumulate values in neighborhood
    			int accumR = 0, accumG = 0, accumB = 0;
    			for (int dy = -filterHalfHeight; dy <= filterHalfHeight; dy++)
    			{
    				for (int dx = -filterHalfWidth; dx <= filterHalfWidth; dx++)
    				{
    					int index = (y+dy)*width + (x+dx);
    					accumR += (pixOld[index] >> 16) & 0xff;
    					accumG += (pixOld[index] >> 8) & 0xff;
    					accumB += pixOld[index] & 0xff;
    				} // dx
    			} // dy
    			
    			// Normalize
    			accumR /= filterArea;
    			accumG /= filterArea;
    			accumB /= filterArea;
    			int index = y*width + x;
    			pixNew[index] = 0xff000000 | (accumR << 16) | (accumG << 8) | accumB;
    		} // x
    	} // y
    	
    	// Change bitmap to use new array
    	Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
    	bitmap.setPixels(pixNew, 0, width, 0, 0, width, height);
    	myBitmap = null;
    	pixOld = null;
    	pixNew = null;
    	return bitmap;
    }
}


main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
	android:id="@+id/widget38"
	android:layout_width="fill_parent"
	android:layout_height="fill_parent"
	android:orientation="vertical"
	xmlns:android="http://schemas.android.com/apk/res/android">
<TextView
	android:id="@+id/ImagePath"
	android:layout_width="fill_parent"
	android:layout_height="wrap_content"
	android:text="ImageProcess" />
<Button
	android:id="@+id/SelectBtn"
	android:layout_width="fill_parent"
	android:layout_height="wrap_content"
	android:text="Select Image" />
<Button
	android:id="@+id/processBtn"
	android:layout_width="fill_parent"
	android:layout_height="wrap_content"
	android:text="Select Image Pcocessing Algorithm" />
<ImageView
	android:id="@+id/imageshow"
	android:layout_width="fill_parent"
	android:layout_height="wrap_content" />
<Button
	android:id="@+id/AboutBtn"
	android:layout_width="fill_parent"
	android:layout_height="wrap_content"
	android:text="About author" />
<TextView
	android:id="@+id/AboutTextView"
	android:layout_width="fill_parent"
	android:layout_height="wrap_content"
	android:text="zhb931706659@126.com  njupt zhb" />
</LinearLayout>



2016-12-22 17:22:50 chenzheng8975 阅读数 1696

一、概述
本篇文章介绍的是关于Android图像处理相关的,主要有动态修改图像的色相、饱和度及亮度,颜色矩阵,图像像素点调整、图像矩阵实现图像的平移缩放等,Xfermode相关知识点,渐变相关,图像倒影,像素块的坐标变化等等。

二、实例演示
1.色相、饱和度、亮度
这里写图片描述
实现效果图如下:
这里写图片描述
核心代码:

/**
     * 调整图像的色相、饱和度、亮度
     * @param bm 需要处理的图像
     * @param hue 色相
     * @param saturation 饱和度
     * @param lum 亮度
     * @return
     */
    public static 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(Paint.ANTI_ALIAS_FLAG);

        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);//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;

    }

2.颜色矩阵
这里写图片描述
实现效果图如下:
这里写图片描述
核心代码:

package com.czhappy.imagedemo.activity;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Paint;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.EditText;
import android.widget.GridLayout;
import android.widget.ImageView;

import com.czhappy.imagedemo.R;

/**
 * Description:颜色矩阵
 * User: chenzheng
 * Date: 2016/12/20 0020
 * Time: 17:42
 */
public class ColorMatrixActivity extends AppCompatActivity {

    private ImageView imageview;
    private GridLayout gridlayout;
    private Bitmap bitmap;
    private int mEtWidth, mEtHeight;

    private EditText[] mEts = new EditText[20];
    private float[] mColorMatrix = new float[20];


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_color_matrix);

        bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.test1);
        imageview = (ImageView) findViewById(R.id.imageview);
        gridlayout = (GridLayout) findViewById(R.id.gridlayout);

        //在控件绘制完毕调用
        gridlayout.post(new Runnable() {
            @Override
            public void run() {
                mEtWidth = gridlayout.getWidth()/5;
                mEtHeight = gridlayout.getHeight()/4;
                addEt();
                initMatrix();
                getMatrix();
                setImageMatrix();

            }
        });
    }

    private void addEt(){
        for (int i = 0; i < 20; i++) {
            EditText et = new EditText(this);
            mEts[i] = et;
            gridlayout.addView(et, mEtWidth, mEtHeight);


        }
    }

    private void initMatrix(){
        for (int i = 0; i < 20; i++) {

            if(i%6==0){
                mEts[i].setText("1");
            }else{
                mEts[i].setText("0");
            }
        }
    }

    private void getMatrix(){
        for (int i = 0; i < 20; i++) {
            mColorMatrix[i] = Float.parseFloat(mEts[i].getText().toString());

        }
    }

    private void setImageMatrix(){
        Bitmap bmp = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888);
        ColorMatrix colorMatrix = new ColorMatrix();
        colorMatrix.set(mColorMatrix);

        Canvas canvas = new Canvas(bmp);
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setColorFilter(new ColorMatrixColorFilter(colorMatrix));
        canvas.drawBitmap(bitmap, 0, 0, paint);
        imageview.setImageBitmap(bmp);
    }

    public void btnChange(View view){
        getMatrix();
        setImageMatrix();

    }

    public void btnReset(View view){
        initMatrix();
        getMatrix();
        setImageMatrix();

    }
}

3.图像像素点
实现效果图如下:
这里写图片描述
核心代码:

/**
     * 底片效果
     * @param bm
     * @return
     */
    public static Bitmap handleImageNegative(Bitmap bm){
        int width = bm.getWidth();
        int height = bm.getHeight();
        int color;
        int r, g, b ,a;

        Bitmap bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);

        int [] oldPx = new int[width*height];//新建像素点数组
        int [] newPx = new int[width*height];//新建像素点数组
        bm.getPixels(oldPx, 0, width, 0, 0, width, height);
        for (int i = 0; i < width*height; i++) {
            color = oldPx[i];
            r = Color.red(color);
            g = Color.green(color);
            b = Color.blue(color);
            a = Color.alpha(color);

            r = 255-r;
            g = 255-g;
            b = 255-b;

            if(r>255){
                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, width, 0, 0, width, height);//将新像素点数组赋值给bitmap
        return bmp;

    }

    /**
     * 怀旧效果
     * @param bm
     * @return
     */
    public static Bitmap handleImageOldPhoto(Bitmap bm){
        int width = bm.getWidth();
        int height = bm.getHeight();
        int color;
        int r, g, b ,a, r1, g1, b1;

        Bitmap bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);

        int [] oldPx = new int[width*height];//新建像素点数组
        int [] newPx = new int[width*height];//新建像素点数组
        bm.getPixels(oldPx, 0, width, 0, 0, width, height);
        for (int i = 0; i < width*height; i++) {
            color = oldPx[i];
            r = Color.red(color);
            g = Color.green(color);
            b = Color.blue(color);
            a = Color.alpha(color);

            r = (int)(0.393*r + 0.769*g + 0.189*b);
            g = (int)(0.349*r + 0.686*g + 0.168*b);
            b = (int)(0.272*r + 0.534*g + 0.131*b);

            if(r>255){
                r=255;
            }

            if(g>255){
                g=255;
            }

            if(b>255){
                b=255;
            }
            newPx[i] = Color.argb(a, r, g, b);//生成新的像素点
        }
        bmp.setPixels(newPx, 0, width, 0, 0, width, height);//将新像素点数组赋值给bitmap
        return bmp;

    }

    /**
     * 浮雕效果
     * @param bm
     * @return
     */
    public static Bitmap handleImageRelief(Bitmap bm){
        int width = bm.getWidth();
        int height = bm.getHeight();
        int color=0, colorBefore=0;
        int r, g, b ,a, r1, g1, b1;

        Bitmap bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);

        int [] oldPx = new int[width*height];//新建像素点数组
        int [] newPx = new int[width*height];//新建像素点数组
        bm.getPixels(oldPx, 0, width, 0, 0, width, height);
        for (int i = 1; i < width*height; i++) {
            colorBefore = oldPx[i-1];
            r = Color.red(colorBefore);
            g = Color.green(colorBefore);
            b = Color.blue(colorBefore);
            a = Color.alpha(colorBefore);

            color = oldPx[i];
            r1 = Color.red(color);
            g1 = Color.green(color);
            b1 = Color.blue(color);

            r = (r - r1 + 127);
            g = (g - g1 + 127);
            b = (b - b1 + 127);

            if(r>255){
                r=255;
            }

            if(g>255){
                g=255;
            }

            if(b>255){
                b=255;
            }
            newPx[i] = Color.argb(a, r, g, b);//生成新的像素点
        }
        bmp.setPixels(newPx, 0, width, 0, 0, width, height);//将新像素点数组赋值给bitmap
        return bmp;

    }

4.图像矩阵
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
实现效果图如下:
这里写图片描述
核心代码:

package com.czhappy.imagedemo.activity;

import android.graphics.Matrix;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.Gravity;
import android.view.View;
import android.widget.EditText;
import android.widget.GridLayout;

import com.czhappy.imagedemo.R;
import com.czhappy.imagedemo.view.ImageMatrixView;

/**
 * Description:
 * User: chenzheng
 * Date: 2016/12/21 0021
 * Time: 14:54
 */
public class ImageMatrixActivity extends AppCompatActivity {

    private GridLayout gridlayout;
    private ImageMatrixView imageMatrixView;

    private int mEtWidth, mEtHeight;

    private EditText[] mEts = new EditText[9];
    private float[] mImageMatrix = new float[9];

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_image_matrix);

        imageMatrixView = (ImageMatrixView) findViewById(R.id.myview);
        gridlayout = (GridLayout) findViewById(R.id.gridlayout);

        //在控件绘制完毕调用
        gridlayout.post(new Runnable() {
            @Override
            public void run() {
                mEtWidth = gridlayout.getWidth()/3;
                mEtHeight = gridlayout.getHeight()/3;
                addEts();
                initMatrix();

            }
        });
    }

    private void initMatrix() {
        for (int i = 0; i < 9; i++) {

            if(i%4==0){
                mEts[i].setText("1");
            }else{
                mEts[i].setText("0");
            }
        }
    }

    private void addEts() {
        for (int i = 0; i < 9; i++) {
            EditText et = new EditText(this);
            et.setGravity(Gravity.CENTER);
            mEts[i] = et;
            gridlayout.addView(et, mEtWidth, mEtHeight);
        }
    }

    public void btnChange(View view){
        getMatrix();
        Matrix matrix = new Matrix();
        matrix.setValues(mImageMatrix);
        imageMatrixView.setImageMatrix(matrix);
        imageMatrixView.invalidate();//刷新

        //使用系统api
        //doApi();

    }

    public void btnReset(View view){
        initMatrix();
        getMatrix();
        Matrix matrix = new Matrix();
        matrix.setValues(mImageMatrix);
        imageMatrixView.setImageMatrix(matrix);
        imageMatrixView.invalidate();//刷新

    }

    private void getMatrix(){
        for (int i = 0; i < 9; i++) {
            mImageMatrix[i] = Float.parseFloat(mEts[i].getText().toString());

        }
    }

    private void doApi(){
        getMatrix();
        Matrix matrix = new Matrix();
        matrix.setScale(2, 2);//将图像变为原来的来2倍
        matrix.postTranslate(200, 200);//平移,顺序显示

        imageMatrixView.setImageMatrix(matrix);
        imageMatrixView.invalidate();//刷新

    }
}

5.Xfermode
这里写图片描述
实现效果图如下:
这里写图片描述
核心代码:

package com.czhappy.imagedemo.view;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;

import com.czhappy.imagedemo.R;

/**
 * Description:圆角图片
 * User: chenzheng
 * Date: 2016/12/22 0022
 * Time: 10:19
 */
public class RoundRectXfermodeView extends View {

    private Bitmap mBitmap, mOut;
    private Paint mPaint;

    public RoundRectXfermodeView(Context context) {
        super(context);
        initView();
    }

    public RoundRectXfermodeView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView();
    }

    public RoundRectXfermodeView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView();
    }

    private void initView(){
        setLayerType(LAYER_TYPE_SOFTWARE, null);//禁用硬件加速
        mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.test1);
        mOut = Bitmap.createBitmap(mBitmap.getWidth(), mBitmap.getHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(mOut);
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        //绘制圆角矩形----dst
        canvas.drawRoundRect(new RectF(0, 0, 300, 300), 50, 50, mPaint);
        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        //绘制图片----src
        canvas.drawBitmap(mBitmap, 0, 0, mPaint);
        mPaint.setXfermode(null);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawBitmap(mOut, 0, 0, null);
    }
}

6.BitmapShader
实现效果图如下:
这里写图片描述
核心代码:

package com.czhappy.imagedemo.view;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Shader;
import android.util.AttributeSet;
import android.view.View;

import com.czhappy.imagedemo.R;

/**
 * Description:
 * User: chenzheng
 * Date: 2016/12/22 0022
 * Time: 11:03
 */
public class BitmapShaderView extends View {
    private Bitmap mBitmap;
    private Paint mPaint;
    private BitmapShader mBitmapShader;


    public BitmapShaderView(Context context) {
        super(context);
    }

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

    public BitmapShaderView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        mPaint = new Paint();
        mBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);

        //REPEAT:重复,CLAMP:拉伸,MIRROR:镜像
        mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR);
        mPaint.setShader(mBitmapShader);
        canvas.drawCircle(200, 300, 300, mPaint);


    }
}

7.倒影
实现效果图如下:
这里写图片描述
核心代码:

package com.czhappy.imagedemo.view;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Shader;
import android.util.AttributeSet;
import android.view.View;

import com.czhappy.imagedemo.R;

/**
 * Description:倒影
 * User: chenzheng
 * Date: 2016/12/22 0022
 * Time: 11:34
 */
public class ReflectView extends View{

    private Bitmap mSrcBitmap, mRefBitmap;
    private Paint mPaint;

    public ReflectView(Context context) {
        super(context);
        initView();
    }

    public ReflectView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView();
    }

    public ReflectView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView();
    }

    private void initView(){
        mSrcBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.test1);
        Matrix matrix = new Matrix();
        matrix.setScale(1, -1);//x轴对称
        mRefBitmap = Bitmap.createBitmap(mSrcBitmap, 0, 0, mSrcBitmap.getWidth(), mSrcBitmap.getHeight(), matrix, true);
        mPaint = new Paint();
        //透明度线性渐变
        mPaint.setShader(new LinearGradient(0, mSrcBitmap.getHeight(), 0, mSrcBitmap.getHeight()*1.4f,
                0xdd000000, 0x10000000, Shader.TileMode.CLAMP));
        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));

    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawColor(Color.BLACK);
        canvas.drawBitmap(mSrcBitmap, 0, 0, null);
        canvas.drawBitmap(mRefBitmap, 0, mSrcBitmap.getHeight(), null);
        canvas.drawRect(0, mRefBitmap.getHeight(), mSrcBitmap.getWidth(), mSrcBitmap.getHeight()*2, mPaint);

    }
}

8.像素块drawBitmapMesh
实现效果图如下:
这里写图片描述
核心代码:

package com.czhappy.imagedemo.view;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.View;

import com.czhappy.imagedemo.R;

/**
 * Description:
 * User: chenzheng
 * Date: 2016/12/22 0022
 * Time: 14:13
 */
public class MeshView extends View {

    private int WIDTH = 200;//纵向线的条数
    private int HEIGHT = 200;//横向线的条数
    private int COUNT = (WIDTH+1)*(HEIGHT+1);
    private float[] verts = new float[COUNT*2] ;//改变后的坐标
    private float[] orig = new float[COUNT*2] ;//原始坐标
    private Bitmap mBitmap;

    private float k = 1f;

    public MeshView(Context context) {
        super(context);
        initView();
    }

    public MeshView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView();
    }

    public MeshView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView();
    }

    private void initView(){
        int index = 0;
        mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.test1);
        float bmWidth = mBitmap.getWidth();
        float bmHeight = mBitmap.getHeight();
        for (int i = 0; i < HEIGHT+1; i++) {
            float fy = bmHeight*i/HEIGHT;
            for (int j = 0; j < WIDTH+1; j++) {
                float fx = bmWidth*j/WIDTH;
                orig[index*2+0] = verts[index*2+0] = fx;
                orig[index*2+1] = verts[index*2+1] = fy+50;
                index++;
            }
        }

    }

    @Override
    protected void onDraw(Canvas canvas) {
        for (int i = 0; i < HEIGHT+1; i++) {
            for (int j = 0; j < WIDTH+1; j++) {
                verts[(i*(WIDTH+1)+j)*2+0] += 0;
                //偏移量
                float offsetY = (float) Math.sin((float)j/WIDTH*2*Math.PI + k * 2 * Math.PI);
                verts[(i*(WIDTH+1)+j)*2+1] = orig[(i*(WIDTH+1)+j)*2+1] + offsetY*50;
            }
        }
        k+=0.1f;
        canvas.drawBitmapMesh(mBitmap, WIDTH, HEIGHT, verts, 0, null, 0, null);
        invalidate();//重绘
    }
}

三、源代码下载
http://download.csdn.net/detail/chenzheng8975/9718794

2017-01-05 17:18:50 qq_32175491 阅读数 0

一Bitmap和BitmapFactory
这里写图片描述
二Android绘图基础
这里写图片描述
三Path类
Path
PathText
PathText
采用双缓冲实现画图板
HandleDraw
弹球游戏
弹球游戏
matr
使用drawBitmapMesh扭曲图像
Mesh
使用Shader填充图形
这里写图片描述

一 Bitmap和BitmapFactory

Bitmap提供下面静态方法来创建Bitmap对象:
1. createBitmap(Bitmap source,int x ,int y,int width,int height).
2. createScaledBitmap(Bitmap src,int dstWidth,int dstHeight,boolean filter).
3. createBitmap(int width,int height,Bitmap.Config config)
4. createBitmap(Bitmap source,int x,int y,int width,int height,Matrix m,boolean filter)

BitmapFactory提供如下方法从不同的数据源来解析创建Bitmap对象。

  1. decodeByteArray(byte[] data,int offset,int length);
  2. decodeFile(String pathName);
  3. decodeFileDescriptor(FileDescriptor fd);
  4. decodeResource(Resource res,int id);
  5. decodeStream(inputStream is);

Android为Bitmap提供下面两个方法来判断它是否回收,以及强制Bitmap回收自己。

  1. boolean isRecycled();
  2. void recycle();

代码:

public class MainActivity extends Activity
{
    String[] images = null;
    AssetManager assets = null;
    int currentImg = 0;
    ImageView image;
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        image = (ImageView) findViewById(R.id.image);
        try
        {
            assets = getAssets();
            // 获取/assets/目录下所有文件
            images = assets.list("");
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
        // 获取next按钮
        final Button next = (Button) findViewById(R.id.next);
        // 为next按钮绑定事件监听器,该监听器将会查看下一张图片
        next.setOnClickListener(new OnClickListener()
        {
            @Override
            public void onClick(View sources)
            {
                // 如果发生数组越界
                if (currentImg >= images.length)
                {
                    currentImg = 0;
                }
                // 找到下一个图片文件
                while (!images[currentImg].endsWith(".png")
                        && !images[currentImg].endsWith(".jpg")
                        && !images[currentImg].endsWith(".gif"))
                {
                    currentImg++;
                    // 如果已发生数组越界
                    if (currentImg >= images.length)
                    {
                        currentImg = 0;
                    }
                }
                InputStream assetFile = null;
                try
                {
                    // 打开指定资源对应的输入流
                    assetFile = assets.open(images[currentImg++]);
                }
                catch (IOException e)
                {
                    e.printStackTrace();
                }
                BitmapDrawable bitmapDrawable = (BitmapDrawable) image
                    .getDrawable();
                // 如果图片还未回收,先强制回收该图片
                if (bitmapDrawable != null
                        && !bitmapDrawable.getBitmap().isRecycled()) // ①
                {
                    bitmapDrawable.getBitmap().recycle();
                }
                // 改变ImageView显示的图片
                image.setImageBitmap(BitmapFactory
                    .decodeStream(assetFile)); // ②
            }
        });
    }
}

Android绘图基础,Canvas , Paint

方法 简要说明
drawCircle() 画圆
drawPath(Path path,Paint paint) 沿着指定Path绘制任意形状
drawRect(RectF rect, Paint paint) 绘制区域,参数一为RectF一个区域
drawPath(Path path, Paint paint) 绘制一个路径,参数一为Path路径对象
drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint) 贴图,参数一就是我们常规的Bitmap对象,参数二是源区域(这里是bitmap),参数三是目标区域(应该在canvas的位置和大小),参数四是Paint画刷对象,因为用到了缩放和拉伸的可能,当原始Rect不等于目标Rect时性能将会有大幅损失。
drawLine(float startX, float startY, float stopX, float stopY, Paintpaint) 画线,参数一起始点的x轴位置,参数二起始点的y轴位置,参数三终点的x轴水平位置,参数四y轴垂直位置,最后一个参数为Paint 画刷对象。
drawPoint(float x, float y, Paint paint) 画点,参数一水平x轴,参数二垂直y轴,第三个参数为Paint对象。
drawText(String text, float x, floaty, Paint paint) 渲染文本,Canvas类除了上面的还可以描绘文字,参数一是String类型的文本,参数二x轴,参数三y轴,参数四是Paint对象。
drawOval(RectF oval, Paint paint) 画椭圆,参数一是扫描区域,参数二为paint对象;
drawCircle(float cx, float cy, float radius,Paint paint) 绘制圆,参数一是中心点的x轴,参数二是中心点的y轴,参数三是半径,参数四是paint对象;
drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint) 画弧,参数一是RectF对象,一个矩形区域椭圆形的界限用于定义在形状、大小、电弧,参数二是起始角(度)在电弧的开始,参数三扫描角(度)开始顺时针测量的,参数四是如果这是真的话,包括椭圆中心的电弧,并关闭它,如果它是假这将是一个弧线,参数五是Paint对象;

还有如下方法进行坐标变换

方法 简要说明
rotate(float degrees,float px,float py) 对Canvas执行旋转变换
scale(float sx,float sy,float px,float py) 对Canvas执行缩放变换
skew(float sx,float sy) 对Canvas执行倾斜变换
trenslate(float dx,float dy) 移动Canvas.向dx距离向下移动dy距离

代码:

public class MyView extends View
{
    public MyView(Context context, AttributeSet set)
    {
        super(context, set);
    }
    @Override
    // 重写该方法,进行绘图
    protected void onDraw(Canvas canvas)
    {
        super.onDraw(canvas);
        // 把整张画布绘制成白色
        canvas.drawColor(Color.WHITE);
        Paint paint = new Paint();
        // 去锯齿
        paint.setAntiAlias(true);
        paint.setColor(Color.BLUE);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(4);
        int viewWidth = this.getWidth();
        // 绘制圆形
        canvas.drawCircle(viewWidth / 10 + 10, viewWidth / 10 + 10
            , viewWidth / 10, paint);
        // 绘制正方形
        canvas.drawRect(10 , viewWidth / 5 + 20 , viewWidth / 5 + 10
            , viewWidth * 2 / 5 + 20 , paint);
        // 绘制矩形
        canvas.drawRect(10, viewWidth * 2 / 5 + 30, viewWidth / 5 + 10
            , viewWidth / 2 + 30, paint);
        RectF re1 = new RectF(10, viewWidth / 2 + 40
            , 10 + viewWidth / 5 ,viewWidth * 3 / 5 + 40);
        // 绘制圆角矩形
        canvas.drawRoundRect(re1, 15, 15, paint);
        RectF re11 = new RectF(10, viewWidth * 3 / 5 + 50
            , 10 + viewWidth / 5 ,viewWidth * 7 / 10 + 50);
        // 绘制椭圆
        canvas.drawOval(re11, paint);
        // 定义一个Path对象,封闭成一个三角形
        Path path1 = new Path();
        path1.moveTo(10, viewWidth * 9 / 10 + 60);
        path1.lineTo(viewWidth / 5 + 10, viewWidth * 9 / 10 + 60);
        path1.lineTo(viewWidth / 10 + 10, viewWidth * 7 / 10 + 60);
        path1.close();
        // 根据Path进行绘制,绘制三角形
        canvas.drawPath(path1, paint);
        // 定义一个Path对象,封闭成一个五角形
        Path path2 = new Path();
        path2.moveTo(10 + viewWidth / 15, viewWidth * 9 / 10 + 70);
        path2.lineTo(10 + viewWidth * 2 / 15, viewWidth * 9 / 10 + 70);
        path2.lineTo(10 + viewWidth / 5, viewWidth + 70);
        path2.lineTo(10 + viewWidth / 10, viewWidth * 11/10 + 70);
        path2.lineTo(10 , viewWidth + 70);
        path2.close();
        // 根据Path进行绘制,绘制五角形
        canvas.drawPath(path2, paint);
        // ----------设置填充风格后绘制----------
        paint.setStyle(Paint.Style.FILL);
        paint.setColor(Color.RED);
        // 绘制圆形
        canvas.drawCircle(viewWidth * 3 / 10 + 20, viewWidth / 10 + 10
            , viewWidth / 10, paint);
        // 绘制正方形
        canvas.drawRect(viewWidth / 5 + 20 , viewWidth / 5 + 20
            , viewWidth * 2 / 5 + 20 , viewWidth * 2 / 5 + 20 , paint);
        // 绘制矩形
        canvas.drawRect(viewWidth / 5 + 20, viewWidth * 2 / 5 + 30
            , viewWidth * 2 / 5 + 20 , viewWidth / 2 + 30, paint);
        RectF re2 = new RectF(viewWidth / 5 + 20, viewWidth / 2 + 40
            , 20 + viewWidth * 2 / 5 ,viewWidth * 3 / 5 + 40);
        // 绘制圆角矩形
        canvas.drawRoundRect(re2, 15, 15, paint);
        RectF re21 = new RectF(20 + viewWidth / 5, viewWidth * 3 / 5 + 50
            , 20 + viewWidth * 2 / 5 ,viewWidth * 7 / 10 + 50);
        // 绘制椭圆
        canvas.drawOval(re21, paint);
        // 定义一个Path对象,封闭成一个三角形
        Path path3 = new Path();
        path3.moveTo(20 + viewWidth / 5, viewWidth * 9 / 10 + 60);
        path3.lineTo(viewWidth * 2 / 5 + 20, viewWidth * 9 / 10 + 60);
        path3.lineTo(viewWidth * 3 / 10 + 20, viewWidth * 7 / 10 + 60);
        path3.close();
        // 根据Path进行绘制,绘制三角形
        canvas.drawPath(path3, paint);
        // 定义一个Path对象,封闭成一个五角形
        Path path4 = new Path();
        path4.moveTo(20 + viewWidth *4 / 15, viewWidth * 9 / 10 + 70);
        path4.lineTo(20 + viewWidth / 3, viewWidth * 9 / 10 + 70);
        path4.lineTo(20 + viewWidth * 2 / 5, viewWidth + 70);
        path4.lineTo(20 + viewWidth * 3 / 10, viewWidth * 11/10 + 70);
        path4.lineTo(20 + viewWidth / 5 , viewWidth + 70);
        path4.close();
        // 根据Path进行绘制,绘制五角形
        canvas.drawPath(path4, paint);
        // ----------设置渐变器后绘制----------
        // 为Paint设置渐变器
        Shader mShader = new LinearGradient(0, 0, 40, 60
            , new int[] {Color.RED, Color.GREEN, Color.BLUE, Color.YELLOW }
            , null , Shader.TileMode.REPEAT);
        paint.setShader(mShader);
        //设置阴影
        paint.setShadowLayer(25 , 20 , 20 , Color.GRAY);
        // 绘制圆形
        canvas.drawCircle(viewWidth / 2 + 30, viewWidth / 10 + 10
            , viewWidth / 10, paint);
        // 绘制正方形
        canvas.drawRect(viewWidth * 2 / 5 + 30 , viewWidth / 5 + 20
            , viewWidth * 3 / 5 + 30 , viewWidth * 2 / 5 + 20 , paint);
        // 绘制矩形
        canvas.drawRect(viewWidth * 2 / 5 + 30, viewWidth * 2 / 5 + 30
            , viewWidth * 3 / 5 + 30 , viewWidth / 2 + 30, paint);
        RectF re3 = new RectF(viewWidth * 2 / 5 + 30, viewWidth / 2 + 40
            , 30 + viewWidth * 3 / 5 ,viewWidth * 3 / 5 + 40);
        // 绘制圆角矩形
        canvas.drawRoundRect(re3, 15, 15, paint);
        RectF re31 = new RectF(30 + viewWidth *2 / 5, viewWidth * 3 / 5 + 50
            , 30 + viewWidth * 3 / 5 ,viewWidth * 7 / 10 + 50);
        // 绘制椭圆
        canvas.drawOval(re31, paint);
        // 定义一个Path对象,封闭成一个三角形
        Path path5 = new Path();
        path5.moveTo(30 + viewWidth * 2/ 5, viewWidth * 9 / 10 + 60);
        path5.lineTo(viewWidth * 3 / 5 + 30, viewWidth * 9 / 10 + 60);
        path5.lineTo(viewWidth / 2 + 30, viewWidth * 7 / 10 + 60);
        path5.close();
        // 根据Path进行绘制,绘制三角形
        canvas.drawPath(path5, paint);
        // 定义一个Path对象,封闭成一个五角形
        Path path6 = new Path();
        path6.moveTo(30 + viewWidth * 7 / 15, viewWidth * 9 / 10 + 70);
        path6.lineTo(30 + viewWidth * 8 / 15, viewWidth * 9 / 10 + 70);
        path6.lineTo(30 + viewWidth * 3 / 5, viewWidth + 70);
        path6.lineTo(30 + viewWidth / 2, viewWidth * 11/10 + 70);
        path6.lineTo(30 + viewWidth * 2 / 5 , viewWidth + 70);
        path6.close();
        // 根据Path进行绘制,绘制五角形
        canvas.drawPath(path6, paint);
        // ----------设置字符大小后绘制----------
        paint.setTextSize(48);
        paint.setShader(null);
        // 绘制7个字符串
        canvas.drawText(getResources().getString(R.string.circle)
            , 60 + viewWidth * 3 / 5, viewWidth / 10 + 10, paint);
        canvas.drawText(getResources().getString(R.string.square)
            , 60 + viewWidth * 3 / 5, viewWidth * 3 / 10 + 20, paint);
        canvas.drawText(getResources().getString(R.string.rect)
            , 60 + viewWidth * 3 / 5, viewWidth * 1 / 2 + 20, paint);
        canvas.drawText(getResources().getString(R.string.round_rect)
            , 60 + viewWidth * 3 / 5, viewWidth * 3 / 5 + 30, paint);
        canvas.drawText(getResources().getString(R.string.oval)
            , 60 + viewWidth * 3 / 5, viewWidth * 7 / 10 + 30, paint);
        canvas.drawText(getResources().getString(R.string.triangle)
            , 60 + viewWidth * 3 / 5, viewWidth * 9 / 10 + 30, paint);
        canvas.drawText(getResources().getString(R.string.pentagon)
            , 60 + viewWidth * 3 / 5, viewWidth * 11 / 10 + 30, paint);
    }
}

三Path类

Android还为路径绘制提供PathEffect来定义绘制效果。PathEffect还有如下子类:
1. ComposePathEffect
2. CornerPathEffect
3. DashPathEffect
4. DiscretePathEffect
5. PathDashPathEffect
6. SumPathEffect

代码:

public class MainActivity extends Activity
{
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(new MyView(this));
    }
    class MyView extends View
    {
        float phase;
        PathEffect[] effects = new PathEffect[7];
        int[] colors;
        private Paint paint;
        Path path;
        public MyView(Context context)
        {
            super(context);
            paint = new Paint();
            paint.setStyle(Paint.Style.STROKE);
            paint.setStrokeWidth(4);
            // 创建并初始化Path
            path = new Path();
            path.moveTo(0, 0);
            for (int i = 1; i <= 40; i++)
            {
                // 生成40个点,随机生成它们的Y坐标,并将它们连成一条Path
                path.lineTo(i * 20, (float) Math.random() * 60);
            }
            // 初始化7个颜色
            colors = new int[] { Color.BLACK, Color.BLUE, Color.CYAN,
                Color.GREEN, Color.MAGENTA, Color.RED, Color.YELLOW };
        }
        @Override
        protected void onDraw(Canvas canvas)
        {
            // 将背景填充成白色
            canvas.drawColor(Color.WHITE);
            // -----------下面开始初始化7种路径效果----------
            // 不使用路径效果
            effects[0] = null;
            // 使用CornerPathEffect路径效果
            effects[1] = new CornerPathEffect(10);
            // 初始化DiscretePathEffect
            effects[2] = new DiscretePathEffect(3.0f, 5.0f);
            // 初始化DashPathEffect
            effects[3] = new DashPathEffect(new float[] { 20, 10, 5, 10 },
                    phase);
            // 初始化PathDashPathEffect
            Path p = new Path();
            p.addRect(0, 0, 8, 8, Path.Direction.CCW);
            effects[4] = new PathDashPathEffect(p, 12, phase,
                    PathDashPathEffect.Style.ROTATE);
            // 初始化ComposePathEffect
            effects[5] = new ComposePathEffect(effects[2], effects[4]);
            effects[6] = new SumPathEffect(effects[4], effects[3]);
            // 将画布移动到(8、8)处开始绘制
            canvas.translate(8, 8);
            // 依次使用7种不同路径效果、7种不同的颜色来绘制路径
            for (int i = 0; i < effects.length; i++)
            {
                paint.setPathEffect(effects[i]);
                paint.setColor(colors[i]);
                canvas.drawPath(path, paint);
                canvas.translate(0, 60);
            }
            // 改变phase值,形成动画效果
            phase += 1;
            invalidate();
        }
    }
}

PathText

代码:

public class MainActivity extends Activity
{
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(new TextView(this));
    }
    class TextView extends View
    {
        final String DRAW_STR = "aserbao";
        Path[] paths = new Path[3];
        Paint paint;
        public TextView(Context context)
        {
            super(context);
            paths[0] = new Path();
            paths[0].moveTo(0, 0);
            for (int i = 1; i <= 20; i++)
            {
                // 生成20个点,随机生成它们的Y坐标,并将它们连成一条Path
                paths[0].lineTo(i * 30, (float) Math.random() * 30);
            }
            paths[1] = new Path();
            RectF rectF = new RectF(0, 0, 600, 360);
            paths[1].addOval(rectF, Path.Direction.CCW);
            paths[2] = new Path();
            paths[2].addArc(rectF, 60, 180);
            // 初始化画笔
            paint = new Paint();
            paint.setAntiAlias(true);
            paint.setColor(Color.CYAN);
            paint.setStrokeWidth(1);
        }
        @Override
        protected void onDraw(Canvas canvas)
        {
            canvas.drawColor(Color.WHITE);
            canvas.translate(40, 40);
            // 设置从右边开始绘制(右对齐)
            paint.setTextAlign(Paint.Align.RIGHT);
            paint.setTextSize(20);
            // 绘制路径
            paint.setStyle(Paint.Style.STROKE);
            canvas.drawPath(paths[0], paint);
            paint.setTextSize(40);
            // 沿着路径绘制一段文本
            paint.setStyle(Paint.Style.FILL);
            canvas.drawTextOnPath(DRAW_STR, paths[0], -8, 20, paint);
            // 对Canvas进行坐标变换:画布下移60
            canvas.translate(0, 60);
            // 绘制路径
            paint.setStyle(Paint.Style.STROKE);
            canvas.drawPath(paths[1], paint);
            // 沿着路径绘制一段文本
            paint.setStyle(Paint.Style.FILL);
            canvas.drawTextOnPath(DRAW_STR, paths[1], -20, 20, paint);
            // 对Canvas进行坐标变换: 画布下移360
            canvas.translate(0, 360);
            // 绘制路径
            paint.setStyle(Paint.Style.STROKE);
            canvas.drawPath(paths[2], paint);
            // 沿着路径绘制一段文本
            paint.setStyle(Paint.Style.FILL);
            canvas.drawTextOnPath(DRAW_STR, paths[2], -10, 20, paint);
        }
    }
}

采用双缓冲实现画图板

DrawView代码:

public class DrawView extends View
{
    // 定义记录前一个拖动事件发生点的坐标
    float preX;
    float preY;
    private Path path;
    public Paint paint = null;
    // 定义一个内存中的图片,该图片将作为缓冲区
    Bitmap cacheBitmap = null;
    // 定义cacheBitmap上的Canvas对象
    Canvas cacheCanvas = null;
    public DrawView(Context context, int width , int height)
    {
        super(context);
        // 创建一个与该View相同大小的缓存区
        cacheBitmap = Bitmap.createBitmap(width, height,
            Bitmap.Config.ARGB_8888);
        cacheCanvas = new Canvas();
        path = new Path();
        // 设置cacheCanvas将会绘制到内存中的cacheBitmap上
        cacheCanvas.setBitmap(cacheBitmap);
        // 设置画笔的颜色
        paint = new Paint(Paint.DITHER_FLAG);
        paint.setColor(Color.RED);
        // 设置画笔风格
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(1);
        // 反锯齿
        paint.setAntiAlias(true);
        paint.setDither(true);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event)
    {
        // 获取拖动事件的发生位置
        float x = event.getX();
        float y = event.getY();
        switch (event.getAction())
        {
            case MotionEvent.ACTION_DOWN:
                // 从前一个点绘制到当前点之后,把当前点定义成下次绘制的前一个点
                path.moveTo(x, y);
                preX = x;
                preY = y;
                break;
            case MotionEvent.ACTION_MOVE:
                // 从前一个点绘制到当前点之后,把当前点定义成下次绘制的前一个点
                path.quadTo(preX, preY, x, y);
                preX = x;
                preY = y;
                break;
            case MotionEvent.ACTION_UP:
                cacheCanvas.drawPath(path, paint); // ①
                path.reset();
                break;
        }
        invalidate();
        // 返回true表明处理方法已经处理该事件
        return true;
    }
    @Override
    public void onDraw(Canvas canvas)
    {
        Paint bmpPaint = new Paint();
        // 将cacheBitmap绘制到该View组件上
        canvas.drawBitmap(cacheBitmap, 0, 0, bmpPaint); // ②
        // 沿着path绘制
        canvas.drawPath(path, paint);
    }
}

MainActivity代码

public class MainActivity extends Activity
{
    EmbossMaskFilter emboss;
    BlurMaskFilter blur;
    DrawView drawView;
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        LinearLayout line = new LinearLayout(this);
        DisplayMetrics displayMetrics = new DisplayMetrics();
        // 获取创建的宽度和高度
        getWindowManager().getDefaultDisplay()
            .getRealMetrics(displayMetrics);
        // 创建一个DrawView,该DrawView的宽度、高度与该Activity保持相同
        drawView = new DrawView(this, displayMetrics.widthPixels
            , displayMetrics.heightPixels);
        line.addView(drawView);
        setContentView(line);
        emboss = new EmbossMaskFilter(new float[]
            { 1.5f, 1.5f, 1.5f }, 0.6f, 6, 4.2f);
        blur = new BlurMaskFilter(8, BlurMaskFilter.Blur.NORMAL);
    }
    @Override
    // 负责创建选项菜单
    public boolean onCreateOptionsMenu(Menu menu)
    {
        MenuInflater inflator = new MenuInflater(this);
        // 装载R.menu.my_menu对应的菜单,并添加到menu中
        inflator.inflate(R.menu.menu_main, menu);
        return super.onCreateOptionsMenu(menu);
    }
    @Override
    // 菜单项被单击后的回调方法
    public boolean onOptionsItemSelected(MenuItem mi)
    {
        // 判断单击的是哪个菜单项,并有针对性地作出响应
        switch (mi.getItemId())
        {
            case R.id.red:
                drawView.paint.setColor(Color.RED);
                mi.setChecked(true);
                break;
            case R.id.green:
                drawView.paint.setColor(Color.GREEN);
                mi.setChecked(true);
                break;
            case R.id.blue:
                drawView.paint.setColor(Color.BLUE);
                mi.setChecked(true);
                break;
            case R.id.width_1:
                drawView.paint.setStrokeWidth(1);
                break;
            case R.id.width_3:
                drawView.paint.setStrokeWidth(3);
                break;
            case R.id.width_5:
                drawView.paint.setStrokeWidth(5);
                break;
            case R.id.blur:
                drawView.paint.setMaskFilter(blur);
                break;
            case R.id.emboss:
                drawView.paint.setMaskFilter(emboss);
                break;
        }
        return true;
    }
}

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:title="@string/color">
        <menu>
            <!-- 定义一组单选菜单项 -->
            <group android:checkableBehavior="single">
                <!-- 定义多个菜单项 -->
                <item android:id="@+id/red"
                     android:title="@string/color_red"/>
                <item android:id="@+id/green"
                     android:title="@string/color_green"/>
                <item android:id="@+id/blue"
                     android:title="@string/color_blue"/>
            </group>
        </menu>
    </item>
    <item android:title="@string/width">
        <menu>
            <!-- 定义一组菜单项 -->
            <group>
                <!-- 定义三个菜单项 -->
                <item android:id="@+id/width_1"
                     android:title="@string/width_1"/>
                <item android:id="@+id/width_3"
                     android:title="@string/width_3"/>
                <item android:id="@+id/width_5"
                     android:title="@string/width_5"/>
            </group>
        </menu>
    </item>
    <item android:id="@+id/blur" android:title="@string/blur"/>
    <item android:id="@+id/emboss" android:title="@string/emboss"/>
</menu>

弹球游戏

代码:

public class MainActivity extends Activity
{
    // 桌面的宽度
    private int tableWidth;
    // 桌面的高度
    private int tableHeight;
    // 球拍的垂直位置
    private int racketY;
    // 下面定义球拍的高度和宽度
    private final int RACKET_HEIGHT = 30;
    private final int RACKET_WIDTH = 90;
    // 小球的大小
    private final int BALL_SIZE = 16;
    // 小球纵向的运行速度
    private int ySpeed = 15;
    Random rand = new Random();
    // 返回一个-0.5~0.5的比率,用于控制小球的运行方向
    private double xyRate = rand.nextDouble() - 0.5;
    // 小球横向的运行速度
    private int xSpeed = (int) (ySpeed * xyRate * 2);
    // ballX和ballY代表小球的坐标
    private int ballX = rand.nextInt(200) + 20;
    private int ballY = rand.nextInt(10) + 20;
    // racketX代表球拍的水平位置
    private int racketX = rand.nextInt(200);
    // 游戏是否结束的旗标
    private boolean isLose = false;
    private GameView contentView;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 去掉窗口标题
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        // 全屏显示
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
            WindowManager.LayoutParams.FLAG_FULLSCREEN);
        // 创建GameView组件
        final GameView gameView = new GameView(this);
        setContentView(gameView);
        // 获取窗口管理器
        WindowManager windowManager = getWindowManager();
        Display display = windowManager.getDefaultDisplay();
        DisplayMetrics metrics = new DisplayMetrics();
        display.getMetrics(metrics);
        // 获得屏幕宽和高
        tableWidth = metrics.widthPixels;
        tableHeight = metrics.heightPixels;
        racketY = tableHeight - 80;
        final Handler handler = new Handler() {
            public void handleMessage(Message msg) {
                if (msg.what == 0x123) {
                    gameView.invalidate();
                }
            }
        };
        gameView.setOnKeyListener(new OnKeyListener() // ②
        {
            @Override
            public boolean onKey(View source, int keyCode, KeyEvent event) {
                // 获取由哪个键触发的事件
                switch (event.getKeyCode()) {
                    // 控制挡板左移
                    case KeyEvent.KEYCODE_A:
                        if (racketX > 0) racketX -= 10;
                        break;
                    // 控制挡板右移
                    case KeyEvent.KEYCODE_D:
                        if (racketX < tableWidth - RACKET_WIDTH) racketX += 10;
                        break;
                }
                // 通知gameView组件重绘
                gameView.invalidate();
                return true;
            }
        });
        final Timer timer = new Timer();
        timer.schedule(new TimerTask() // ①
        {
            @Override
            public void run() {
                // 如果小球碰到左边边框
                if (ballX <= 0 || ballX >= tableWidth - BALL_SIZE) {
                    xSpeed = -xSpeed;
                }
                // 如果小球高度超出了球拍位置,且横向不在球拍范围之内,游戏结束
                if (ballY >= racketY - BALL_SIZE
                        && (ballX < racketX || ballX > racketX
                        + RACKET_WIDTH)) {
                    timer.cancel();
                    // 设置游戏是否结束的旗标为true
                    isLose = true;
                }
                // 如果小球位于球拍之内,且到达球拍位置,小球反弹
                else if (ballY <= 0
                        || (ballY >= racketY - BALL_SIZE
                        && ballX > racketX && ballX <= racketX
                        + RACKET_WIDTH)) {
                    ySpeed = -ySpeed;
                }
                // 小球坐标增加
                ballY += ySpeed;
                ballX += xSpeed;
                // 发送消息,通知系统重绘组件
                handler.sendEmptyMessage(0x123);
            }
        }, 0, 100);
    }
    class GameView extends View
    {
        Paint paint = new Paint();
        public GameView(Context context)
        {
            super(context);
            setFocusable(true);
        }
        // 重写View的onDraw方法,实现绘画
        public void onDraw(Canvas canvas)
        {
            paint.setStyle(Paint.Style.FILL);
            // 设置去锯齿
            paint.setAntiAlias(true);
            // 如果游戏已经结束
            if (isLose)
            {
                paint.setColor(Color.RED);
                paint.setTextSize(40);
                canvas.drawText("游戏已结束", tableWidth / 2 - 100, 200, paint);
            }
            // 如果游戏还未结束
            else
            {
                // 设置颜色,并绘制小球
                paint.setColor(Color.rgb(255, 0, 0));
                canvas.drawCircle(ballX, ballY, BALL_SIZE, paint);
                // 设置颜色,并绘制球拍
                paint.setColor(Color.rgb(80, 80, 200));
                canvas.drawRect(racketX, racketY, racketX + RACKET_WIDTH,
                        racketY + RACKET_HEIGHT, paint);
            }
        }
    }
}

图形特效处理

Matrix控制变换步骤:

    1. 获取Matrix对象。
    2. 调用Matrix的平移,旋转,缩放,倾斜等。
    3. 将程序对Matrix所做的变换应用的指定图形或组件。

代码:

public class MyView extends View
{
    // 初始的图片资源
    private Bitmap bitmap;
    // Matrix 实例
    private Matrix matrix = new Matrix();
    // 设置倾斜度
    private float sx = 0.0f;
    // 位图宽和高
    private int width, height;
    // 缩放比例
    private float scale = 1.0f;
    // 判断缩放还是旋转
    private boolean isScale = false;
    public MyView(Context context , AttributeSet set)
    {
        super(context , set);
        // 获得位图
        bitmap = ((BitmapDrawable) context.getResources().getDrawable(
            R.drawable.a)).getBitmap();
        // 获得位图宽
        width = bitmap.getWidth();
        // 获得位图高
        height = bitmap.getHeight();
        // 使当前视图获得焦点
        this.setFocusable(true);
    }
    @Override
    protected void onDraw(Canvas canvas)
    {
        super.onDraw(canvas);
        // 重置Matrix
        matrix.reset();
        if (!isScale)
        {
            // 旋转Matrix
            matrix.setSkew(sx, 0);
        }
        else
        {
            // 缩放Matrix
            matrix.setScale(scale, scale);
        }
        // 根据原始位图和Matrix创建新图片
        Bitmap bitmap2 = Bitmap.createBitmap(bitmap, 0, 0, width, height,
                matrix, true);
        // 绘制新位图
        canvas.drawBitmap(bitmap2, matrix, null);
    }
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event)
    {
        switch(keyCode)
        {
            // 向左倾斜
            case KeyEvent.KEYCODE_A:
                isScale = false;
                sx += 0.1;
                postInvalidate();
                break;
            // 向右倾斜
            case KeyEvent.KEYCODE_D:
                isScale = false;
                sx -= 0.1;
                postInvalidate();
                break;
            // 放大
            case KeyEvent.KEYCODE_W:
                isScale = true;
                if (scale < 2.0)
                    scale += 0.1;
                postInvalidate();
                break;
            // 缩小
            case KeyEvent.KEYCODE_S:
                isScale = true;
                if (scale > 0.5)
                    scale -= 0.1;
                postInvalidate();
                break;
        }
        return super.onKeyDown(keyCode, event);
    }
}

使用drawBitmapMesh扭曲图像

可以使用此方法在Android应用中开发除“水波荡漾”“风吹旗帜”等扭曲效果

代码:

public class MainActivity extends Activity
{
    private Bitmap bitmap;
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(new MyView(this, R.drawable.jinta));
    }
    private class MyView extends View
    {
        // 定义两个常量,这两个常量指定该图片横向、纵向上都被划分为20格
        private final int WIDTH = 20;
        private final int HEIGHT = 20;
        // 记录该图片上包含441个顶点
        private final int COUNT = (WIDTH + 1) * (HEIGHT + 1);
        // 定义一个数组,保存Bitmap上的21 * 21个点的坐标
        private final float[] verts = new float[COUNT * 2];
        // 定义一个数组,记录Bitmap上的21 * 21个点经过扭曲后的坐标
        // 对图片进行扭曲的关键就是修改该数组里元素的值
        private final float[] orig = new float[COUNT * 2];
        public MyView(Context context, int drawableId)
        {
            super(context);
            setFocusable(true);
            // 根据指定资源加载图片
            bitmap = BitmapFactory.decodeResource(getResources()
                    , drawableId);
            // 获取图片宽度、高度
            float bitmapWidth = bitmap.getWidth();
            float bitmapHeight = bitmap.getHeight();
            int index = 0;
            for (int y = 0; y <= HEIGHT; y++)
            {
                float fy = bitmapHeight * y / HEIGHT;
                for (int x = 0; x <= WIDTH; x++)
                {
                    float fx = bitmapWidth * x / WIDTH;
                    // 初始化orig、verts数组。 初始化后,orig、verts
                    // 两个数组均匀地保存了21 * 21个点的x,y坐标
                    orig[index * 2 + 0] = verts[index * 2 + 0] = fx;
                    orig[index * 2 + 1] = verts[index * 2 + 1] = fy;
                    index += 1;
                }
            }
            // 设置背景色
            setBackgroundColor(Color.WHITE);
        }
        @Override
        protected void onDraw(Canvas canvas)
        {
            //对bitmap按verts数组进行扭曲
            //从第一个点(由第5个参数0控制)开始扭曲
            canvas.drawBitmapMesh(bitmap, WIDTH, HEIGHT, verts
                    , 0, null, 0,null);
        }
        // 工具方法,用于根据触摸事件的位置计算verts数组里各元素的值
        private void warp(float cx, float cy)
        {
            for (int i = 0; i < COUNT * 2; i += 2)
            {
                float dx = cx - orig[i + 0];
                float dy = cy - orig[i + 1];
                float dd = dx * dx + dy * dy;
                // 计算每个坐标点与当前点(cx、cy)之间的距离
                float d = (float) Math.sqrt(dd);
                // 计算扭曲度,距离当前点(cx、cy)越远,扭曲度越小
                float pull = 100000 / ((float) (dd * d));
                // 对verts数组(保存bitmap上21 * 21个点经过扭曲后的坐标)重新赋值
                if (pull >= 1)
                {
                    verts[i + 0] = cx;
                    verts[i + 1] = cy;
                }
                else
                {
                    // 控制各顶点向触摸事件发生点偏移
                    verts[i + 0] = orig[i + 0] + dx * pull;
                    verts[i + 1] = orig[i + 1] + dy * pull;
                }
            }
            // 通知View组件重绘
            invalidate();
        }
        @Override
        public boolean onTouchEvent(MotionEvent event)
        {
            // 调用warp方法根据触摸屏事件的坐标点来扭曲verts数组
            warp(event.getX(), event.getY());
            return true;
        }
    }
}

使用Shader填充图形

MainActivity:

public class MainActivity extends Activity
        implements OnClickListener
{
    // 声明位图渲染对象
    private Shader[] shaders = new Shader[5];
    // 声明颜色数组
    private int[] colors;
    MyView myView;
    // 自定义视图类
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        myView = (MyView)findViewById(R.id.my_view);
        // 获得Bitmap实例
        Bitmap bm = BitmapFactory.decodeResource(getResources()
                , R.drawable.water);
        // 设置渐变的颜色组,也就是按红、绿、蓝的方式渐变
        colors = new int[] { Color.BLACK, Color.WHITE, Color.GRAY };
        // 实例化BitmapShader,x坐标方向重复图形,y坐标方向镜像图形
        shaders[0] = new BitmapShader(bm, TileMode.REPEAT,
                TileMode.MIRROR);
        // 实例化LinearGradient
        shaders[1] = new LinearGradient(0, 0, 100, 100
                , colors, null, TileMode.REPEAT);
        // 实例化RadialGradient
        shaders[2] = new RadialGradient(100, 100, 80, colors, null,
                TileMode.REPEAT);
        // 实例化SweepGradient
        shaders[3] = new SweepGradient(160, 160, colors, null);
        // 实例化ComposeShader
        shaders[4] = new ComposeShader(shaders[1], shaders[2],
                PorterDuff.Mode.DARKEN);
        Button bn1 = (Button)findViewById(R.id.bn1);
        Button bn2 = (Button)findViewById(R.id.bn2);
        Button bn3 = (Button)findViewById(R.id.bn3);
        Button bn4 = (Button)findViewById(R.id.bn4);
        Button bn5 = (Button)findViewById(R.id.bn5);
        bn1.setOnClickListener(this);
        bn2.setOnClickListener(this);
        bn3.setOnClickListener(this);
        bn4.setOnClickListener(this);
        bn5.setOnClickListener(this);
    }
    @Override
    public void onClick(View source)
    {
        switch(source.getId())
        {
            case R.id.bn1:
                myView.paint.setShader(shaders[0]);
                break;
            case R.id.bn2:
                myView.paint.setShader(shaders[1]);
                break;
            case R.id.bn3:
                myView.paint.setShader(shaders[2]);
                break;
            case R.id.bn4:
                myView.paint.setShader(shaders[3]);
                break;
            case R.id.bn5:
                myView.paint.setShader(shaders[4]);
                break;
        }
        // 重绘界面
        myView.invalidate();
    }
}

MyView代码:

public class MyView extends View
{
    // 声明画笔
    public Paint paint;
    public MyView(Context context , AttributeSet set)
    {
        super(context , set);
        paint = new Paint();
        paint.setColor(Color.RED);
    }
    @Override
    protected void onDraw(Canvas canvas)
    {
        super.onDraw(canvas);
        // 使用指定Paint对象画矩形
        canvas.drawRect(0, 0, getWidth(), getHeight(), paint);
    }
}