Android中的Picasso实现圆角图片
16lz
2021-12-04
注:有关Picasso的基本介绍,请参考我的上篇博客
Android图形缓存库Picasso的使用
Picasso支持自定义Transformation
public class CropSquareTransformation implements Transformation { @Override public Bitmap transform(Bitmap source) { int size = Math.min(source.getWidth(), source.getHeight()); int x = (source.getWidth() - size) / 2; int y = (source.getHeight() - size) / 2; Bitmap result = Bitmap.createBitmap(source, x, y, size, size); if (result != source) { source.recycle(); } return result; } @Override public String key() { return "square()"; }}
使用
Picasso.with(context).load(url).transform(new CropSquareTransformation()).placeholder(R.drawable.user_placeholder).error(R.drawable.user_placeholder_error).into(imageView);
参考:RoundedImageView
RoundedImageView:https://github.com/vinc3m1/RoundedImageView
实现RoundedTransformationBuilder
Transformation transformation = new RoundedTransformationBuilder().borderColor(Color.TRANSPARENT).borderWidthDp(0).oval(false).build();
Picasso.with(context).load(persons.get(position).getPictureUrl().transform(transformation).placeholder(R.mipmap.image_default)//没有加载图片时显示的默认图像.error(R.mipmap.image_default)// 图像加载错误时显示的图像.into(holder.mImg);// 被加载的控件
注意:
图片的scaleType=centerCrop时没有圆角的效果
android:scaleType="fitXY"
才有圆角效果
实现效果如下:
RoundedTransformationBuilder.java代码
/** Copyright (C) 2015 Vincent Mi** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at** http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/package com.zhoujian.picasso.transform;import android.content.res.ColorStateList;import android.content.res.Resources;import android.graphics.Bitmap;import android.util.DisplayMetrics;import android.util.TypedValue;import android.widget.ImageView;import com.squareup.picasso.Transformation;import java.util.Arrays;public final class RoundedTransformationBuilder { private final DisplayMetrics mDisplayMetrics; private float[] mCornerRadii = new float[] { 0, 0, 0, 0 }; private boolean mOval = false; private float mBorderWidth = 0; private ColorStateList mBorderColor = ColorStateList.valueOf(RoundedDrawable.DEFAULT_BORDER_COLOR); private ImageView.ScaleType mScaleType = ImageView.ScaleType.FIT_CENTER; public RoundedTransformationBuilder() { mDisplayMetrics = Resources.getSystem().getDisplayMetrics(); } public RoundedTransformationBuilder scaleType(ImageView.ScaleType scaleType) { mScaleType = scaleType; return this; } /** * Set corner radius for all corners in px. * * @param radius the radius in px * @return the builder for chaining. */ public RoundedTransformationBuilder cornerRadius(float radius) { mCornerRadii[Corner.TOP_LEFT] = radius; mCornerRadii[Corner.TOP_RIGHT] = radius; mCornerRadii[Corner.BOTTOM_RIGHT] = radius; mCornerRadii[Corner.BOTTOM_LEFT] = radius; return this; } /** * Set corner radius for a specific corner in px. * * @param corner the corner to set. * @param radius the radius in px. * @return the builder for chaning. */ public RoundedTransformationBuilder cornerRadius(int corner, float radius) { mCornerRadii[corner] = radius; return this; } /** * Set corner radius for all corners in density independent pixels. * * @param radius the radius in density independent pixels. * @return the builder for chaining. */ public RoundedTransformationBuilder cornerRadiusDp(float radius) { return cornerRadius( TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, radius, mDisplayMetrics)); } /** * Set corner radius for a specific corner in density independent pixels. * * @param corner the corner to set * @param radius the radius in density independent pixels. * @return the builder for chaining. */ public RoundedTransformationBuilder cornerRadiusDp(int corner, float radius) { return cornerRadius(corner, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, radius, mDisplayMetrics)); } /** * Set the border width in pixels. * * @param width border width in pixels. * @return the builder for chaining. */ public RoundedTransformationBuilder borderWidth(float width) { mBorderWidth = width; return this; } /** * Set the border width in density independent pixels. * * @param width border width in density independent pixels. * @return the builder for chaining. */ public RoundedTransformationBuilder borderWidthDp(float width) { mBorderWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, width, mDisplayMetrics); return this; } /** * Set the border color. * * @param color the color to set. * @return the builder for chaining. */ public RoundedTransformationBuilder borderColor(int color) { mBorderColor = ColorStateList.valueOf(color); return this; } /** * Set the border color as a {@link ColorStateList}. * * @param colors the {@link ColorStateList} to set. * @return the builder for chaining. */ public RoundedTransformationBuilder borderColor(ColorStateList colors) { mBorderColor = colors; return this; } /** * Sets whether the image should be oval or not. * * @param oval if the image should be oval. * @return the builder for chaining. */ public RoundedTransformationBuilder oval(boolean oval) { mOval = oval; return this; } /** * Creates a {@link Transformation} for use with picasso. * * @return the {@link Transformation} */ public Transformation build() { return new Transformation() { @Override public Bitmap transform(Bitmap source) { Bitmap transformed = RoundedDrawable.fromBitmap(source) .setScaleType(mScaleType) .setCornerRadius(mCornerRadii[0], mCornerRadii[1], mCornerRadii[2], mCornerRadii[3]) .setBorderWidth(mBorderWidth) .setBorderColor(mBorderColor) .setOval(mOval) .toBitmap(); if (!source.equals(transformed)) { source.recycle(); } return transformed; } @Override public String key() { return "r:" + Arrays.toString(mCornerRadii) + "b:" + mBorderWidth + "c:" + mBorderColor + "o:" + mOval; } }; }}
相关类:RoundedDrawable.java和Corner.java
/** Copyright (C) 2015 Vincent Mi** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at** http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/package com.zhoujian.picasso.transform;import android.content.res.ColorStateList;import android.graphics.Bitmap;import android.graphics.Bitmap.Config;import android.graphics.BitmapShader;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.ColorFilter;import android.graphics.Matrix;import android.graphics.Paint;import android.graphics.PixelFormat;import android.graphics.Rect;import android.graphics.RectF;import android.graphics.Shader;import android.graphics.drawable.BitmapDrawable;import android.graphics.drawable.Drawable;import android.graphics.drawable.LayerDrawable;import android.support.annotation.ColorInt;import android.support.annotation.NonNull;import android.util.Log;import android.widget.ImageView.ScaleType;import java.util.HashSet;import java.util.Set;@SuppressWarnings("UnusedDeclaration")public class RoundedDrawable extends Drawable { public static final String TAG = "RoundedDrawable"; public static final int DEFAULT_BORDER_COLOR = Color.BLACK; private final RectF mBounds = new RectF(); private final RectF mDrawableRect = new RectF(); private final RectF mBitmapRect = new RectF(); private final Bitmap mBitmap; private final Paint mBitmapPaint; private final int mBitmapWidth; private final int mBitmapHeight; private final RectF mBorderRect = new RectF(); private final Paint mBorderPaint; private final Matrix mShaderMatrix = new Matrix(); private final RectF mSquareCornersRect = new RectF(); private Shader.TileMode mTileModeX = Shader.TileMode.CLAMP; private Shader.TileMode mTileModeY = Shader.TileMode.CLAMP; private boolean mRebuildShader = true; // [ topLeft, topRight, bottomLeft, bottomRight ] private float mCornerRadius = 0f; private final boolean[] mCornersRounded = new boolean[] { true, true, true, true }; private boolean mOval = false; private float mBorderWidth = 0; private ColorStateList mBorderColor = ColorStateList.valueOf(DEFAULT_BORDER_COLOR); private ScaleType mScaleType = ScaleType.FIT_CENTER; public RoundedDrawable(Bitmap bitmap) { mBitmap = bitmap; mBitmapWidth = bitmap.getWidth(); mBitmapHeight = bitmap.getHeight(); mBitmapRect.set(0, 0, mBitmapWidth, mBitmapHeight); mBitmapPaint = new Paint(); mBitmapPaint.setStyle(Paint.Style.FILL); mBitmapPaint.setAntiAlias(true); mBorderPaint = new Paint(); mBorderPaint.setStyle(Paint.Style.STROKE); mBorderPaint.setAntiAlias(true); mBorderPaint.setColor(mBorderColor.getColorForState(getState(), DEFAULT_BORDER_COLOR)); mBorderPaint.setStrokeWidth(mBorderWidth); } public static RoundedDrawable fromBitmap(Bitmap bitmap) { if (bitmap != null) { return new RoundedDrawable(bitmap); } else { return null; } } public static Drawable fromDrawable(Drawable drawable) { if (drawable != null) { if (drawable instanceof RoundedDrawable) { // just return if it's already a RoundedDrawable return drawable; } else if (drawable instanceof LayerDrawable) { LayerDrawable ld = (LayerDrawable) drawable; int num = ld.getNumberOfLayers(); // loop through layers to and change to RoundedDrawables if possible for (int i = 0; i < num; i++) { Drawable d = ld.getDrawable(i); ld.setDrawableByLayerId(ld.getId(i), fromDrawable(d)); } return ld; } // try to get a bitmap from the drawable and Bitmap bm = drawableToBitmap(drawable); if (bm != null) { return new RoundedDrawable(bm); } } return drawable; } public static Bitmap drawableToBitmap(Drawable drawable) { if (drawable instanceof BitmapDrawable) { return ((BitmapDrawable) drawable).getBitmap(); } Bitmap bitmap; int width = Math.max(drawable.getIntrinsicWidth(), 2); int height = Math.max(drawable.getIntrinsicHeight(), 2); try { bitmap = Bitmap.createBitmap(width, height, Config.ARGB_8888); Canvas canvas = new Canvas(bitmap); drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); drawable.draw(canvas); } catch (Exception e) { e.printStackTrace(); Log.w(TAG, "Failed to create bitmap from drawable!"); bitmap = null; } return bitmap; } public Bitmap getSourceBitmap() { return mBitmap; } @Override public boolean isStateful() { return mBorderColor.isStateful(); } @Override protected boolean onStateChange(int[] state) { int newColor = mBorderColor.getColorForState(state, 0); if (mBorderPaint.getColor() != newColor) { mBorderPaint.setColor(newColor); return true; } else { return super.onStateChange(state); } } private void updateShaderMatrix() { float scale; float dx; float dy; switch (mScaleType) { case CENTER: mBorderRect.set(mBounds); mBorderRect.inset(mBorderWidth / 2, mBorderWidth / 2); mShaderMatrix.reset(); mShaderMatrix.setTranslate((int) ((mBorderRect.width() - mBitmapWidth) * 0.5f + 0.5f), (int) ((mBorderRect.height() - mBitmapHeight) * 0.5f + 0.5f)); break; case CENTER_CROP: mBorderRect.set(mBounds); mBorderRect.inset(mBorderWidth / 2, mBorderWidth / 2); mShaderMatrix.reset(); dx = 0; dy = 0; if (mBitmapWidth * mBorderRect.height() > mBorderRect.width() * mBitmapHeight) { scale = mBorderRect.height() / (float) mBitmapHeight; dx = (mBorderRect.width() - mBitmapWidth * scale) * 0.5f; } else { scale = mBorderRect.width() / (float) mBitmapWidth; dy = (mBorderRect.height() - mBitmapHeight * scale) * 0.5f; } mShaderMatrix.setScale(scale, scale); mShaderMatrix.postTranslate((int) (dx + 0.5f) + mBorderWidth / 2, (int) (dy + 0.5f) + mBorderWidth / 2); break; case CENTER_INSIDE: mShaderMatrix.reset(); if (mBitmapWidth <= mBounds.width() && mBitmapHeight <= mBounds.height()) { scale = 1.0f; } else { scale = Math.min(mBounds.width() / (float) mBitmapWidth, mBounds.height() / (float) mBitmapHeight); } dx = (int) ((mBounds.width() - mBitmapWidth * scale) * 0.5f + 0.5f); dy = (int) ((mBounds.height() - mBitmapHeight * scale) * 0.5f + 0.5f); mShaderMatrix.setScale(scale, scale); mShaderMatrix.postTranslate(dx, dy); mBorderRect.set(mBitmapRect); mShaderMatrix.mapRect(mBorderRect); mBorderRect.inset(mBorderWidth / 2, mBorderWidth / 2); mShaderMatrix.setRectToRect(mBitmapRect, mBorderRect, Matrix.ScaleToFit.FILL); break; default: case FIT_CENTER: mBorderRect.set(mBitmapRect); mShaderMatrix.setRectToRect(mBitmapRect, mBounds, Matrix.ScaleToFit.CENTER); mShaderMatrix.mapRect(mBorderRect); mBorderRect.inset(mBorderWidth / 2, mBorderWidth / 2); mShaderMatrix.setRectToRect(mBitmapRect, mBorderRect, Matrix.ScaleToFit.FILL); break; case FIT_END: mBorderRect.set(mBitmapRect); mShaderMatrix.setRectToRect(mBitmapRect, mBounds, Matrix.ScaleToFit.END); mShaderMatrix.mapRect(mBorderRect); mBorderRect.inset(mBorderWidth / 2, mBorderWidth / 2); mShaderMatrix.setRectToRect(mBitmapRect, mBorderRect, Matrix.ScaleToFit.FILL); break; case FIT_START: mBorderRect.set(mBitmapRect); mShaderMatrix.setRectToRect(mBitmapRect, mBounds, Matrix.ScaleToFit.START); mShaderMatrix.mapRect(mBorderRect); mBorderRect.inset(mBorderWidth / 2, mBorderWidth / 2); mShaderMatrix.setRectToRect(mBitmapRect, mBorderRect, Matrix.ScaleToFit.FILL); break; case FIT_XY: mBorderRect.set(mBounds); mBorderRect.inset(mBorderWidth / 2, mBorderWidth / 2); mShaderMatrix.reset(); mShaderMatrix.setRectToRect(mBitmapRect, mBorderRect, Matrix.ScaleToFit.FILL); break; } mDrawableRect.set(mBorderRect); } @Override protected void onBoundsChange(@NonNull Rect bounds) { super.onBoundsChange(bounds); mBounds.set(bounds); updateShaderMatrix(); } @Override public void draw(@NonNull Canvas canvas) { if (mRebuildShader) { BitmapShader bitmapShader = new BitmapShader(mBitmap, mTileModeX, mTileModeY); if (mTileModeX == Shader.TileMode.CLAMP && mTileModeY == Shader.TileMode.CLAMP) { bitmapShader.setLocalMatrix(mShaderMatrix); } mBitmapPaint.setShader(bitmapShader); mRebuildShader = false; } if (mOval) { if (mBorderWidth > 0) { canvas.drawOval(mDrawableRect, mBitmapPaint); canvas.drawOval(mBorderRect, mBorderPaint); } else { canvas.drawOval(mDrawableRect, mBitmapPaint); } } else { if (any(mCornersRounded)) { float radius = mCornerRadius; if (mBorderWidth > 0) { canvas.drawRoundRect(mDrawableRect, radius, radius, mBitmapPaint); canvas.drawRoundRect(mBorderRect, radius, radius, mBorderPaint); redrawBitmapForSquareCorners(canvas); redrawBorderForSquareCorners(canvas); } else { canvas.drawRoundRect(mDrawableRect, radius, radius, mBitmapPaint); redrawBitmapForSquareCorners(canvas); } } else { canvas.drawRect(mDrawableRect, mBitmapPaint); if (mBorderWidth > 0) { canvas.drawRect(mBorderRect, mBorderPaint); } } } } private void redrawBitmapForSquareCorners(Canvas canvas) { if (all(mCornersRounded)) { // no square corners return; } if (mCornerRadius == 0) { return; // no round corners } float left = mDrawableRect.left; float top = mDrawableRect.top; float right = left + mDrawableRect.width(); float bottom = top + mDrawableRect.height(); float radius = mCornerRadius; if (!mCornersRounded[Corner.TOP_LEFT]) { mSquareCornersRect.set(left, top, left + radius, top + radius); canvas.drawRect(mSquareCornersRect, mBitmapPaint); } if (!mCornersRounded[Corner.TOP_RIGHT]) { mSquareCornersRect.set(right - radius, top, right, radius); canvas.drawRect(mSquareCornersRect, mBitmapPaint); } if (!mCornersRounded[Corner.BOTTOM_RIGHT]) { mSquareCornersRect.set(right - radius, bottom - radius, right, bottom); canvas.drawRect(mSquareCornersRect, mBitmapPaint); } if (!mCornersRounded[Corner.BOTTOM_LEFT]) { mSquareCornersRect.set(left, bottom - radius, left + radius, bottom); canvas.drawRect(mSquareCornersRect, mBitmapPaint); } } private void redrawBorderForSquareCorners(Canvas canvas) { if (all(mCornersRounded)) { // no square corners return; } if (mCornerRadius == 0) { return; // no round corners } float left = mDrawableRect.left; float top = mDrawableRect.top; float right = left + mDrawableRect.width(); float bottom = top + mDrawableRect.height(); float radius = mCornerRadius; float offset = mBorderWidth / 2; if (!mCornersRounded[Corner.TOP_LEFT]) { canvas.drawLine(left - offset, top, left + radius, top, mBorderPaint); canvas.drawLine(left, top - offset, left, top + radius, mBorderPaint); } if (!mCornersRounded[Corner.TOP_RIGHT]) { canvas.drawLine(right - radius - offset, top, right, top, mBorderPaint); canvas.drawLine(right, top - offset, right, top + radius, mBorderPaint); } if (!mCornersRounded[Corner.BOTTOM_RIGHT]) { canvas.drawLine(right - radius - offset, bottom, right + offset, bottom, mBorderPaint); canvas.drawLine(right, bottom - radius, right, bottom, mBorderPaint); } if (!mCornersRounded[Corner.BOTTOM_LEFT]) { canvas.drawLine(left - offset, bottom, left + radius, bottom, mBorderPaint); canvas.drawLine(left, bottom - radius, left, bottom, mBorderPaint); } } @Override public int getOpacity() { return PixelFormat.TRANSLUCENT; } @Override public int getAlpha() { return mBitmapPaint.getAlpha(); } @Override public void setAlpha(int alpha) { mBitmapPaint.setAlpha(alpha); invalidateSelf(); } @Override public ColorFilter getColorFilter() { return mBitmapPaint.getColorFilter(); } @Override public void setColorFilter(ColorFilter cf) { mBitmapPaint.setColorFilter(cf); invalidateSelf(); } @Override public void setDither(boolean dither) { mBitmapPaint.setDither(dither); invalidateSelf(); } @Override public void setFilterBitmap(boolean filter) { mBitmapPaint.setFilterBitmap(filter); invalidateSelf(); } @Override public int getIntrinsicWidth() { return mBitmapWidth; } @Override public int getIntrinsicHeight() { return mBitmapHeight; } /** * @return the corner radius. */ public float getCornerRadius() { return mCornerRadius; } /** * @param corner the specific corner to get radius of. * @return the corner radius of the specified corner. */ public float getCornerRadius(@Corner int corner) { return mCornersRounded[corner] ? mCornerRadius : 0f; } /** * Sets all corners to the specified radius. * * @param radius the radius. * @return the {@link RoundedDrawable} for chaining. */ public RoundedDrawable setCornerRadius(float radius) { setCornerRadius(radius, radius, radius, radius); return this; } /** * Sets the corner radius of one specific corner. * * @param corner the corner. * @param radius the radius. * @return the {@link RoundedDrawable} for chaining. */ public RoundedDrawable setCornerRadius(@Corner int corner, float radius) { if (radius != 0 && mCornerRadius != 0 && mCornerRadius != radius) { throw new IllegalArgumentException("Multiple nonzero corner radii not yet supported."); } if (radius == 0) { if (only(corner, mCornersRounded)) { mCornerRadius = 0; } mCornersRounded[corner] = false; } else { if (mCornerRadius == 0) { mCornerRadius = radius; } mCornersRounded[corner] = true; } return this; } /** * Sets the corner radii of all the corners. * * @param topLeft top left corner radius. * @param topRight top right corner radius * @param bottomRight bototm right corner radius. * @param bottomLeft bottom left corner radius. * @return the {@link RoundedDrawable} for chaining. */ public RoundedDrawable setCornerRadius(float topLeft, float topRight, float bottomRight, float bottomLeft) { Set radiusSet = new HashSet<>(4); radiusSet.add(topLeft); radiusSet.add(topRight); radiusSet.add(bottomRight); radiusSet.add(bottomLeft); radiusSet.remove(0f); if (radiusSet.size() > 1) { throw new IllegalArgumentException("Multiple nonzero corner radii not yet supported."); } if (!radiusSet.isEmpty()) { float radius = radiusSet.iterator().next(); if (Float.isInfinite(radius) || Float.isNaN(radius) || radius < 0) { throw new IllegalArgumentException("Invalid radius value: " + radius); } mCornerRadius = radius; } else { mCornerRadius = 0f; } mCornersRounded[Corner.TOP_LEFT] = topLeft > 0; mCornersRounded[Corner.TOP_RIGHT] = topRight > 0; mCornersRounded[Corner.BOTTOM_RIGHT] = bottomRight > 0; mCornersRounded[Corner.BOTTOM_LEFT] = bottomLeft > 0; return this; } public float getBorderWidth() { return mBorderWidth; } public RoundedDrawable setBorderWidth(float width) { mBorderWidth = width; mBorderPaint.setStrokeWidth(mBorderWidth); return this; } public int getBorderColor() { return mBorderColor.getDefaultColor(); } public RoundedDrawable setBorderColor(@ColorInt int color) { return setBorderColor(ColorStateList.valueOf(color)); } public ColorStateList getBorderColors() { return mBorderColor; } public RoundedDrawable setBorderColor(ColorStateList colors) { mBorderColor = colors != null ? colors : ColorStateList.valueOf(0); mBorderPaint.setColor(mBorderColor.getColorForState(getState(), DEFAULT_BORDER_COLOR)); return this; } public boolean isOval() { return mOval; } public RoundedDrawable setOval(boolean oval) { mOval = oval; return this; } public ScaleType getScaleType() { return mScaleType; } public RoundedDrawable setScaleType(ScaleType scaleType) { if (scaleType == null) { scaleType = ScaleType.FIT_CENTER; } if (mScaleType != scaleType) { mScaleType = scaleType; updateShaderMatrix(); } return this; } public Shader.TileMode getTileModeX() { return mTileModeX; } public RoundedDrawable setTileModeX(Shader.TileMode tileModeX) { if (mTileModeX != tileModeX) { mTileModeX = tileModeX; mRebuildShader = true; invalidateSelf(); } return this; } public Shader.TileMode getTileModeY() { return mTileModeY; } public RoundedDrawable setTileModeY(Shader.TileMode tileModeY) { if (mTileModeY != tileModeY) { mTileModeY = tileModeY; mRebuildShader = true; invalidateSelf(); } return this; } private static boolean only(int index, boolean[] booleans) { for (int i = 0, len = booleans.length; i < len; i++) { if (booleans[i] != (i == index)) { return false; } } return true; } private static boolean any(boolean[] booleans) { for (boolean b : booleans) { if (b) { return true; } } return false; } private static boolean all(boolean[] booleans) { for (boolean b : booleans) { if (b) { return false; } } return true; } public Bitmap toBitmap() { return drawableToBitmap(this); }}
package com.zhoujian.picasso.transform;import android.support.annotation.IntDef;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;@Retention(RetentionPolicy.SOURCE)@IntDef({ Corner.TOP_LEFT, Corner.TOP_RIGHT, Corner.BOTTOM_LEFT, Corner.BOTTOM_RIGHT})public @interface Corner { int TOP_LEFT = 0; int TOP_RIGHT = 1; int BOTTOM_RIGHT = 2; int BOTTOM_LEFT = 3;}
源码下载
源码下载:https://github.com/zeke123/PicassoMy
更多相关文章
- Android(Java):Android(安卓)ListView checkbox 全选效果
- 高斯模糊效果移植到android系统
- Android(安卓)使用 TableLayout 布局拉伸宽度
- 14 Android(安卓)android 按钮效果的两种实现方法
- android旋转图像Matrix
- Android音乐播放器之经典布局
- Android(安卓)SmartRecyclerView
- android图像变为黑白
- Android(安卓)图片渐变切换效果