【android】Android中图形图片及处理相关Api的小总结

开发应用中图片的使用是必不可少的,Android系统提供了丰富的图片支持功能。我们除了可以使Drawable资源库,还可以使用Bitmap、Picture类去创建图片,也可以使用Canvas、Paint、Path类等去绘制我们满意的图片。在自定义控件时,这些API使用尤为常见。因此,小编觉得有必要简单的做个小总结。

那就先从Bitmap和BitmapFactory开始吧

Bitmap和BitmapFactory

BitmapFactory

      Bitmap代表一张位图。BitmapDrawable中封装的图片就是一个Bitmap对象。

可以调用BitmapDrawable的构造器将一个Bitmap对象封装成为一个BitmapDrawable对象,方法如下:

 BitmapDrawable drawable = new BitmapDrawable(bitmap);

如果想要获取BitmapDrawable中封装的Bitmap对象,可以采用如下方法:

Bitmap
bitmap = drawable.getBitmap();

BitmapFactory中提供了多个方法来解析、创建Bitmap的对象:

      decodeByteArray(byte[]
data, int offset, int length) :将制定字节数组从offset字节开始length长度的字节解析成Bitmap对象。

decodeFile(String
pathName) :将指定路径下的文件解析成Bitmap对象。

decodeFileDescriptor(FileDescriptor fd) :将FileDescriptor对应文件中解析,创建Bitmap对象。

decodeResource(Resources res, int id) :将给定的资源ID解析成Bitmap对象。

decodeStream(InputStream
is) 将指定的字节流解析成Bitmap对象。

另外,需要注意的是Android为Bitmap提供了两种方法判断它是否已经回收,以及强制Bitmap回收自己。分别为Boolean isRecycled() 和void recycle()方法

Canvas

  Canvas, 我们称之为“画布“,主要适用于绘制View的。 Canvas中提供了大量绘制图形的方法:

绘制扇形

drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint
paint): 第一个参数RectF对象,指定扇形的区域;二个参数是起始角度;第三个参数是旋转角度,顺时针旋转;第四个参数是是否填充,true为填充,false为不填充,也就是为一条弧线;第五个参数是绘制图形的画笔对象Paint。

RectF:通过RectF(float
left, float top, float right, float bottom)构造器创建RectF对象。

Paint:是绘制所有图形所用到的一个画笔,我们在稍后讲解。

drawArc(float left, float top, float right, float bottom, float startAngle, float
sweepAngle, boolean useCenter, Paint paint) :这个是将扇形区域左,上,右,下边的坐标直接输入,而不是通过RectF对象。其他参数同上。

绘制圆形:

drawCircle(float cx, float cy, float radius, Paint paint): 第一、二个参数是指圆形的x,
y坐标; 第三个参数是半径; 第四个参数是画笔Paint对象。

绘制直线:

drawLine(float startX, float startY, float stopX, float stopY, Paint paint) :两点确定一条直线,第一、二参数是起始点的坐标;第三、四参数是结束点的坐标;第五个参数画笔Paint对象。

drawLines(float[] pts, Paint paint) :多个点确定一条直线,第一个参数是点的数组;第二个参数是画笔Paint对象。

drawLines(float[] pts, int offset, int count, Paint paint)

绘制椭圆

drawOval(float left, float top, float right, float bottom, Paint paint):前四个参数是椭圆的左,上,右,下边的坐标,第五个是画笔Paint对象。

drawOval(RectF oval, Paint paint):第一个参数是RectF对象,
第二个参数是画笔Paint对象。

绘制矩形:

drawRect(RectF rect, Paint paint) :第一个参数是RectF对象,
第二个参数是画笔Paint对象。

绘制点:

drawPoint(float x, float y, Paint paint) :第一、二个参数点的坐标,第三个参数为Paint对象。

渲染文本:

drawText(String text, float x, floaty, Paint paint)

drawText(CharSequence text, int start, int end, float x, float y, Paint paint)

