編輯:關於Android編程
接著上一篇的問題來研究研究:
**問題來了:效果是有了,但有發現麼?我設置的scaleType只有fitxy
是有效果的,其他的都沒有效果了。設置為其他的scaleType都變成matrix那種效果了,也就是圖片默認從控件的左上角開始擺放。**
我們先看看ImageView的scaleType
以下內容來自www.2cto.com/kf/201411/348601.html
讓我們看看scaleType的各類效果圖:
ImageView的scaleType的屬性有好幾種,分別是matrix(默認)、center、centerCrop、centerInside、fitCenter、fitEnd、fitStart、fitXY
android:scaleType=”center”
保持原圖的大小,顯示在ImageView的中心。當原圖的size大於ImageView的size,超過部分裁剪處理。
android:scaleType=”centerCrop”
以填滿整個ImageView為目的,將原圖的中心對准ImageView的中心,等比例放大原圖,直到填滿ImageView為止(指的是ImageView的寬和高都要填滿),原圖超過ImageView的部分作裁剪處理。
android:scaleType=”centerInside”
以原圖完全顯示為目的,將圖片的內容完整居中顯示,通過按比例縮小原圖的size寬(高)等於或小於ImageView的寬(高)。如果原圖的size本身就小於ImageView的size,則原圖的size不作任何處理,居中顯示在ImageView。
android:scaleType=”matrix”
不改變原圖的大小,從ImageView的左上角開始繪制原圖,原圖超過ImageView的部分作裁剪處理。
android:scaleType=”fitCenter”
把原圖按比例擴大或縮小到ImageView的ImageView的高度,居中顯示
android:scaleType=”fitEnd”
把原圖按比例擴大(縮小)到ImageView的高度,顯示在ImageView的下部分位置
android:scaleType=”fitStart”
把原圖按比例擴大(縮小)到ImageView的高度,顯示在ImageView的上部分位置
android:scaleType=”fitXY”
把原圖按照指定的大小在View中顯示,拉伸顯示圖片,不保持原比例,填滿ImageView.
下面附上效果圖:
原圖為Pocoyo的頭像,上圖為原圖的size大於ImageView的size,下圖為原圖的size小於ImageView的size
vcTYo788L3N0cm9uZz48YnIgLz4NCs7Sw8e/tL+0SW1hZ2VWaWV31LTC6zwvcD4NCjxwcmUgY2xhc3M9"brush:java;">
private void configureBounds() {
if (mDrawable == null || !mHaveFrame) {
return;
}
final int dwidth = mDrawableWidth;
final int dheight = mDrawableHeight;
final int vwidth = getWidth() - mPaddingLeft - mPaddingRight;
final int vheight = getHeight() - mPaddingTop - mPaddingBottom;
final boolean fits = (dwidth < 0 || vwidth == dwidth)
&& (dheight < 0 || vheight == dheight);
if (dwidth <= 0 || dheight <= 0 || ScaleType.FIT_XY == mScaleType) {
/* If the drawable has no intrinsic size, or we're told to
scaletofit, then we just fill our entire view.
*/
mDrawable.setBounds(0, 0, vwidth, vheight);
mDrawMatrix = null;
} else {
......
}
}
我們看到這麼一行代碼:
mDrawable.setBounds(0, 0, vwidth, vheight);
當為fitxy的時候給mDrawable(也就是我們設置的那張圖片)設置了bounds為vwidth,vheight,也就是控件的寬高。所以fitxy時才會鋪滿整個屏幕的。
搞懂了fitxy,那麼matrix又是怎麼樣的呢?
我們看看onDraw方法:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mDrawable == null) {
return; // couldn't resolve the URI
}
if (mDrawableWidth == 0 || mDrawableHeight == 0) {
return; // nothing to draw (empty bounds)
}
if (mDrawMatrix == null && mPaddingTop == 0 && mPaddingLeft == 0) {
mDrawable.draw(canvas);
} else {
final int saveCount = canvas.getSaveCount();
canvas.save();
if (mCropToPadding) {
final int scrollX = mScrollX;
final int scrollY = mScrollY;
canvas.clipRect(scrollX + mPaddingLeft, scrollY + mPaddingTop,
scrollX + mRight - mLeft - mPaddingRight,
scrollY + mBottom - mTop - mPaddingBottom);
}
canvas.translate(mPaddingLeft, mPaddingTop);
if (mDrawMatrix != null) {
canvas.concat(mDrawMatrix);
}
mDrawable.draw(canvas);
canvas.restoreToCount(saveCount);
}
}
有點長,我們看到有一行代碼:
if (mDrawMatrix != null) {
canvas.concat(mDrawMatrix);
}
canvas.concat(mDrawMatrix);是指給canvas做一些改變,比如縮放、平移…… 也就是說源碼中通過我們設置的scaleType來通過算法計算mDrawMatrix ,然後再onDraw方法中賦給了canvas,但是我們重寫了onDraw方法,也就是說mDrawMatrix 壓根就不起作用了,所以當我們在RoundImageView中執行
//最後把我們准備好的Bitmap畫在canvas上
canvas.drawBitmap(bitmap,0,0,null);
的時候,圖片就是默認不縮放,原圖從控件的左上角開始擺放的。
到此,我們終於弄懂了我們遇到的問題,既然遇到了,那我們就解決下問題。
我們看看ImageView到底是怎麼縮放圖片的:
private void configureBounds() {
if (mDrawable == null || !mHaveFrame) {
return;
}
final int dwidth = mDrawableWidth;
final int dheight = mDrawableHeight;
final int vwidth = getWidth() - mPaddingLeft - mPaddingRight;
final int vheight = getHeight() - mPaddingTop - mPaddingBottom;
final boolean fits = (dwidth < 0 || vwidth == dwidth)
&& (dheight < 0 || vheight == dheight);
if (dwidth <= 0 || dheight <= 0 || ScaleType.FIT_XY == mScaleType) {
/* If the drawable has no intrinsic size, or we're told to
scaletofit, then we just fill our entire view.
*/
mDrawable.setBounds(0, 0, vwidth, vheight);
mDrawMatrix = null;
} else {
// We need to do the scaling ourself, so have the drawable
// use its native size.
mDrawable.setBounds(0, 0, dwidth, dheight);
if (ScaleType.MATRIX == mScaleType) {
// Use the specified matrix as-is.
if (mMatrix.isIdentity()) {
mDrawMatrix = null;
} else {
mDrawMatrix = mMatrix;
}
} else if (fits) {
// The bitmap fits exactly, no transform needed.
mDrawMatrix = null;
} else if (ScaleType.CENTER == mScaleType) {
// Center bitmap in view, no scaling.
mDrawMatrix = mMatrix;
mDrawMatrix.setTranslate(Math.round((vwidth - dwidth) * 0.5f),
Math.round((vheight - dheight) * 0.5f));
} else if (ScaleType.CENTER_CROP == mScaleType) {
mDrawMatrix = mMatrix;
float scale;
float dx = 0, dy = 0;
if (dwidth * vheight > vwidth * dheight) {
scale = (float) vheight / (float) dheight;
dx = (vwidth - dwidth * scale) * 0.5f;
} else {
scale = (float) vwidth / (float) dwidth;
dy = (vheight - dheight * scale) * 0.5f;
}
mDrawMatrix.setScale(scale, scale);
mDrawMatrix.postTranslate(Math.round(dx), Math.round(dy));
} else if (ScaleType.CENTER_INSIDE == mScaleType) {
mDrawMatrix = mMatrix;
float scale;
float dx;
float dy;
if (dwidth <= vwidth && dheight <= vheight) {
scale = 1.0f;
} else {
scale = Math.min((float) vwidth / (float) dwidth,
(float) vheight / (float) dheight);
}
dx = Math.round((vwidth - dwidth * scale) * 0.5f);
dy = Math.round((vheight - dheight * scale) * 0.5f);
mDrawMatrix.setScale(scale, scale);
mDrawMatrix.postTranslate(dx, dy);
} else {
// Generate the required transform.
mTempSrc.set(0, 0, dwidth, dheight);
mTempDst.set(0, 0, vwidth, vheight);
mDrawMatrix = mMatrix;
mDrawMatrix.setRectToRect(mTempSrc, mTempDst, scaleTypeToScaleToFit(mScaleType));
}
}
}
就是我們上面所說的,先通過我們設置的scaleType計算mDrawMatrix,
然後再onDraw方法中賦給canvans,再貼一遍ImageView的onDraw方法。
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mDrawable == null) {
return; // couldn't resolve the URI
}
if (mDrawableWidth == 0 || mDrawableHeight == 0) {
return; // nothing to draw (empty bounds)
}
if (mDrawMatrix == null && mPaddingTop == 0 && mPaddingLeft == 0) {
mDrawable.draw(canvas);
} else {
final int saveCount = canvas.getSaveCount();
canvas.save();
if (mCropToPadding) {
final int scrollX = mScrollX;
final int scrollY = mScrollY;
canvas.clipRect(scrollX + mPaddingLeft, scrollY + mPaddingTop,
scrollX + mRight - mLeft - mPaddingRight,
scrollY + mBottom - mTop - mPaddingBottom);
}
canvas.translate(mPaddingLeft, mPaddingTop);
if (mDrawMatrix != null) {
canvas.concat(mDrawMatrix);
}
mDrawable.draw(canvas);
canvas.restoreToCount(saveCount);
}
}
我們自己重寫了onDraw方法,也就是我們只需要像ImageView一樣,把configureBounds方法搬到我們的控件中就可以了,好了,我們試試:
@Override
protected void onDraw(Canvas canvas) {
Bitmap bitmap = mWeakReference==null?null:mWeakReference.get();
if(bitmap==null || bitmap.isRecycled()){
//獲取一下設置的圖片資源
Drawable drawable=getDrawable();
if(drawable!=null){
//創建一個空白畫布,用來畫模板跟原圖
bitmap=Bitmap.createBitmap(getWidth(),getHeight(),Bitmap.Config.ARGB_8888);
////修改過的代碼
Matrix matrix=null;
Canvas dstCanvas=new Canvas(bitmap);
dstCanvas.save();
if (getScaleType()==ScaleType.FIT_XY){
drawable.setBounds(0,0,getWidth(),getHeight());
matrix=null;
}else{
matrix=new Matrix();
configureBounds(drawable,matrix);
}
if(matrix!=null){
dstCanvas.concat(matrix);
}
drawable.draw(dstCanvas);
dstCanvas.restore();
////修改過的代碼
//畫模板
if(mMaskBitmap==null||mMaskBitmap.isRecycled()){
mMaskBitmap=getShapeBitmap();
}
dstCanvas.drawBitmap(mMaskBitmap,0,0,mPaint);
mPaint.setXfermode(null);
}
}
//最後把我們准備好的Bitmap畫在canvas上
canvas.drawBitmap(bitmap,0,0,null);
}
private void configureBounds(Drawable drawable, Matrix matrix) {
ScaleType mScaleType = getScaleType();
//獲取圖片的寬高
int dwidth = drawable.getIntrinsicWidth();
int dheight = drawable.getIntrinsicHeight();
int vwidth =getWidth();
int vheight = getHeight();
if (ScaleType.MATRIX == mScaleType) {
/////
} else if (ScaleType.CENTER_CROP == mScaleType) {
float scale;
float dx = 0, dy = 0;
if (dwidth * vheight > vwidth * dheight) {
scale = (float) vheight / (float) dheight;
dx = (vwidth - dwidth * scale) * 0.5f;
} else {
scale = (float) vwidth / (float) dwidth;
dy = (vheight - dheight * scale) * 0.5f;
}
matrix.setScale(scale, scale);
matrix.postTranslate(Math.round(dx), Math.round(dy));
} else if (ScaleType.CENTER_INSIDE == mScaleType) {
float scale;
float dx;
float dy;
if (dwidth <= vwidth && dheight <= vheight) {
scale = 1.0f;
} else {
scale = Math.min((float) vwidth / (float) dwidth,
(float) vheight / (float) dheight);
}
dx = Math.round((vwidth - dwidth * scale) * 0.5f);
dy = Math.round((vheight - dheight * scale) * 0.5f);
matrix.setScale(scale, scale);
matrix.postTranslate(dx, dy);
} else {
matrix.setRectToRect(new RectF(drawable.getBounds()), new RectF(0, 0, getWidth(), getHeight()), scaleTypeToScaleToFit(mScaleType));
}
}
直接拖的ImageView的源碼,不要問我算法為什麼是這樣,我也研究了蠻久,數學不好(^__^) 嘻嘻……
到這又郁悶了,scaleTypeToScaleToFit方法沒法copy了,怎麼辦? 反射呗!說干咱就干。
private Matrix.ScaleToFit scaleTypeToScaleToFit(ScaleType mScaleType) {
Class mClass=ImageView.class;
try {
Method method = mClass.getDeclaredMethod("scaleTypeToScaleToFit", new Class[]{ScaleType.class});
method.setAccessible(true);
if(method!=null){
Matrix.ScaleToFit fit = (Matrix.ScaleToFit) (method.invoke(null, new Object[]{mScaleType}));
return fit;
}
} catch (Exception e) {
e.printStackTrace();
}
return Matrix.ScaleToFit.FILL;
}
不懂的童鞋自己去腦補下javase的東西哈!!
到此算是寫完了,我們再次運行代碼:

終於是完美的呈現了,從來沒寫過這麼長的博客,小伙伴默默點個贊哈,大牛勿噴!!(^__^) 嘻嘻……
最後附上github地址:https://github.com/913453448/CircleViewDemo
Android的下拉刷新/上拉加載控件
事實上之所以會有之前的那篇博文的出現,是起因於前段時間自己在寫一個練手的App時很快就遇到這種需求。其實我們可以發現類似這樣下拉刷新、上拉加載的功能正在變得越來越普遍,可
Android中常用的五種數據存儲方式
第一種: 使用SharedPreferences存儲數據適用范圍:保存少量的數據,且這些數據的格式非常簡單:字符串型、基本類型的值。比如應用程序的各種配置信息(如是否打開
Android五大布局Layout
?? Android開發中,我們可能會遇到過一些很復雜的布局,對於初學者來說,可能腦子會嗡的一下,“這麼復雜!該怎麼整?!”。 不要擔心!再復雜的
ListView異步加載與緩存
該例子實現的是從網絡上異步獲取數據,包括圖片與文字,然後顯示在listView中,並對圖片進行內存緩存。程序截圖第一次錄制gif。。。效果很差首先定義布局主界面就一個li