一Bitmap和BitmapFactory

二Android绘图基础

三Path类

PathText

采用双缓冲实现画图板

弹球游戏


使用drawBitmapMesh扭曲图像

使用Shader填充图形

      • 一 Bitmap和BitmapFactory
          • 代码
      • Android绘图基础Canvas Paint
          • 代码
      • 三Path类
          • 代码
        • PathText
          • 代码
      • 采用双缓冲实现画图板
          • DrawView代码
          • MainActivity代码
          • menu_mainxml
      • 弹球游戏
          • 代码
      • 图形特效处理
        • Matrix控制变换步骤
          • 代码
      • 使用drawBitmapMesh扭曲图像
          • 代码
      • 使用Shader填充图形
          • MainActivity
          • MyView代码

一 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);    }}

更多相关文章

  1. mybatisplus的坑 insert标签insert into select无参数问题的解决
  2. Python技巧匿名函数、回调函数和高阶函数
  3. python list.sort()根据多个关键字排序的方法实现
  4. ANDROID源代码结构
  5. textview中加链接
  6. Android中正确自适应屏幕翻转
  7. 【Android(安卓)应用开发】 Android(安卓)相关代码规范 更新中 .
  8. 《第一行代码Android》学习总结第十三章 Android编程技巧
  9. Android(安卓)如何建立AIDL

随机推荐

  1. Vue实现简书导航栏效果
  2. App测试中Android和IOS测试区别
  3. PC上安装android market软件并提取apk文
  4. android 中asynctask的一些研究
  5. Android贝塞尔曲线实现水波纹的效果
  6. Android自适应屏幕方向、大小和分辨率之
  7. 移植FFmpeg到android ics
  8. Windows Cocos2d-x(二) Visual Studio 编写
  9. android项目打包成lib而非jar包的方法
  10. Android Scripting Environment动手玩