drawText(char[] text, int index, int count, float x, float y, Paint paint)

drawText(String text, int start, int end, float x, float y, Paint paint)

Canvas中还给我们提供了很多绘制其他图形的方法,这里我们不在一一列举。我们来看一下Paint”画笔“。

Paint:

  Paint是用于绘制的画笔,Canvas就像是我们的画纸,我们需要笔才可以完成一整幅图。Paint中为我们提供了很多设置的方法(我们这里只列举常用的方法):

setARGB(int a, int r, int g, int b) :设置
Paint对象颜色,参数一为alpha透明值

setAlpha(int a) :设置alpha不透明度,范围为0~255

setAntiAlias(boolean aa) :是否抗锯齿,这个一般是都要设置的。

setColor(int color) :设置颜色,这里Android内部定义的有Color类包含了一些常见颜色定义

setTextScaleX(float scaleX) :设置文本缩放倍数,1.0f为原始

setTextSize(float textSize) :设置字体大小

setUnderlineText(booleanunderlineText) :设置下划线

setStrokeCap(Paint.Cap cap) :当画笔样式为STROKE或FILL_OR_STROKE时,设置笔刷的图形样式,如圆形样式
Cap.ROUND,或方形样式Cap.SQUARE

setSrokeJoin(Paint.Join join) :设置绘制时各图形的结合方式,如平滑效果等

Path

Path, 轨迹,路径。Path可以沿着多个点绘制一条路径,
在Canvas中可以根据Path绘制不同的图形。

我们在使用Path绘制路径,一般要使用到以下几个方法:

moveTo(float x, float y): 移动到(x, y)坐标点。绘制路径时,路径的第一个点一般我们通过moveTo()来决定,否则默认为(0,
0)点。

lineTo(float x, float y): 从当前点绘制直线到(x, y)点。这个与moveTo()不同,moveTo()是指跳转到(x, y)点,而不绘制连线。

close(): 将路径封闭。举例来说,我们绘制一个三角形,三角形的三个点是(0, 0,),(100, 0),(0, 100)我们使用lineTo将(0, 0,),(100, 0)连接,(100, 0),(0, 100)连接,这样还差(0, 0,),(0, 100)之间的连线,这时我们可以直接调用close()方法,这样就会直接将图形封闭构成三角形。

quadTo(float x1, float y1, float x2, float y2): 绘制贝塞尔曲线,贝塞尔曲线是由三个点控制的:起始点,终止点,控制点。在该方法中,前两个参数是控制点的坐标,后两个参数是终止点坐标。

  

  Path中我们可以通过点来绘制路径也可以通过addXXX()方法来绘制,Path中给我们提供了很多这样的方法来添加不同的路径:

方法 用途
addArc(RectF oval, float startAngle, float sweepAngle) 添加圆弧轨迹
addCircle(float x, float y, float radius, Path.Direction dir) 添加圆形轨迹
addOval(float left, float top, float right, float bottom, Path.Direction dir) 添加椭圆轨迹
addRect(float left, float top, float right, float bottom, Path.Direction dir) 添加矩形轨迹
addRoundRect(float left, float top, float right, float bottom, float rx, float ry, Path.Direction dir) 添加椭圆角矩形轨迹

下面主要通过两个简单的小例子加深对知识点的理解。当然,在文章开始我们已经提到这些Api在自定义控件中比较常见,下面也涉及到一些自定义View方面的知识。(~!~如果时间希望能够系统的学习下自定义View以及ViewGroup方面的知识以及写一篇这方面的总结)

自定义时钟的Demo主要用到了Canvas以及Paint方面的知识,来看看代码吧:

public class MainActivity extends AppCompatActivity {

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

主活动中仅仅加载了一个布局。

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
    android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity">

