編輯:關於Android編程
開發應用中圖片的使用是必不可少的,Android系統提供了豐富的圖片支持功能。我們除了可以使Drawable資源庫,還可以使用Bitmap、Picture類去創建圖片,也可以使用Canvas、Paint、Path類等去繪制我們滿意的圖片。在自定義控件時,這些API使用尤為常見。因此,小編覺得有必要簡單的做個小總結。
那就先從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, 我們稱之為“畫布“,主要適用於繪制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是用於繪制的畫筆,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可以沿著多個點繪制一條路徑, 在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中給我們提供了很多這樣的方法來添加不同的路徑:
自定義時鐘的Demo主要用到了Canvas以及Paint方面的知識,來看看代碼吧:
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } }主活動中僅僅加載了一個布局。
在布局文件中放置我們自定義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()。
運行結果如下:
異步任務AsyncTask AsyncTask主要用來更新UI線程,比較耗時的操作可以在AsyncTask中使用。 AsyncTask是個抽象類,使用時需要繼承這個類
前言默認情況下,Android Studio設置新的項目並且部署到模擬器或者真機設備上,只需要點擊幾下。使用即時運行,你並不需要構建一個新的APK即可將改變後的方法和現有
之前在eclipse上顯示百度地圖的時候並沒有發生什麼不愉快的事,但是AS上簡直是RI了DOG。我簡直不能忍Baidu地圖API文檔,丑陋至極。1、新建一個項目,名為Ba
好長時間沒更博客了,最近一直在做比賽的一個項目,就是實現客戶端和PC端的文件互傳,其實一開始在看到這個題目的時候,完全不知道怎麼去實現,感覺一臉懵逼,後來在查閱了資料以及