Android 사용자 정의 AvatarImageView 프로필 이미지 표시 효과 구현
18941 단어 AndroidAvatarImageView두상
우리 프로젝트 의 이미지 디 스 플레이 는 일반적으로 원형 이지 만 가끔 은 여러 가지 스타일(규칙 적 인 모양 은 아 닙 니 다)을 제외 하지 않 습 니 다.예 를 들 어 지난번 UI 가 저 에 게 원형 아래 에 한 조각 이 빠 졌 습 니 다.저 희 는 일반적으로 사용자 정의 모양 을 실현 하 는 도형 은 세 가지 방식 이 있 습 니 다.Porter DuffXfermode,BitmapShader,ClipPath 입 니 다.다음은 제 가 여기 서 사용 하 는 첫 번 째 방식(실현 은 비교적 간단 합 니 다)을 설명 하 겠 습 니 다.
1.PorterDuffXfermode
이것 은 Tomas Proter 와 Tom Duff 가 명명 한 이미지 변환 모드 로 16 개의 매 거 진 값 으로 Canvas 상하 두 개의 그림 층 의 상호작용 을 제어 합 니 다(먼저 그린 그림 층 은 하층 에 있 습 니 다).
(파란색 은 윗 층 에 있다)
1.Porter Duff.Mode.CLEAR 가 그린 그림 은 캔버스 에 제출 되 지 않 습 니 다.
2.PorterDuff.Mode.SRC 상단 그림 표시
3.PorterDuff.Mode.DST 아래 그림 보이 기
4.PorterDuff.Mode.SRC_오 버 는 정상적으로 디 스 플레이 를 그립 니 다.상하 부 는 폴 더 캡 을 그립 니 다.
5.PorterDuff.Mode.DST_오 버,위아래 다 나 와.아래쪽 이 위 에 표 시 됩 니 다.
6.PorterDuff.Mode.SRC_IN 은 두 겹 으로 교 집합 을 그립 니 다.상층 부 를 드러내다.
7.PorterDuff.Mode.DST_IN 은 두 겹 으로 교 집합 을 그립 니 다.아래쪽 보이 기.
8.PorterDuff.Mode.SRC_OUT 는 상부 에서 비 교 집합 부분 을 그립 니 다.
9.PorterDuff.Mode.DST_OUT 는 아래쪽 에서 비 교 집합 부분 을 그립 니 다.
10.PorterDuff.Mode.SRC_ATOP 는 하층부 비 교 집합 부분 과 상층 부 교 집합 부분 을 취한 다.
11.PorterDuff.Mode.DST_ATOP 는 상층 의 비 교 집합 부분 과 하층 의 교 집합 부분 을 취한 다.
12.Porter Duff.Mode.XOR 이상 또는:두 그림 의 교차 부분 제거
13.PorterDuff.Mode.DARKEN 은 두 개의 그림 을 가 져 와 모든 영역 을 만 들 고 교차 부분 색상 이 진 화 됩 니 다.
14.Porter Duff.Mode.LIGHTEN 은 두 개의 그림 을 모두 취하 고 교차 부분 색상 을 밝 게 합 니 다.
15.Porter Duff.Mode.MULTIPLY 두 그림 의 교차 부분 을 중첩 한 후 색상
16.Porter Duff.Mode.Screen 은 두 그림 의 모든 영역 을 취하 고 교차 부분 은 투명 색 으로 변 합 니 다.
1.1 사고방식
Ps 를 할 줄 아 는 친 구 는 두 개의 그림 이 있다 면 위의 그림 을 아래 그림 의 모양 으로 자 르 려 면 아래 그림 의 선택 영역 을 바 꾸 고 위의 그림 을 선택 하면 됩 니 다.
그러면 저 희 는 Porter Duff.Mode 의 SRC 를 이용 할 수 있 습 니 다.IN 또는 DSTIN 은 두 개의 그림 의 교 집합 을 얻어 서 우리 가 원 하 는 여러 가지 스타일 로 그림 을 자 릅 니 다.우 리 는 형상 도 층 과 표시 도 층 이 필요 하 다.또한 그림 층 을 전면적으로 덮어 쓰 려 고 합 니 다.
1.2 실현
ImageView 를 계승 하여 imageview 의 네 가지 setImage 방법(더 좋 은 호환성 을 위해)을 복사 하여 setImageDrawable 방법 에서 전경 그림 을 얻 었 습 니 다.
@Override
public void setImageBitmap(Bitmap bm) {
super.setImageBitmap(bm);
mBitmap = bm;
setBitmaps();
}
/**
* src
*/
@Override
public void setImageDrawable(Drawable drawable) {
super.setImageDrawable(drawable);
mBitmap = getBitmapFromDrawable(drawable);
setBitmaps();
}
@Override
public void setImageResource(int resId) {
super.setImageResource(resId);
mBitmap = getBitmapFromDrawable(getDrawable());
setBitmaps();
}
@Override
public void setImageURI(Uri uri) {
super.setImageURI(uri);
mBitmap = getBitmapFromDrawable(getDrawable());
setBitmaps();
}
배경 그림 을 가 져 오 면 invalidate()는 onDraw 방법 으로 다시 그립 니 다.
private void setBitmaps(){
if(null==getBackground()){
throw new IllegalArgumentException(String.format("background is null."));
}else{
backgroundBitmap = getBitmapFromDrawable(getBackground());
invalidate();
}
}
물론 onMeasure 에서 view 의 높이 와 너 비 를 가 져 와 두 그림 의 크기 를 조정 해 야 합 니 다.
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec,heightMeasureSpec);
/**
* , MeasureSpec.EXACTLY ( match_parent , accurate )
*
*/
viewWidth = MeasureSpec.getSize(widthMeasureSpec);
viewHeight = MeasureSpec.getSize(heightMeasureSpec);
}
그 다음 에 Porter Duff Xfermode 를 사용 하여 두 그림 의 상호작용 을 최종 Bitmap 로 만 듭 니 다.
private Bitmap createImage()
{
Paint paint = new Paint();
paint.setAntiAlias(true);
Bitmap finalBmp = Bitmap.createBitmap(viewWidth,viewHeight, Bitmap.Config.ARGB_8888);
/**
*
*/
Canvas canvas = new Canvas(finalBmp);
/**
*
*/
canvas.drawBitmap(backgroundBitmap, 0, 0, paint);
/**
* SRC_IN, , 。
*/
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
/**
*
*/
canvas.drawBitmap(mBitmap, 0, 0, paint);
return finalBmp;
}
다시 그리 기 시작 합 니 다.
@Override
protected void onDraw(Canvas canvas) {
if(mBitmap!=null && backgroundBitmap!=null){
/**
*
*/
int min = Math.min(viewWidth, viewHeight);
backgroundBitmap = Bitmap.createScaledBitmap(backgroundBitmap, min, min, false);
mBitmap = Bitmap.createScaledBitmap(mBitmap, min, min, false);
/**
* bitmap
*/
canvas.drawBitmap(createImage(), 0, 0, null);
}
}
도구 함수 붙 이기
/**
* Drawable Bitmap
*/
private Bitmap getBitmapFromDrawable(Drawable drawable) {
super.setScaleType(ScaleType.CENTER_CROP);
if (drawable == null) {
return null;
}
if (drawable instanceof BitmapDrawable) {
return ((BitmapDrawable) drawable).getBitmap();
}
try {
Bitmap bitmap;
bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(),Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
drawable.draw(canvas);
return bitmap;
} catch (OutOfMemoryError e) {
return null;
}
}
간단 하지 않 습 니까?그리고 사 용 했 습 니 다.1.3 사용
속성 값 이 추가 되 지 않 았 습 니 다.레이아웃 에서 다음 과 같이 사용 합 니 다.
<cn.fanrunqi.avatarimageview.AvatarImageView
android:layout_width="100dp"
android:layout_height="100dp"
android:background="@drawable/oval_shape"
<!--android:background="@drawable/bg_a"-->
android:src="@drawable/c"
/>
①、여기 안 드 로 이 드:background 는 우리 의 모양 그림 층 을 정의 합 니 다.xxx 일 수 있 습 니 다.shape.xml 의 레이아웃 파일,예 를 들 어.
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval"
android:useLevel="false">
<solid android:color="#fff"/>
<size android:width="100dp"
android:height="100dp"/>
</shape>
그림 일 수도 있 습 니 다.②、android:src 는 우리 가 표시 할 프로필 사진 을 정의 합 니 다.
원본 주소
2.BitmapShader
우 리 는 착색 기 라 고 할 수 있 는데,아래 코드 를 통 해 착색 기 를 얻 을 수 있다.
mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
매개 변수
① mBitmap:당신 이 그 릴 그림
② emun Shader.TileMode 는 세 가지 착색 모드 를 정의 합 니 다.
CLAMP 스 트 레 칭
반복 반복
MIRROR 미 러
해상도 와 컴퓨터 화면 이 다른 그림 을 벽지 로 설정 할 때 선택 한 세 가지 방식 과 같다.
우 리 는 붓 에 착색 기 를 설치 할 수 있 습 니 다.그러면 붓 은 canvas 의 해당 모양 에 우리 의 그림 mBitmap 를 그 릴 수 있 습 니 다.
mBitmapPaint.setShader(mBitmapShader);
canvas.drawCircle(getWidth() / 2, getHeight() / 2, mDrawableRadius, mBitmapPaint);
물론 우리 가 일반적으로 설정 하 는 모드 는 CLAMP 스 트 레 칭(그림 mBitmap 의 너비 가 View 보다 작 을 때 스 트 레 칭)이지 만 우 리 는 일반적으로 스 트 레 칭(변형)하지 않 기 때문에 착색 기 에 matrix 를 설정 하여 그림 을 적당 하 게 확대 하거나 축소 해 야 한다.
mBitmapShader.setLocalMatrix(mShaderMatrix);
2.1 CircleImageView 소스 코드 분석유명한 프로젝트 CircleImageView 는 착색 기로 이 루어 진 것 으로 실현 사고방식 에 이미 말 했 듯 이 코드 에 상세 한 주석 이 있어 이해 하기 에는 문제 가 없 을 것 이다.
public class CircleImageView extends ImageView {
//
private static final ScaleType SCALE_TYPE = ScaleType.CENTER_CROP;
private static final Bitmap.Config BITMAP_CONFIG = Bitmap.Config.ARGB_8888;
private static final int COLORDRAWABLE_DIMENSION = 2;
//
private static final int DEFAULT_BORDER_WIDTH = 0;
//
private static final int DEFAULT_BORDER_COLOR = Color.BLACK;
private static final boolean DEFAULT_BORDER_OVERLAY = false;
private final RectF mDrawableRect = new RectF();
private final RectF mBorderRect = new RectF();
private final Matrix mShaderMatrix = new Matrix();
// mBitmapShader canvas (mBitmapShader bitmap )
private final Paint mBitmapPaint = new Paint();
// , bitmap ,
private final Paint mBorderPaint = new Paint();
//
private int mBorderColor = DEFAULT_BORDER_COLOR;
private int mBorderWidth = DEFAULT_BORDER_WIDTH;
private Bitmap mBitmap;
private BitmapShader mBitmapShader; //
private int mBitmapWidth; //
private int mBitmapHeight; //
private float mDrawableRadius;//
private float mBorderRadius;//
private ColorFilter mColorFilter;
// false
private boolean mReady;
private boolean mSetupPending;
private boolean mBorderOverlay;
//
public CircleImageView(Context context) {
super(context);
init();
}
//
public CircleImageView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
/**
*
*/
public CircleImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CircleImageView, defStyle, 0);
// TypedArray getXXXX xml ;
//
mBorderWidth = a.getDimensionPixelSize(R.styleable.CircleImageView_border_width, DEFAULT_BORDER_WIDTH);
//
mBorderColor = a.getColor(R.styleable.CircleImageView_border_color, DEFAULT_BORDER_COLOR);
mBorderOverlay = a.getBoolean(R.styleable.CircleImageView_border_overlay, DEFAULT_BORDER_OVERLAY);
// recycle() TypedArray,
a.recycle();
init();
}
/**
* setup
*/
private void init() {
// ScaleType CENTER_CROP, , 。
super.setScaleType(SCALE_TYPE);
mReady = true;
if (mSetupPending) {
setup();
mSetupPending = false;
}
}
@Override
public ScaleType getScaleType() {
return SCALE_TYPE;
}
/**
* imageview CENTER_CROP
*
* @param scaleType
*/
@Override
public void setScaleType(ScaleType scaleType) {
if (scaleType != SCALE_TYPE) {
throw new IllegalArgumentException(String.format("ScaleType %s not supported.", scaleType));
}
}
@Override
public void setAdjustViewBounds(boolean adjustViewBounds) {
if (adjustViewBounds) {
throw new IllegalArgumentException("adjustViewBounds not supported.");
}
}
@Override
protected void onDraw(Canvas canvas) {
//
if (getDrawable() == null) {
return;
}
// mBitmapPaint
canvas.drawCircle(getWidth() / 2, getHeight() / 2, mDrawableRadius, mBitmapPaint);
// 0 mBorderPaint
if (mBorderWidth != 0) {
canvas.drawCircle(getWidth() / 2, getHeight() / 2, mBorderRadius, mBorderPaint);
}
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
setup();
}
public int getBorderColor() {
return mBorderColor;
}
public void setBorderColor(int borderColor) {
if (borderColor == mBorderColor) {
return;
}
mBorderColor = borderColor;
mBorderPaint.setColor(mBorderColor);
invalidate();
}
public void setBorderColorResource( int borderColorRes) {
setBorderColor(getContext().getResources().getColor(borderColorRes));
}
public int getBorderWidth() {
return mBorderWidth;
}
public void setBorderWidth(int borderWidth) {
if (borderWidth == mBorderWidth) {
return;
}
mBorderWidth = borderWidth;
setup();
}
public boolean isBorderOverlay() {
return mBorderOverlay;
}
public void setBorderOverlay(boolean borderOverlay) {
if (borderOverlay == mBorderOverlay) {
return;
}
mBorderOverlay = borderOverlay;
setup();
}
/**
*
* ImageView setImageXxx()
*
* @param bm
*/
@Override
public void setImageBitmap(Bitmap bm) {
super.setImageBitmap(bm);
mBitmap = bm;
setup();
}
@Override
public void setImageDrawable(Drawable drawable) {
super.setImageDrawable(drawable);
mBitmap = getBitmapFromDrawable(drawable);
setup();
}
@Override
public void setImageResource( int resId) {
super.setImageResource(resId);
mBitmap = getBitmapFromDrawable(getDrawable());
setup();
}
@Override
public void setImageURI(Uri uri) {
super.setImageURI(uri);
mBitmap = getBitmapFromDrawable(getDrawable());
setup();
}
@Override
public void setColorFilter(ColorFilter cf) {
if (cf == mColorFilter) {
return;
}
mColorFilter = cf;
mBitmapPaint.setColorFilter(mColorFilter);
invalidate();
}
/**
* Drawable Bitmap
* @param drawable
* @return
*/
private Bitmap getBitmapFromDrawable(Drawable drawable) {
if (drawable == null) {
return null;
}
if (drawable instanceof BitmapDrawable) {
// 。 bitmap
return ((BitmapDrawable) drawable).getBitmap();
}
try {
Bitmap bitmap;
if (drawable instanceof ColorDrawable) {
bitmap = Bitmap.createBitmap(COLORDRAWABLE_DIMENSION, COLORDRAWABLE_DIMENSION, BITMAP_CONFIG);
} else {
bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), BITMAP_CONFIG);
}
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
drawable.draw(canvas);
return bitmap;
} catch (OutOfMemoryError e) {
return null;
}
}
/**
* , (Paint) :
* BitmapShader Bitmap , ,
* updateShaderMatrix() invalidate() ;
*/
private void setup() {
// mReady false, if
// mSetupPending true , 。
if (!mReady) {
mSetupPending = true;
return;
}
//
if (mBitmap == null) {
return;
}
// , mBitmap ,
mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
//
mBitmapPaint.setAntiAlias(true);
//
mBitmapPaint.setShader(mBitmapShader);
//
mBorderPaint.setStyle(Paint.Style.STROKE);//
mBorderPaint.setAntiAlias(true);
mBorderPaint.setColor(mBorderColor); //
mBorderPaint.setStrokeWidth(mBorderWidth);//
//
mBitmapHeight = mBitmap.getHeight();
mBitmapWidth = mBitmap.getWidth();
// , CircleImageView ,
mBorderRect.set(0, 0, getWidth(), getHeight());
// ( ) , mBorderRect
mBorderRadius = Math.min((mBorderRect.height() - mBorderWidth) / 2, (mBorderRect.width() - mBorderWidth) / 2);
// mBorderRect(CircleImageView )
mDrawableRect.set(mBorderRect);
if (!mBorderOverlay) {
//demo
// inset mBorderRect
mDrawableRect.inset(mBorderWidth, mBorderWidth);
}
// ,
mDrawableRadius = Math.min(mDrawableRect.height() / 2, mDrawableRect.width() / 2);
// mBitmap
updateShaderMatrix();
// ondraw()
invalidate();
}
/**
* BitmapShader Matrix , , 。
* :
*/
private void updateShaderMatrix() {
float scale;
float dx = 0;
float dy = 0;
mShaderMatrix.set(null);
// (mBitmapWidth / mDrawableRect.width()) > (mBitmapHeight / mDrawableRect.height())
//
if (mBitmapWidth * mDrawableRect.height() > mDrawableRect.width() * mBitmapHeight) {
//y x y (mDrawableRect) )
scale = mDrawableRect.height() / (float) mBitmapHeight;
dx = (mDrawableRect.width() - mBitmapWidth * scale) * 0.5f;
} else {
//x y x (mDrawableRect) )
scale = mDrawableRect.width() / (float) mBitmapWidth;
dy = (mDrawableRect.height() - mBitmapHeight * scale) * 0.5f;
}
// shaeder , 。
mShaderMatrix.setScale(scale, scale);
//
mShaderMatrix.postTranslate((int) (dx + 0.5f) + mDrawableRect.left, (int) (dy + 0.5f) + mDrawableRect.top);
//
mBitmapShader.setLocalMatrix(mShaderMatrix);
}
}
3.ClipPath우 리 는 다음 과 같이 경 로 를 새로 만 든 다음 path.addxxx()방법 으로 경 로 를 얻 을 수 있다 는 것 을 알 고 있 습 니 다.
Path path = new Path();
그 다음 에 canvas 를 우리 경로 구역 의 모양 으로 자 를 수 있 습 니 다.바로 캔버스 모양 이 확정 되 었 고 마지막 으로 도형 만 그리 면 됩 니 다.다음 코드 는 도형 bitmap 를 원 에 그 려 서 원형 두상 을 얻 을 수 있 습 니 다.
@Override
protected void onDraw(Canvas canvas) {
Path path = new Path();
//
path.addCircle(float x, float y, mRadius, Direction.CCW);
// canvas
canvas.save();
// canvas
canvas.clipPath(path);
// Bitmap
canvas.drawBitmap(Bitmap,float left, float top, mPaint);
// Canvas
canvas.restore();
}
이런 방식 은 분명히 가장 간단 하 다.너 는 좌표 점 을 하나씩 추가 하여 하나의 경 로 를 형성 할 수 있다.하지만 모양 이 복잡 한 상황 에 서 는 첫 번 째 실현 이 편리 하 다.이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Bitrise에서 배포 어플리케이션 설정 테스트하기이 글은 Bitrise 광고 달력의 23일째 글입니다. 자체 또는 당사 등에서 Bitrise 구축 서비스를 사용합니다. 그나저나 며칠 전 Bitrise User Group Meetup #3에서 아래 슬라이드를 발표했...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.