    <com.example.zjn.clock.MyView
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>

在布局文件中放置我们自定义View,比较简单不需要太多介绍。

public class MyView  extends View{
        private int width;//设置高
        private int height;//设置高
        private Paint mPaintLine;//定义一个绘制直线的画笔
        private Paint mPaintSecondLine;//定义一个绘制直线的画笔
        private Paint mPaintInterCircle;//定义一个绘制圆的画笔
        private Paint mPaintOutSideCircle;//定义一个绘制圆的画笔
        private Paint mPaintText;//定义一个绘制文字的画笔
        private Calendar mCalendar;//创建一个时间类
        private static final int NEED_INVALIDATE = 0X6666;

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

    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);
        //初始化画直线的画笔
        mPaintLine = new Paint();
        mPaintLine.setAntiAlias(true);//消除锯齿
        mPaintLine.setColor(Color.GRAY);//设置画笔颜色
        mPaintLine.setStyle(Paint.Style.STROKE);//设置为空心
        mPaintLine.setStrokeWidth(10);//设置宽度// 初始化秒针的画笔
        mPaintSecondLine = new Paint();
        mPaintSecondLine.setAntiAlias(true);//消除锯齿
        mPaintSecondLine.setColor(Color.GRAY);//设置画笔颜色
        mPaintSecondLine.setStyle(Paint.Style.STROKE);//设置为空心
        mPaintSecondLine.setStrokeWidth(7);//设置宽度//初始化内圆的画笔
        mPaintInterCircle = new Paint();
        mPaintInterCircle.setAntiAlias(true);//消除锯齿
        mPaintInterCircle.setColor(Color.BLACK);
        mPaintInterCircle.setStyle(Paint.Style.STROKE);//设置为空心
        mPaintInterCircle.setStrokeWidth(5);
        //初始化外圆的画笔
        mPaintOutSideCircle = new Paint();
        mPaintOutSideCircle.setAntiAlias(true);//消除锯齿
        mPaintOutSideCircle.setColor(Color.BLACK);
        mPaintOutSideCircle.setStyle(Paint.Style.STROKE);//设置为空心
        mPaintOutSideCircle.setStrokeWidth(10);

        //绘制文字的画笔
        mPaintText = new Paint();
        mPaintText.setAntiAlias(true);//消除锯齿
        mPaintText.setColor(Color.GRAY);
        mPaintText.setStyle(Paint.Style.STROKE);//设置为空心
        mPaintText.setTextAlign(Paint.Align.CENTER);
        mPaintText.setTextSize(40);
        mPaintText.setStrokeWidth(6);

        //初始化日历
        mCalendar = Calendar.getInstance();
        //发送一个消息给UI主线程
        Handler handler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                switch (msg.what) {
                    case NEED_INVALIDATE:
                        //跟新时间
                        mCalendar = Calendar.getInstance();
                        invalidate();
                        sendEmptyMessageDelayed(NEED_INVALIDATE, 1000);
                        break;
                }

            }
        };
        handler.sendEmptyMessageDelayed(NEED_INVALIDATE, 2000);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        width = getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec);
        height = getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec);
        setMeasuredDimension(width, height);//设置宽和高
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        // 主线程自动调用
        canvas.drawCircle(width / 2, height / 2, 300, mPaintInterCircle);
        canvas.drawCircle(width / 2, height / 2, 320, mPaintOutSideCircle);
        for (int i = 1; i <= 12; i++) {
            canvas.save();//保存当前状态
            canvas.rotate(360 / 12 * i, width / 2, height / 2);//根据点width/2,height/2旋转
            canvas.drawLine(width / 2, height / 2 - 300, width / 2, height / 2 - 270, mPaintLine);
            canvas.drawText("" + i, width / 2, height / 2 - 240, mPaintText);
            canvas.restore();//回到save()方法保存的状态
        }

        //绘制分针
        int minute=  mCalendar.get(Calendar.MINUTE);
        float minuteDegree = minute / 60f * 360;
        canvas.save();
        canvas.rotate(minuteDegree, width / 2, height / 2);
        canvas.drawLine(width / 2, height / 2 - 200, width / 2, height / 2 + 40, mPaintLine);
        canvas.restore();
        //绘制时针
        int hour=  mCalendar.get(Calendar.HOUR);
        float hourDegree = (hour * 60 + minute);//(12f*60)*360;
        canvas.save();
        canvas.rotate(hourDegree, width / 2, height / 2);
        canvas.drawLine(width / 2, height / 2 - 170, width / 2, height / 2 + 30, mPaintLine);
        canvas.restore();
        //绘制秒针
        int second =  mCalendar.get(Calendar.SECOND);
        float secondDegree = second * 6;//一秒是6度。
        canvas.save();
        canvas.rotate(secondDegree, width / 2, height / 2);
        canvas.drawLine(width / 2, height / 2 - 220, width / 2, height / 2 + 50, mPaintSecondLine);
        canvas.restore();
    }
}

