Android 사용자 정의 View 메모리 청소 가속 볼 효과 구현
치타 청소 마스터 나 비슷 한 보안 소프트웨어 를 사용 한 적 이 있 습 니 다.모두 가 알 고 있 는 기능 이 있 습 니 다.그것 은 바로 메모리 청소 입 니 다.보 여 주 는 형식 은 둥 근 작은 공 을 통 해 메모리 크기 를 표시 하고 백분율 숫자 와 진도 바 의 형식 으로 정리 의 진 도 를 표시 하 는 것 입 니 다.본 고 는 이 효과 의 실현 과정 에 대해 상세 하 게 설명 할 것 이지 만 메모리 청소 의 실현 은 언급 되 지 않 을 것 이다.
미리 보기
최종 실현 효과 가 어떤 지 먼저 살 펴 보 겠 습 니 다.
위의 그림 에서 우 리 는 볼 수 있다.
① 가속 구 뷰 가 표 시 될 때 진도 게이지 와 백분율 숫자 는 0%에서 특정한 수치(60%)로 증가한다.
② 진도 게이지 가 멈 추 면 가운데 원 이 Y 축 을 따라 뒤 집 히 기 시작 하고 180 도 뒤 집 히 며 위의 백분율 숫자 에 미 러 효과 가 나타 나 지 않 습 니 다(아래 언급).
③ 사용자 가 이 작은 공 을 클릭 한 후에 메모 리 를 정리 하기 시작 하면 진도 바 와 백분율 숫자 는 0 으로 줄 어 들 고 0 에서 특정한 수치 로 증가 하 는 과정 을 거 친다.
실현 과정 상세 설명
사실 위의 효 과 는 필 자 는 치타 가 대사의 가속 구 를 정리 하 는 것 을 본 떠 서 이 루어 진 것 으로 약간 다 르 지만 대체적으로 형식 이 같다.독자 가 위의 효과 에 관심 이 있다 면 계속 읽 어 보 세 요.다음은 본문 부분 입 니 다.
1 단계 초기 화
우 리 는 먼저 LieBaoView 를 새로 만들어 야 합 니 다.자바,자체 View 를 계승 하여 다음 과 같은 구조 함 수 를 다시 씁 니 다.
public LieBaoView(Context context) {
super(context);
init();
}
public LieBaoView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public LieBaoView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
어떤 방식 으로 이 View 를 예화 하 든 init()방법 을 호출 합 니 다.이 방법 은 주로 각종 구성원 변 수 를 초기 화 하 는 데 사 용 됩 니 다.그러면 우 리 는 어떤 구성원 변수 나 어떤 인 스 턴 스 가 필요 합 니까?필자 의 생각 은 다음 과 같다.빈 비트 맵 을 통 해 우 리 는 위 에 원형,문자 등 을 그립 니 다.그러면 마지막 으로 이 비트 맵 을 우리 의 view 위 에 그립 니 다.
따라서 초기 화 할 때 각종 Paint(화필),Bitmap(빈 그림),Canvas(캔버스)등의 인 스 턴 스 를 가 져 와 야 합 니 다.다시 한 번 생각해 보 겠 습 니 다.중간의 원 은 회전 할 수 있 습 니 다.그러면 중간의 회전 원 은 다른 원 과 같은 bitmap 에 놓 을 수 없습니다.그렇지 않 으 면 뒤에서 회전 하 는 실현 에 번 거 로 움 을 줄 수 있 기 때문에 우 리 는 두 장의 빈 bitmap 를 준비 할 수 있 습 니 다.그러면 우 리 는 먼저 이렇게 할 수 있다.
public void init(){
//
mBackgroundCirclePaint = new Paint();
mBackgroundCirclePaint.setAntiAlias(true);
mBackgroundCirclePaint.setColor(Color.argb(0xff, 0x10, 0x53, 0xff));
//
mFrontCirclePaint = new Paint();
mFrontCirclePaint.setAntiAlias(true);
mFrontCirclePaint.setColor(Color.argb(0xff, 0x5e, 0xae, 0xff));
//
mTextPaint = new Paint();
mTextPaint.setAntiAlias(true);
mTextPaint.setTextSize(80);
mTextPaint.setColor(Color.WHITE);
//
mArcPaint = new Paint();
mArcPaint.setAntiAlias(true);
mArcPaint.setColor(Color.WHITE);
mArcPaint.setStrokeWidth(12);
mArcPaint.setStyle(Paint.Style.STROKE);
mBitmap = Bitmap.createBitmap(mWidth,mHeight, Bitmap.Config.ARGB_8888);
mBitmapCanvas = new Canvas(mBitmap); // Bitmap
// bitmap
mOverturnBitmap = Bitmap.createBitmap(mWidth,mHeight, Bitmap.Config.ARGB_8888);
mOverturnBitmapCanvas = new Canvas(mOverturnBitmap);
// ...
//Camera、Matrix、Runnable
mMatrix = new Matrix();
mCamera = new Camera();
}
위 에 서 는 다양한 화필 유형 을 초기 화하 고 비트 맵 과 그 와 관련 된 캔버스 두 개 를 준 비 했 는데,우 리 는 그 와 관련 된 캔버스 에 그리 면 된다.그러면 내용 이 있 는 비트 맵 두 개 를 얻 을 수 있다.우 리 는 이 어 뒤 집기 효 과 를 실현 하려 면 무엇이 더 필요 할 까?라 고 생각 했다.Android SDK 는 카메라 와 Matrix 라 는 도 구 를 준비 해 주 었 습 니 다.이 두 도 구 를 이용 하면 크기 조정,이동,반전 등 Bitmap 에 대한 다양한 변환 을 편리 하 게 실현 할 수 있 습 니 다.Camera 와 Matrix 에 관 해 서 는 독자 들 이 더 자세 한 관련 지식 을 검색 할 수 있 으 므 로 자세 한 이 야 기 는 전개 되 지 않 습 니 다.마지막 으로 우 리 는 Runnable 이 필요 합 니 다.자동 반전 과 진도 바 의 자동 증가 와 감 소 를 실현 해 야 하기 때문에 Runnable 은 다음 에 상세 하 게 설명 할 것 입 니 다.서 두 르 지 마 세 요.물론 클릭 모니터 도 설치 해 야 합 니 다.
단계 2.그림 그리 기
위 에는 이미 우 리 를 위해 붓,캔버스 등 이 준비 되 어 있 으 며,우 리 는 다음 에 필요 한 그림 을 그 릴 것 이다.View 의 onDraw()방법 을 다시 쓰 면 됩 니 다.
① 배경 원,즉 위의 그림 에서 가장 바깥쪽 짙 은 파란색 원 을 그립 니 다.
mBitmapCanvas.drawCircle(mWidth / 2, mHeight / 2, mWidth / 2, mBackgroundCirclePaint);
② 가운데 에 있 는 흰색 배경 원 을 그립 니 다.즉,회전 원 을 뒤 집 는 과정 에서 배경의 흰색 부분 을 그립 니 다.
mBitmapCanvas.drawCircle(mWidth / 2, mHeight / 2, mWidth / 2 - mPadding, mTextPaint);
③ 진도 바 를 그립 니 다.원호 형 진도 바 는 어떻게 실현 해 야 합 니까?여기 서 필자 의 생각 을 제시 합 니 다.canvas 의 draw Arc()방법 을 통 해 이 방법 은 사각형 에서 가장 큰 원(또는 타원)을 그 릴 수 있 습 니 다.붓 을 중 공 으로 설정 하고 붓 라인 의 폭 을 12 정도 로 설정 하면 굵 은 포물선 을 실현 할 수 있 습 니 다.그리고 onDraw()방법 을 계속 호출 할 수 있 습 니 다.draw Arc()의 각 도 를 수정 하여 진도 조 효 과 를 실현 합 니 다.만약 여러분 에 게 또 다른 실현 방법 이 있다 면,교 류 를 환영 합 니 다.
mBitmapCanvas.save();
// , Bitmap ,
//
RectF rectF = new RectF(10,10,mWidth-10,mHeight-10);
// 90 , drawArc 0 ,
mBitmapCanvas.rotate(-90, mWidth / 2, mHeight / 2);
mBitmapCanvas.drawArc(rectF, 0, ((float)mProgress/mMaxProgress)*360, false, mArcPaint);
mBitmapCanvas.restore();
canvas.drawBitmap(mBitmap, 0, 0, null);
④ 가운데 회전 원 을 그립 니 다.위 에서 말 했 듯 이 반전 효 과 를 실현 하려 면 같은 비트 맵 에 그 릴 수 없 기 때문에 우 리 는 다른 빈 비트 맵 을 사용 합 니 다.회전 원 의 그리 기 는 매우 간단 합 니 다.그것 의 반지름 이 바깥 원 의 반지름 과 진도 조 의 너비 보다 더 작 으 면 됩 니 다.mOverturnBitmapCanvas.drawCircle(mWidth / 2, mHeight / 2, mWidth / 2 - mPadding, mFrontCirclePaint);
⑤ 마지막 으로 회전 원 에 백분율 숫자 를 그립 니 다.텍스트 를 그립 니 다.Canvas 의 drawText 방법 을 사용 해 야 합 니 다.이 방법 을 중점적으로 살 펴 보 겠 습 니 다.
/**
* Draw the text, with origin at (x,y), using the specified paint. The
* origin is interpreted based on the Align setting in the paint.
*
* @param text The text to be drawn
* @param x The x-coordinate of the origin of the text being drawn
* @param y The y-coordinate of the baseline of the text being drawn
* @param paint The paint used for the text (e.g. color, size, style)
*/
public void drawText(@NonNull String text, float x, float y, @NonNull Paint paint) {
//...
}
첫 번 째 와 네 번 째 매개 변 수 는 할 말 이 없다.두 번 째 매개 변 수 는 문자 가 시 작 된 x 좌 표를 나타 내 고 세 번 째 매개 변 수 는 문자 의 baseline 의 y 좌 표를 나타 낸다.텍스트 를 가운데 로 표시 하려 면 적당 한 x,y 좌표 만 설정 하면 됩 니 다.그러면 baseline 은 무엇 입 니까?그것 은 사실 텍스트 의 기준점 을 대표 하고 있 습 니 다.우 리 는 그림 을 보 겠 습 니 다.그림 에서 알 수 있 듯 이 baseline 이상 에서 텍스트 의 최고점 은 Ascent 이 고 마이너스 이다.baseline 아래 에서 텍스트 의 가장 낮은 점 은 Descent 이 고 플러스 입 니 다.따라서 만약 에 우리 가 텍스트 를 컨트롤 안에 가운데 로 표시 하려 면-(ascent-descent)/2 를 이용 하여 텍스트 의 높이 의 절반 을 계산 할 수 있 습 니 다.이때 mHeight/2(컨트롤 높이 의 절반)를 이용 하여 이 값 을 더 하면 컨트롤 에 있 는 baseline 값 을 얻 을 수 있 습 니 다.이때 중간 에 표 시 됩 니 다.코드 는 다음 과 같 습 니 다.
String text = (int) (((float)mProgress / mMaxProgress) *100) + "%";
//
float textWidth = mTextPaint.measureText(text);
//
Paint.FontMetrics metrics = mTextPaint.getFontMetrics();
float baseLine = mHeight / 2 - (metrics.ascent + metrics.descent) /2;
mOverturnBitmapCanvas.drawText(text, mWidth / 2 - textWidth / 2, baseLine, mTextPaint);
마지막 으로 bitmap 를 view 에 그립 니 다.canvas.drawBitmap(mOverturnBitmap, mMatrix, null);
이상 의 그림 을 통 해 우 리 는 먼저 효과 가 어떤 지 봅 시다.
그럼 기본 적 인 효 과 는 다 이 루어 졌 습 니 다.다음 에 우 리 는 동적 효 과 를 실현 할 것 이다.
Step 3.자동 반전 효과 구현
위의 애니메이션 효 과 를 보면 우 리 는 먼저 진도 바 를 0 에서 특정한 수치 로 증가 시 킨 다음 에 자동 으로 뒤 집 습 니 다.수 치 를 증가 시 키 는 것 은 간단 합 니 다.Runnable 을 사용 하면 Runnable 에서 mProgress 값 을 계속 증가 시 키 고 invalidate()방법 으로 View 를 새로 고침 하면 됩 니 다.진도 가 증가 하면 뒤 집기 시작 합 니 다.뒤 집기 시작 하면 Camera 와 Matrix 를 이용 하여 중간의 bitmap 를 조작 하고 각 도 를 계속 바 꾸 면 이 루어 집 니 다.코드 를 살 펴 보 겠 습 니 다.
onDraw()방법 에서:
@Override
protected void onDraw(Canvas canvas) {
//....
//
if(isRotating) {
mCamera.save();
//
mCamera.rotateY(mRotateAngle);
// 180 , 180
if (mRotateAngle >= 180) {
mRotateAngle -= 180;
}
// Camera
mCamera.getMatrix(mMatrix);
mCamera.restore();
mMatrix.preTranslate(-mWidth / 2, -mHeight / 2);
mMatrix.postTranslate(mWidth / 2, mHeight / 2);
}
canvas.drawBitmap(mOverturnBitmap, mMatrix, null);
//
if(!isRotating && !isInital){
// isIncreasing,
isIncreasing = true;
isRotating = true;
postDelayed(mRotateRunnable,10);
}
이어서 mRotateRunnable 을 쓰 겠 습 니 다.Runnable 의 초기 화 는 init()방법 에 있 습 니 다.
mRotateRunnable = new Runnable() {
@Override
public void run() {
//
if(isIncreasing){
Log.d("cylog","mProgress:"+mProgress);
// ,
if(mProgress >= 59){
isIncreasing = false;
}
mProgress++;
}else {
// ,
// mRotateAngle 90 , bitmap 90 ,
// bitmap , , 180 ,
// , 180 onDraw 。
if (mRotateAngle > 90 && mRotateAngle < 180)
mRotateAngle = mRotateAngle + 3 + 180;
// mRotateAngle 180 ,
else if (mRotateAngle >= 180) {
isRotating = false;
isInital = true;
mRotateAngle = 0;
return;
} else
// 3, ,
mRotateAngle += 3;
}
invalidate();
//25ms
postDelayed(this,25);
}
};
이상 의 Runnable 및 onDraw()방법의 배합 을 통 해 자동 반전 효 과 를 실현 할 수 있 습 니 다.Step 4.클릭 하여 청소 하 는 효과 구현
자,마지막 효 과 를 실현 하 겠 습 니 다.마찬가지 로 저 희 는 Runnable 을 이용 하여 이 루어 집 니 다.이 청소 효 과 는 사용자 가 작은 공 을 클릭 한 후에 청 소 를 시작 해 야 하기 때문에 이벤트 모니터 가 필요 합 니 다.사용자 가 클릭 할 때마다 onClick 방법 에 Runnable 을 게시 하면 됩 니 다.
먼저 mCleaning Runnable 을 실현 하고 init()방법 에서:
mCleaningRunnable = new Runnable() {
@Override
public void run() {
// ,
if (mProgress >= 60) {
isCleaning = false;
return;
}
// ,mProgress , 0
if (isDescending) {
mProgress--;
if (mProgress <= 0)
isDescending = false;
} else {
mProgress++;
}
invalidate();
postDelayed(this,40);
}
};
setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if(isCleaning) return;
// , return, post
// flag,
isDescending = true;
isCleaning = true;
mProgress--;
postDelayed(mCleaningRunnable, 40);
}
});
위의 논 리 는 실 현 됩 니 다.클릭 할 때마다 진도 치 를 0 까지 줄 이 고 그 다음 에 특정한 값 까지 계속 증가 합 니 다.모든 호출 invalidate()방법 을 통 해 구성 요소 의 갱신 을 알 리 면 동적 효 과 를 실현 합 니 다.자,지금까지 모든 효과 가 이 루어 졌 습 니 다.모든 코드 를 아래 에 붙 입 니 다.읽 어 주 셔 서 감사합니다.
public class LieBaoView extends View {
private Paint mBackgroundCirclePaint;
private Paint mFrontCirclePaint;
private Paint mTextPaint;
private Paint mArcPaint;
private Bitmap mBitmap;
private Bitmap mOverturnBitmap;
private Canvas mBitmapCanvas;
private Canvas mOverturnBitmapCanvas;
private Matrix mMatrix;
private Camera mCamera;
private int mWidth = 400;
private int mHeight = 400;
private int mPadding = 20;
private int mProgress = 0;
private int mMaxProgress = 100;
private int mRotateAngle = 0;
private Runnable mRotateRunnable;
private Runnable mCleaningRunnable;
private boolean isRotating;
private boolean isInital = false;
private boolean isDescending;
private boolean isIncreasing;
private boolean isCleaning;
public LieBaoView(Context context) {
super(context);
init();
}
public LieBaoView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public LieBaoView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(mWidth,mHeight);
}
public void init(){
//
mBackgroundCirclePaint = new Paint();
mBackgroundCirclePaint.setAntiAlias(true);
mBackgroundCirclePaint.setColor(Color.argb(0xff, 0x10, 0x53, 0xff));
//
mFrontCirclePaint = new Paint();
mFrontCirclePaint.setAntiAlias(true);
mFrontCirclePaint.setColor(Color.argb(0xff, 0x5e, 0xae, 0xff));
//
mTextPaint = new Paint();
mTextPaint.setAntiAlias(true);
mTextPaint.setTextSize(80);
mTextPaint.setColor(Color.WHITE);
//
mArcPaint = new Paint();
mArcPaint.setAntiAlias(true);
mArcPaint.setColor(Color.WHITE);
mArcPaint.setStrokeWidth(12);
mArcPaint.setStyle(Paint.Style.STROKE);
mBitmap = Bitmap.createBitmap(mWidth,mHeight, Bitmap.Config.ARGB_8888);
mBitmapCanvas = new Canvas(mBitmap); // Bitmap
// bitmap
mOverturnBitmap = Bitmap.createBitmap(mWidth,mHeight, Bitmap.Config.ARGB_8888);
mOverturnBitmapCanvas = new Canvas(mOverturnBitmap);
mMatrix = new Matrix();
mCamera = new Camera();
mRotateRunnable = new Runnable() {
@Override
public void run() {
//
if(isIncreasing){
Log.d("cylog","mProgress:"+mProgress);
// ,
if(mProgress >= 59){
isIncreasing = false;
}
mProgress++;
}else {
// ,
// mRotateAngle 90 , bitmap 90 ,
// bitmap , , 180 ,
// , 180 onDraw 。
if (mRotateAngle > 90 && mRotateAngle < 180)
mRotateAngle = mRotateAngle + 3 + 180;
// mRotateAngle 180 ,
else if (mRotateAngle >= 180) {
isRotating = false;
isInital = true;
mRotateAngle = 0;
return;
} else
// 3, ,
mRotateAngle += 3;
}
invalidate();
//25ms
postDelayed(this,25);
}
};
mCleaningRunnable = new Runnable() {
@Override
public void run() {
// ,
if (mProgress >= 60) {
isCleaning = false;
return;
}
// ,mProgress , 0
if (isDescending) {
mProgress--;
if (mProgress <= 0)
isDescending = false;
} else {
mProgress++;
}
invalidate();
postDelayed(this,40);
}
};
setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if(isCleaning) return;
isDescending = true;
isCleaning = true;
mProgress--;
postDelayed(mCleaningRunnable, 40);
}
});
}
@Override
protected void onDraw(Canvas canvas) {
mBitmapCanvas.drawCircle(mWidth / 2, mHeight / 2, mWidth / 2, mBackgroundCirclePaint);
mBitmapCanvas.drawCircle(mWidth / 2, mHeight / 2, mWidth / 2 - mPadding, mTextPaint);
mBitmapCanvas.save();
// , Bitmap ,
//
RectF rectF = new RectF(10,10,mWidth-10,mHeight-10);
// 90 , drawArc 0 ,
mBitmapCanvas.rotate(-90, mWidth / 2, mHeight / 2);
mBitmapCanvas.drawArc(rectF, 0, ((float)mProgress/mMaxProgress)*360, false, mArcPaint);
mBitmapCanvas.restore();
canvas.drawBitmap(mBitmap, 0, 0, null);
mOverturnBitmapCanvas.drawCircle(mWidth / 2, mHeight / 2, mWidth / 2 - mPadding, mFrontCirclePaint);
String text = (int) (((float)mProgress / mMaxProgress) *100) + "%";
//
float textWidth = mTextPaint.measureText(text);
//
Paint.FontMetrics metrics = mTextPaint.getFontMetrics();
float baseLine = mHeight / 2 - (metrics.ascent + metrics.descent) /2;
mOverturnBitmapCanvas.drawText(text, mWidth / 2 - textWidth / 2, baseLine, mTextPaint);
//
if(isRotating) {
mCamera.save();
//
mCamera.rotateY(mRotateAngle);
// 180 , 180
if (mRotateAngle >= 180) {
mRotateAngle -= 180;
}
// Camera
mCamera.getMatrix(mMatrix);
mCamera.restore();
mMatrix.preTranslate(-mWidth / 2, -mHeight / 2);
mMatrix.postTranslate(mWidth / 2, mHeight / 2);
}
canvas.drawBitmap(mOverturnBitmap, mMatrix, null);
//
if(!isRotating && !isInital){
// isIncreasing,
isIncreasing = true;
isRotating = true;
postDelayed(mRotateRunnable,10);
}
}
}
이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 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에 따라 라이센스가 부여됩니다.