Android实战技巧之二十九:画布Canvas

Android Framework提供了一些2D画图的API,android.graphics包就是其中之一。

为了画一些东西,需要4个元素(或称组件)协同来完成:

* 位图:Bitmap来保持(hold)那些像素

* 画布:Canvas来响应画画(draw)的调用(并将其写入bitmap)

* 画笔:paint描述画画的颜色和样式等

* “颜料“:drawing primitive,比如矩形、路径、文字、位图等其他元素

而这其中Canvas是比较重要的一环,今天就来演示Canvas的基本使用方法。

一、基本的draw方法

android.graphics.Canvas类提供了很多“画“的方法,让这块画布具有了丰富多彩的画画能力。比如:画点、线、矩形、椭圆、圆、文字等等。下面的例子演示了这些方法的使用。

先来建一个类,继承自View。让画布铺在View上而显示出来(这也是自定义UI组件的路子)。

重载onDraw方法,让这些画画的步骤在onDraw中完成。

源码如下:

package com.lazytech.canvasdemo;
public class PaintBoard extends View {

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

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        //paint a circle
        Paint paint = new Paint();
        paint.setColor(Color.BLUE);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(10);
        canvas.drawCircle(120, 80, 60, paint);

        //paint string
        paint = new Paint();
        paint.setColor(Color.YELLOW);
        paint.setTextSize(20);
        canvas.drawText("My name is Linc!",245,140,paint);

        //draw line
        paint = new Paint();
        paint.setColor(Color.BLACK);
        canvas.drawLine(245,145,500,145,paint);
    }
}

只要在Activity的布局文件中加上这个view就可以了。如下:

    <com.lazytech.canvasdemo.PaintBoard
        android:id="@+id/paint_board"
        android:layout_width="match_parent"
        android:layout_below="@id/text"
        android:layout_height="200dp"
        android:background="@android:color/holo_green_light"
        />

二、一个画板

有了上面的基础,我们就可以发挥一下,写一个画板的demo。随着手指的滑动,屏幕上留下了你艺术的痕迹。那么这个画板要如何实现呢?

有几个点要抓住:

* 捕捉你手指的滑动轨迹。重载onTouchEvent方法来实现。

* 实时更新你的画图。用invalidate方法来通知onDraw重绘。

* 保存我们的作品。将bitmap保存成文件。

有了上述要点整理,我们开始工作吧。首先还是建一个画板类PaintBoard2继承自View。三个重要元素作为类成员并在构造函数中做初始化。

public class PaintBoard2 extends View {
    private Paint mPaint = null;
    private Bitmap mBitmap = null;
    private Canvas mBitmapCanvas = null;
        public PaintBoard2(Context context, AttributeSet attrs) {
        super(context, attrs);
        mBitmap = Bitmap.createBitmap(500,200, Bitmap.Config.ARGB_8888);
        mBitmapCanvas = new Canvas(mBitmap);
        mBitmapCanvas.drawColor(Color.GRAY);
        mPaint = new Paint();
        mPaint.setColor(Color.RED);
        mPaint.setStrokeWidth(6);
    }

随着手指滑动去画线:

    private float startX;
    private float startY ;
    @Override
    public boolean onTouchEvent(MotionEvent event) {

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                startX = event.getX();
                startY = event.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                float stopX = event.getX();
                float stopY = event.getY();
                Log.e(TAG,"onTouchEvent-ACTION_MOVE\nstartX is "+startX+
                " startY is "+startY+" stopX is "+stopX+ " stopY is "+stopY);
                mBitmapCanvas.drawLine(startX, startY, stopX, stopY, mPaint);
                startX = event.getX();
                startY = event.getY();
                invalidate();//call onDraw()
                break;
        }
        return true;
    }

在onDraw时画bitmap:

    @Override
    protected void onDraw(Canvas canvas) {
        if(mBitmap != null) {
            canvas.drawBitmap(mBitmap, 0, 0, mPaint);
        }
    }

提供一个将bitmap存入OutputStream的方法供保存位图做准备。

    public void saveBitmap(OutputStream stream) {
        if (mBitmap != null) {
            mBitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream);
        }
    }

它是如何在Activity中调用的呢?

activity的layout:

    <com.lazytech.canvasdemo.PaintBoard2
        android:id="@+id/paint_board2"
        android:layout_below="@id/paint_board"
        android:layout_width="match_parent"
        android:layout_height="200dp" />
    <Button
        android:id="@+id/btn_save"
        android:text="Save"
        android:layout_below="@id/paint_board2"
        android:onClick="OnSaveClicked"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

定义PaintBoard2作为Activity的成员,并在onCreate初始化:

private PaintBoard2 paintBoard2;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        paintBoard2 = (PaintBoard2)findViewById(R.id.paint_board2);
    }