onDraw是UI主线程不断调用重绘界面的,因此我们需要使用到Handler,通过发送一个消息给Handler对象,让Handler对象在每一秒重绘一次MyView控件。这里重绘不能调用onDraw()方法额,而要调用的是invalidate()方法,invalidate()方法中调用了onDraw()方法。

下面马上来看看效果吧:

采用双缓冲实现画图板用到了以上提到的各类知识,主要原理是:当程序需要在指定View上进行绘制时,程序并不直接绘制到该View组建上,而是先绘制到内存中的一个Bitmap图片上,等内存中的Bitmap绘制好之后,再一次性将Bitmap绘制到View上面,还是直接看代码吧:

public class MainActivity extends AppCompatActivity {

    DrawView drawView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        LinearLayout line = new LinearLayout(this);
        DisplayMetrics displayMetrics = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getRealMetrics(displayMetrics);
        drawView = new DrawView(this, displayMetrics.widthPixels,displayMetrics.heightPixels);
        line.addView(drawView);
        setContentView(line);
    }
}

主活动中主要获取了穿件的宽和高,同是创建DrawView,让DrawView的宽和高保持与该Activity相同。来看看DrawView中的代码:

public class DrawView extends View{
    float prex;
    float prey;
    private Path path;
    public Paint paint = null;
    Bitmap CacheBitmap = null;
    Canvas CacheCanvas = null;
    public DrawView(Context context, int widthPixels, int heightPixels) {
        super(context);
        CacheBitmap = Bitmap.createBitmap(widthPixels,heightPixels,Bitmap.Config.ARGB_8888);
        CacheCanvas = new Canvas();
        path = new Path();
        CacheCanvas.setBitmap(CacheBitmap);
        paint = new Paint(Paint.DITHER_FLAG);
        paint.setColor(Color.RED);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(5);
        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();
        return true;
    }

    @Override
    public void onDraw(Canvas canvas) {
        Paint bmPaint = new Paint();
        canvas.drawBitmap(CacheBitmap,0,0,bmPaint);
        canvas.drawPath(path,paint);
    }
}

为了让view绘制的图形发生改变,需要程序记住一些状态数据:采用变量或者采用事件监听器,在监听器中修改这些数据。不管使用哪种方式,每次VIew组件上的图形状态发生改变时都应该通知View组件重新调用OnDraw()方法重绘该控件,通知可以调用invalidate()。

运行结果如下:

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-07 12:16:52

【android】Android中图形图片及处理相关Api的小总结的相关文章

Android工程中加入图片,报错cannot be resolved or is not a field

SDK和ADT为22.6.2版本 工程为4.4.2 今天在写Android代码的时候,往工程中加入了几张图片,然后在代码中使用R.drawable调用时,一直报错 cannot be resolved or is not a field 然后我查看了gen目录下的R.java文件,发现里面已经有我加入的图片资源ID了,觉得很奇怪,一般是无法生成R.java文件的时候才会出现这种现象啊 在网上查了资料也未见有可以解决我这个问题的方法,然后我就把我的代码从头到尾重新看了一遍,开始也没有发现什么异常,

Android:WebView中对图片注册上下文菜单

