Android開發&Canvas

若非專業設置遊戲界面或者從事美工人員,對於2D圖形繪製工具Canvas,只需懂得基本用法就好,技術人員主要是爲了實現功能,Canvas也主要是在自定義view及其子類時,繪製界面所用。
canvas繪圖被人形象的描述爲“畫布”,這樣理解也無可厚非,畫筆“paint”工作的地方就是畫布。
有時候需要製作一些圖形,現有控件是無法辦到的,這時需要我們新建view類,然後將內容畫到視圖中,最常用的就是重寫draw方法,這個方法就是顯示界面時候你指定要”畫“的東西。

class DrawView extends View {
    public DrawView(Context context) {
        super(context);
    }

    @Override
    public void draw(Canvas canvas){
        super.draw(canvas);//參數傳入的畫布,明顯還有其他地方需要引用,因此不要自己去新建canvas了。
        int width =canvas.getWidth();//獲取畫布的寬;
        int height = canvas.getHeight();//獲取畫布的高;
        Paint paint = new Paint();//新建畫筆類;
        paint.setStyle(Paint.Style.STROKE);//設置畫筆類型爲stroke,表示只描邊
        paint.setAlpha(250);//設置——不——透明度,爲0表示透明。
        paint.setAntiAlias(true);//抗鋸齒,爲了讓畫出來的線更平滑。
        paint.setStrokeWidth(5);//設置畫筆線的粗細,是以像素爲單位的,例如紅米手機像素爲720*1280
        paint.setColor(Color.BLUE);//設置畫筆顏色
        canvas.drawPoint(100,100,paint);//在100,100的座標畫一個點,點的大小可以通過設置畫筆線的粗細來調節。
   }
}

如果在linearLayout中橫向排列同時設置了一個控件的width=“wrap_content”和weight,那麼會優先設置控件的寬度大於等於wrap_content;
基本的canvas和paint用法,網上可以自己找,很多人都有介紹,這裏只是畫一個點示範,要注意的是,座標軸是以你自定義的控件的左上角爲座標原點的,如果你控件不是充滿整個屏幕,那麼canvas的width和height就不是爲屏幕的像素了,另外如果你重寫了ontouch方法,會發現座標會有負值出現

爲了學習canvas,我自己製作了一個塗鴉板,手指接觸屏幕或者滑動時,會在上面留下軌跡,然後可以將畫板以圖片文件的形式保存下來,且如果再次打開畫板,可以將保存的圖片文件再顯示到畫板上,功能大概就是如此,且看代碼。

class DrawView extends View {
public DrawView(Context context) {
super(context);
}

@Override
public void draw(Canvas canvas){
super.draw(canvas);
int width =canvas.getWidth();
int height = canvas.getHeight();
Paint paint = new Paint();
paint.setStyle(Paint.Style.STROKE);
paint.setAlpha(250);
paint.setAntiAlias(true);
paint.setStrokeWidth(5);
paint.setColor(Color.BLUE);

//從這裏往下看->
File file=new File(Environment.getExternalStorageDirectory() + "/" + "bitmap.jpg");
if(file.exists()) {
try {
Bitmap b=BitmapFactory.decodeFile(file.getPath());
canvas.setBitmap(b);
} catch (Exception e) {
Toast.makeText(DrawBitmap.this,e.toString(),Toast.LENGTH_LONG).show();
}
}
}
}

爲了省事我把很多的代碼刪除了,所以有些變量看起來跟沒有定義一樣,沒關係,我們直接看。
當有文件bitmap存在時,就通過BitmapFactory.decodeFile()生成bitmap位圖,然後直接通過canvas.setBitmap()方法,把位圖設爲畫布的背景,這時會有異常:
IllegalStateException
查看源碼發現是因爲setBitmap()方法參數必須是可以改變的位圖:

public void setBitmap(@Nullable Bitmap bitmap) {
    if (!bitmap.isMutable()) {
            throw new IllegalStateException();
        }
    }
}
//注意這代碼都是截取的,因此發現跟源碼不同不要驚異。

然後我改變源碼,先把bitmap變爲isMutable的,再傳入:

canvas.setBitmap(b.copy(Bitmap.Config.ARGB_8888, true));

嗯,它又拋出異常:UnsupportOperationException

沒辦法,我只能再上百度,有大神說這是因爲開啓了硬件加速,所以導致異常,於是我再然後在Manifest.xml的application中加上android:hardwareAccelerated=”false”,這是關閉硬件加速,結果沒有錯誤了,界面卻又無法顯示了,手指接觸屏幕也不會有痕跡出現,.還得去查看硬件加速的問題……>>>>>>>>>

<application
    android:hardwareAccelerated="false"

還有的童鞋發現在copy之後,因爲內存中會存在多張圖片,會導致內存溢出OOM,嗯,好吧,如果以後想用setBitmap方法,還是省省吧,改用drawBitmap比較好一點,不會有這麼多的麻煩。
不過需要注意的是,使用drawbitmap時,要不能使用過大的圖片,否則會導致OOM,一般300Kb的就可以,再大就有可能導致畫面卡頓甚至卡死,不過我發現一個奇怪的現象,在我的塗鴉界面中,如果把傳入的
copy之後再drawBitmap,雖然界面還是會卡頓,但不會直接退出。

bitmap大小=圖片長度(px)*圖片寬度(px)*單位像素佔用的字節數
單位像素所佔字節數就是圖片的色彩模式,安卓默認爲四個字節。
在BitmapFactory.Options.inPreferredConfig這裏可以找到,一共有4種, ARGB代表:A 透明度 , R 紅色, G 綠色, B 藍色。

圖片的壓縮方法可以參考以下博客:
android圖片壓縮的三種算法

這是質量壓縮算法的實現:

File file=new File(Environment.getExternalStorageDirectory() + "/" + "bitmap.jpg");
if (file.exists()) {
 int p=90;
 Bitmap b=BitmapFactory.decodeFile(file.getPath());
 while(b.getByteCount()>1024*300&&p>20){
 //這裏爲了明顯對比,進行的是破壞性實驗,把圖片最低壓縮爲30%,可以自己實驗看下效果。
     try {
         b.compress(Bitmap.CompressFormat.JPEG,p--,new FileOutputStream(file));
         b=BitmapFactory.decodeFile(file.getPath());
     } catch (FileNotFoundException e) {
         Toast.makeText(MainActicity.this,"bitmap can't compress",Toast.LENGTH_SHORT).show();
         break;
     }
 }
}

按比例壓縮方法:

private Bitmap compressPicture(String pathName,int height,int width){//按比例壓縮
    BitmapFactory.Options options=new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    //設置options只讀取圖片的大小。
    Bitmap bitmap = BitmapFactory.decodeFile(Environment.getExternalStorageDirectory()+"/"+pathName,options);//此時返回bm爲空,但必須有這一步,這是獲取圖片options的。
    options.inJustDecodeBounds=false;
    int h=options.outHeight;
    int w=options.outWidth;
    //得到圖片的寬高,
    int zip= h/height>w/width?h/height:w/width;
    options.inSampleSize=zip;//設置縮放的比例,此時改變了像素大小,那麼bitmap大小就會改變。
    bitmap=BitmapFactory.decodeFile(Environment.getExternalStorageDirectory()+"/"+pathName,options);
    return bitmap;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章