点击保存按钮的处理:

    public void OnSaveClicked(View view) {
        try {
            File file = new File(Environment.getExternalStorageDirectory(),
                    System.currentTimeMillis() + ".jpg");
            OutputStream stream = new FileOutputStream(file);
            paintBoard2.saveBitmap(stream);
            stream.close();
            // send broadcast to Media to update data
            Intent intent = new Intent();
            intent.setAction(Intent.ACTION_MEDIA_MOUNTED);
            intent.setData(Uri.fromFile(Environment
                    .getExternalStorageDirectory()));
            sendBroadcast(intent);

            Toast.makeText(this, "save success", Toast.LENGTH_SHORT).show();
        } catch (Exception e) {
            Toast.makeText(this, "save failed", Toast.LENGTH_SHORT).show();
            e.printStackTrace();
        }

截图风采:

完成源码参考我的开源demo项目:

https://code.csdn.net/lincyang/androidwidgetdemo

参考:

http://www.cnblogs.com/menlsh/archive/2012/11/18/2776003.html

http://www.cnblogs.com/wuyou/p/3658691.html

时间: 2024-09-30 11:13:50

Android实战技巧之二十九:画布Canvas的相关文章

Android实战技巧之二十四:横竖屏切换

这几年一直在做手机上和电视盒的App,几乎没有考虑过横竖屏切换的问题.电视盒好说,横屏不变,你要是给它设计个竖屏人家也没机会使:而手机上的应用就不好说了,有些界面你设计了横竖屏兼容可能是为了表示你的功能强大.但是按照惯例,或许也是设计师图省事,我们只是做一个方案.就像目前主流的App都只有竖屏一个模式,比如微信.京东和招商银行.我截了几张图表示一下. 但是像地图之类的应用,也许横屏会显示的更友好一些.请看腾讯地图的设计如下: 细心的你会发现,地图的横竖屏的样式几乎是一样的布局,调整起来还是比较容

Android实战技巧之二十五:模拟器如何重启?试试Genymotion!

需要测试Android 5.1系统重启后的一个功能,我手里的设备都没有这么高的版本,那么只好向模拟器求助了.就像大多网友一样,我遇到了一个大问题,如何将Android模拟器重启? 我有几个想法: * 长按模拟器的power kay,调出重启键.可惜的是系统默认只有poweroff一项. * adb reboot或者adb shell进入模拟器再执行reboot.尝试了几个模拟器,都没有成功,只听见我本子的cpu风扇在飞速的运转着. * 自己写一个重启的小应用做重启这件事.这是几年前我就想写的程序

Android实战技巧之二十二:Android 5.1 SDK下载与配置

重要文件下载地址: (1)http://dl-ssl.google.com/android/repository/sources-22_r01.zip (2)http://dl-ssl.google.com/android/repository/tools_r24.1.2-windows.zip (3)http://dl-ssl.google.com/android/repository/android-22_r01.zip (4)http://dl-ssl.google.com/android

Android实战技巧之二十六:persistableMode与Activity的持久化

API 21为Activity增加了一个新的属性,只要将其设置成persistAcrossReboots,activity就有了持久化的能力,另外需要配合一个新的bundle才行,那就是PersistableBundle. 这里的持久化与传统意义的不同,它的具体实现在Activity重载的onSaveInstanceState.onRestoreInstanceState和onCreate方法. public void onSaveInstanceState(Bundle outState, P

Android实战技巧之二十八:启动另一个App/apk中的Activity

Android提供了在一个App中启动另一个App中的Activity的能力,这使我们的程序很容易就可以调用其他程序的功能,从而就丰富了我们App的功能.比如在微信中发送一个位置信息,对方可以点击这个位置信息启动腾讯地图并导航.这个场景在现实中作用很大,尤其是朋友在陌生的环境找不到对方时,这个功能简直就是救星. 本来想把本文的名字叫启动另一个进程中的Activity,觉得这样才有逼格.因为每个App都会运行在自己的虚拟机中,每个虚拟机跑在一个进程中.但仔细一想,能够称为一个进程,前提是这个App

Android实战技巧之二十:两个开源的图表/报表控件

项目中要用饼图和折线图表示数据,重造轮子不是咱风格,先了解一下时下比较流行的开源图表控件吧.调查发现有三个口碑不错的开源项目,它们是MPAndroidChart.AChartEngine.HoloGraphLibrary.由于AChartEngine源码放在code.google上,遂先放弃它,主要研究其他两个在github上的项目. HoloGraphLibrary 项目地址:https://github.com/Androguide/HoloGraphLibrary 简介:这是一个十分简单的

Android实战技巧之二十七:Maven编译开源二维码扫描项目zxing

拥有自己的手机软件工具箱是件非常有意义的事情.就目前国内Android的生态环境来说,混乱的不能再乱了.由于我们登录不了官网App商店,下软件就只好在国内五花八门的软件市场下载.由于这些市场的监管不力,什么样的软件都有,就拿二维码扫描软件来说,好多都带那种狗皮膏药一样的广告插件,真是特别讨厌. 在开源世界中有很多优秀的软件,其中zxing就是非常好的Android扫碼工具软件.我们可以拿来即用还可以学习内部机制,然后做些定制化个性化.既可以自己享用,又可以跟大家分享.真是不错. zxing在gi

从零开始学android&lt;TabHost标签组件.二十九.&gt;

TabHost主要特点是可以在一个窗口中显示多组标签栏的内容,在Android系统之中每个标签栏就称为一个Tab,而包含这多个标签栏的容器就将其称为TabHost,TabHost类的继承结构如下所示: java.lang.Object ? android.view.View ? android.view.ViewGroup ? android.widget.FrameLayout ? android.widget.TabHost 常用方法如下所示 1 public TabHost(Context

Android实战简易教程-第二十九枪(基于Face++实现年龄识别APP(一))

我们通过Face++提供的api和服务实现HowOld App的开发.首先我们实现图片的选择功能. 1.main.xml: <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/container" android:la