前言 今天一朋友问我一个问题,就是如何在WebView控件中的图片增加上下文菜单,以便增加保存图片等功能.今天就给他简单做了一个演示Demo,现写下来,给有相同问题的朋友提供些许思路吧. 概要实现 其实这个功能很简单,没有太复杂的东西,就是对WebView的控件的使用,一是给WebView注册了上下文菜单事件,二是在响应事件中去判断事件源的类型,如果是图片类型,则把url取出来 注册上下文菜单事件 这个就比较简单了通过下面的代码即可完成. WebView vw = (WebView) findV

Android内存中的图片

图片在内存中的大小 Android.graphics.Bitmap类里有一个内部类Bitmap.Config类,在Bitmap类里createBitmap(intwidth, int height, Bitmap.Config config)方法里会用到,打开个这个类一看 枚举变量public static final Bitmap.Config ALPHA_8public static final Bitmap.Config ARGB_4444public static final Bitma

Android EditText中插入图片并响应点击事件

EditText中插入图片基本就是两种方法: 1,通过Html.fromHtml(..)来实现 [mw_shl_code=java,true]eText.append(Html.fromHtml("<img src='" + R.drawable.ohoh + "'/>", imageGetter, null));[/mw_shl_code] 2,通过ImageSpan与SpannableString/SpannableStringBuilder. [m

Android开发中根据图片名称获取在drawable中的ID

在Android开发中图片资源是必不可少的,如ImageView需要图片资源的ID,ImageButton需要资源的ID等等,我们可以用R.drawable.XXX可以获取图片资源的ID,但是,在某些时候,这样做很费时,我们想动态的获得资源ID,比如说,我传入一个图片名称的字符串,根据字符串来获得资源的ID这样就很方便了,没错,这样确实很方便,我们如果对图片的名称稍加改动,比如用img1.png,img2.png,img3.png...这样就可以在一个循环之内获得所有的ID,对开发来说少写的就不

Android 自动循环滚动图片(广告)附带导航小圆点

用viewpage实现了滚动图片,自动循环的效果,附带导航的小圆点 效果图如下: 准备工作: 下载我打包好的jar包,放入项目的libs文件夹下即可使用 下载地址:http://download.csdn.net/detail/u012027644/8744019 使用方法: Activity的代码: public class MainActivity extends Activity { MyImgScroll myPager; // 图片容器 LinearLayout ovalLayout;

cocos2dx中关于Action动作的相关API的详细介绍

 //CCMoveBy  创建一个移动的动作 //参数1:移动到目标坐标所需的时间 //参数2:目标坐标 //支持reverse 可以获取其反向动作 //CCMoveTo  一样的 //CCActionInterval * moveBy = CCMoveBy::create(5,ccp(300, 100)); //CCActionInterval * actionmoveback= moveBy->reverse(); //sp->runAction(actionmoveback); //

Android代码中实现WAP方式联网(转载!)

转载地址:http://www.linuxidc.com/Linux/2012-08/67980.htm 学习中,用到的,记录下. 无论是移动.联通还是电信,都至少提供了两种类型的的APN:WAP方式和NET方式.其中NET方式跟WIFI方式一样,无需任何设置,可自由访问所有类型网站,而WAP方式,需要手机先设置代理服务器和端口号等信息,并且只能访问HTTP协议类型的网站. 1) 移动的WAP名称是CMWAP,NET名称是CMNET: 2) 联通的WAP名称是UNIWAP,NET名称是UNINE

Android Fresco图片处理库用法API英文原文文档1(Facebook开源Android图片库)

Fresco是Facebook最新推出的一款用于Android应用中展示图片的强大图片库,可以从网络.本地存储和本地资源中加载图片.其中的Drawees可以显示占位符,直到图片加载完成.而当图片从屏幕上消失时,会自动释放内存. 功能很强大,为了大家学习方便,我将英文原文文档给大家迁移过来,供参考学习. 这是英文文档的第一部分:QUICK START QUICK START Adding Fresco to your Project Here's how to add Fresco to your