강력 한 기능 을 가 진 안 드 로 이 드 스 크 래 치 효과 컨트롤(ScratchView)
15112 단어 Android상 을 깎다ScratchView
내 주변 에는 개발 한 일부 동료 들 이 있 는데,이런 습관 이 존재 한다.어느 날 갑자기 어떤 앱 에 예 쁜 사용자 정의 컨트롤(애니메이션)효과 가 있 는 것 을 보고 머리 를 쥐 어 짜 서 자신 이 한 발 을 실현 하려 고 한다.물론 나 자신 도 이런 유형의 소 년 에 속 하기 때문에 어떤 효 과 를 보면 손 이 근질근질 하고 실현 방법 을 궁리 하기 어렵다.개인 적 으로 이것 은 진 보 를 추진 하 는 방법 이 라 고 생각 합 니 다.머리 를 쥐 어 짜 서 원 하 는 효 과 를 실현 할 때 안 드 로 이 드 사용자 정의 컨트롤(애니메이션)에 대한 지식 체계 에 대한 인식 이 깊 어 질 수록 오 랜 시간 이 지나 면 자신 도 각종 컨트롤(그림)효 과 를 쉽게 만 들 수 있 습 니 다.만약 어느 날,제품 동 화 는 원형(또는 어떤 App 에)을 들 고 당신 에 게 말 합 니 다."XXXX,이 효과 가 우리 가 실현 할 수 있 을 것 같 습 니까?"그리고 나 서 너 는 얼핏 보 더 니 마음속 에 대나무 가 있 었 다."농담 이 야.그리고 내 가 이 룰 수 없 는 효과 도 있어?"생각해 보니까 좀 설 레 지 않 아 요?자,이제 본론 으로 돌아 갈 뻔 했 습 니 다.이것 은 제 가 처음으로 사용자 정의 컨트롤 에 관 한 글 입 니 다.이 유형의 글 도 계속 삽입 하여 업데이트 할 것 입 니 다.여러분 들 이 좋아 하 시 기 를 바 랍 니 다.몰래 내 다음 글 은 성능 최적화 에 관 한 건어물 이다.물론 저 는 건어물 이 라 고 생각 합 니 다.그때 얼굴 을 때 리 지 않 았 으 면 좋 겠 습 니 다.하하 하!)
실현 효과
이렇게 많아
위 는 기본 적 인 실현 효과 의 일부분 일 뿐 아래 에 다른 컨트롤 이 많이 있 는 것 을 볼 수 있 습 니 다.그들 은 무엇 을 하 는 지 다음 에 모든 것 을 밝 힐 것 입 니 다.
기본 실현
일상생활 에서 우 리 는 스 크 래 치 효과 에 대해 낯 설 지 않 을 것 이다.그 원 리 는 기 존의 도안 과 문자 에 스 크 래 치 를 추가 하여 이 루어 지 는 것 이다.만약 우리 가 스 크 래 치 뒤에 숨겨 진 도안 과 문자 가 무엇 인지 보고 싶다 면 반드시 스 크 래 치 를 통 해 스 크 래 치 를 해 야 한다.이런 방법 을 알 게 되면 인 코딩 을 정리 하고 생각 을 실현 한 다음 에 즐겁게 할 수 있다.
제 가 처음에 실현 한 사 고 는 ImageView 와 TextView 를 다시 쓴 다음 에 각각 코드 로 이미지 와 문자 에 그림 을 추가 하면 효 과 를 얻 을 수 있 습 니 다.그러나 돌 이 켜 보면 옳지 않다.이런 실현 은 한계 가 크다.만약 에 이런 사고방식 으로 실현 된다 면 스 크 래 치 아래 에 그림 이나 문자 만 존재 할 수 있 고 제품 매니저 가 그림 과 문자 가 동시에 존재 하 라 고 요구한다 면?두 장의 그림 을 저장 해 달라 고요?그림 과 문자 가 동시에 존재 하고 문 자 를 그림 의 위(아래,왼쪽,오른쪽)에 놓 아야 합 니까?우 리 는 세상 에서 가장 변 덕 스 러 운 것 은 여동생 의 마음 을 제외 하고 제품 매니저 와 그들의 수요 라 는 것 을 잘 알 고 있다.그래서 다른 실현 방향 을 생각해 냈 습 니 다.View 를 직접 계승 하여 스 크 래 치 를 실현 하고 이 스 크 래 치 와 그림 과 문자 가 어떠한 의존 도 하지 않도록 합 니 다.그리고 FrameLayout 와 결합 하여 스 크 래 치 를 맨 위 에 놓 습 니 다.스 크 래 치 아래 에 얼마나 많은 그림 문 자 를 놓 고 싶 습 니까?그림 문 자 는 어떻게 배치 하 든 좋 습 니 다.여기 서 생각 이 명확 하고 즐겁게 인 코딩 을 시작 할 수 있 습 니 다.
첫 번 째 단계:스 크 래 치 효 과 를 그립 니 다.
package com.clock.scratch;
import ...;
/**
* Created by Clock on 2016/8/26.
*/
public class ScratchView extends View {
...
public ScratchView(Context context) {
super(context);
TypedArray typedArray = context.obtainStyledAttributes(R.styleable.ScratchView);
init(typedArray);
}
...
private void init(TypedArray typedArray) {
...
mMaskColor = typedArray.getColor(R.styleable.ScratchView_maskColor, DEFAULT_MASKER_COLOR);
mMaskPaint = new Paint();
mMaskPaint.setAntiAlias(true);//
mMaskPaint.setDither(true);//
setMaskColor(mMaskColor);
...
}
/**
*
*
* @param color , :0xffff0000( )
*/
public void setMaskColor(int color) {
mMaskPaint.setColor(color);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap(mMaskBitmap, 0, 0, mBitmapPaint);//
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
createMasker(w, h);
}
/**
*
*
* @param width
* @param height
*/
private void createMasker(int width, int height) {
mMaskBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
mMaskCanvas = new Canvas(mMaskBitmap);
Rect rect = new Rect(0, 0, width, height);
mMaskCanvas.drawRect(rect, mMaskPaint);// Bitmap
}
}
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="ScratchView">
<!-- -->
<attr name="maskColor" format="color|reference" />
</declare-styleable>
</resources>
위의 코드 사고방식 은 다음 과 같다.View 에 이 어 사용자 정의 컨트롤 ScratchView 를 만 들 고 init()함수 에서 각종 매개 변수 설정 을 초기 화 합 니 다.스 크 래 치 색상 등;
설정 을 편리 하 게 하기 위해 서 는 파 라 메 터 를 컨트롤 의 사용자 정의 속성 으로 추출 하고 ScratchView 류 에서 set 방법 을 제공 하여 코드 호출 을 제공 해 야 합 니 다.예 를 들 어 스 크 래 치 의 색상 속성 은 바로 maskColor 이 고 클래스 에 대응 하 는 방법 은 setMaskColor 입 니 다.
onSize Changed 에서 View 를 이용 하여 Measure 가 완료 되 었 습 니 다.View 의 너비 와 높이 를 얻 을 수 있 고 Canvas 를 사용 하여 mMaskBitmap 생 성 을 초기 화하 여 스 크 래 치 를 만 드 는 데 사용 할 수 있 습 니 다.
onDraw 에서 canvas.drawBitmap 를 이용 하여 onSizeChanged 에서 mMaskBitmap 생 성 을 초기 화하 여 인터페이스 에 표시 하고 스 크 래 치 를 생 성 합 니 다.
데모 에 다음 레이아웃 을 추가 합 니 다.효과 보기:
<FrameLayout
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_gravity="center_horizontal"
android:layout_marginTop="8dp">
<!-- -->
<ImageView
android:layout_width="150dp"
android:layout_height="150dp"
android:layout_gravity="center"
android:src="@mipmap/lufy" />
<!-- -->
<com.clock.scratch.ScratchView
android:id="@+id/scratch_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
이로써 우 리 는 스 크 래 치 의 실현 효 과 를 얻 었 고 xml 레이아웃 과 자바 코드 에 스 크 래 치 의 색 을 직접 설정 할 수 있 습 니 다.그러나 이 때 는 스 크 래 치 만 비어 있 을 뿐 스 크 래 치 효 과 는 이 루어 지지 않 았 고 이 어 실현 코드 를 계속 추가 했다.
STEP 2:스 크 래 치 효과 구현.
package com.clock.scratch;
import ...;
public class ScratchView extends View {
public ScratchView(Context context) {
super(context);
TypedArray typedArray = context.obtainStyledAttributes(R.styleable.ScratchView);
init(typedArray);
}
private void init(TypedArray typedArray) {
mEraseSize = typedArray.getFloat(R.styleable.ScratchView_eraseSize, DEFAULT_ERASER_SIZE);
...
mErasePaint = new Paint();
mErasePaint.setAntiAlias(true);
mErasePaint.setDither(true);
mErasePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));//
mErasePaint.setStyle(Paint.Style.STROKE);
mErasePaint.setStrokeCap(Paint.Cap.ROUND);// ,
setEraserSize(mEraseSize);
mErasePath = new Path();
ViewConfiguration viewConfiguration = ViewConfiguration.get(getContext());
mTouchSlop = viewConfiguration.getScaledTouchSlop();
}
/**
* ( 60)
*
* @param eraserSize
*/
public void setEraserSize(float eraserSize) {
mErasePaint.setStrokeWidth(eraserSize);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
startErase(event.getX(), event.getY());
invalidate();
return true;
case MotionEvent.ACTION_MOVE:
erase(event.getX(), event.getY());
invalidate();
return true;
case MotionEvent.ACTION_UP:
stopErase();
invalidate();
return true;
default:
break;
}
return super.onTouchEvent(event);
}
/**
*
*
* @param x
* @param y
*/
private void startErase(float x, float y) {
mErasePath.reset();
mErasePath.moveTo(x, y);
this.mStartX = x;
this.mStartY = y;
}
/**
*
*
* @param x
* @param y
*/
private void erase(float x, float y) {
int dx = (int) Math.abs(x - mStartX);
int dy = (int) Math.abs(y - mStartY);
if (dx >= mTouchSlop || dy >= mTouchSlop) {
this.mStartX = x;
this.mStartY = y;
mErasePath.lineTo(x, y);
mMaskCanvas.drawPath(mErasePath, mErasePaint);
mErasePath.reset();
mErasePath.moveTo(mStartX, mStartY);
}
}
/**
*
*/
private void stopErase() {
this.mStartX = 0;
this.mStartY = 0;
mErasePath.reset();
}
}
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="ScratchView">
<!-- -->
<attr name="eraseSize" format="float" />
</declare-styleable>
</resources>
위의 코드 사고방식 은 다음 과 같다.init()에서 mErasePaint 와 mErasePath 를 초기 화하 고 mErasePaint 의 Xfermode 를 Porter Duff.Mode.CLEAR 로 설정 하여 뒤에서 스 크 래 치 효 과 를 만 듭 니 다.
onTouchEvent 함 수 를 다시 쓰 고 터치 이벤트 처리 ACTIONDOWN 、 ACTION_MOVE 、 ACTION_UP 등 세 가지 이벤트 유형 을 이용 하여 mErasePath 를 이용 하여 손가락 이 미 끄 러 지 는 궤적 을 기록 한 다음 에 mMaskCanvas 로 미 끄 러 지 는 궤적 을 첫 번 째 생 성 된 mMaskBitmap 에 그립 니 다.마지막 으로 invalidate()를 호출 하여 View 의 재 그리 기 를 통 해 스 크 래 치 효 과 를 생 성 합 니 다.
미끄럼 이 너무 민감 하지 않도록 시스템 이 제공 하 는 view Configuration.getScaled TouchSlop()을 통 해 시스템 이 생각 하 는 최소 미끄럼 거 리 를 가 져 와 야 합 니 다.이 거 리 를 초과 하거나 초과 할 때 미끄럼 이 라 고 생각 합 니 다.이것 이 바로 제 가 erase()에 dx>=mTouchSlop||dy>=mTouchSlop 의 판단 입 니 다.
스 크 래 치 의 굵기 를 조절 하기 위해 서 는 앞 에 스 크 래 치 를 설정 한 색상 과 마찬가지 로 ScratchView 의 속성 erase Size 를 xml 에서 제어 합 니 다.동시에 자바 코드 에서 호출 방법 을 제공 합 니 다.
여기까지 기본 적 인 스 크 래 치 효과 가 완성 되 었 으 니 실현 효과 가 어떤 지 살 펴 보 자.
위의 두 단 계 는 기초 효 과 를 완성 할 뿐 이 니,다음 에 우 리 는 최 적 화 를 해 보 자.
효과 최적화
첫 번 째 최적화:워 터 마크 추가
많은 스 크 래 치 효과 가 스 크 래 치 에 자신의 로 고 를 추가 하여 워 터 마크 를 만 드 는 효과 가 있 습 니 다.아무튼 대충 그런 뜻 이다.아래 알 리 페 이 처럼
우 리 는 기본 적 으로 실 현 된 첫 번 째 단계 에서 스 크 래 치 함수 에 실현 코드 를 추가 하 는 동시에 사용자 정의 속성 과 set 방법 을 추가 하여 호출 할 수 있 습 니 다.
/**
*
*
* @param resId id,-1
*/
public void setWatermark(int resId) {
if (resId == -1) {
mWatermark = null;
} else {
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), resId);
mWatermark = new BitmapDrawable(bitmap);
mWatermark.setTileModeXY(Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
}
}
/**
*
*
* @param width
* @param height
*/
private void createMasker(int width, int height) {
...
if (mWatermark != null) {//
Rect bounds = new Rect(rect);
mWatermark.setBounds(bounds);
mWatermark.draw(mMaskCanvas);
}
}
실현 효 과 는 다음 과 같다.물론 효과 적 으로 추가 할 수 있 는 것 도 많 습 니 다.예 를 들 어 알 리 페 이의 가장자리 톱니 효과 도 추가 할 수 있 습 니 다.여 기 는 어린이 신발 의 자체 뇌 동 으로 이 루어 집 니 다.
두 번 째 최적화:해당 이벤트 모니터 를 추가 하고 상용 함 수 를 보완 합 니 다.
사건 감청 하면 여기 가 추첨 이 끝 난 사건 이 라 고 생각 합 니 다.이 컨트롤 을 사용 하 는 개발 자 에 게 는 긁 힌 후에 해당 하 는 조작 을 해 야 합 니 다.예 를 들 어 사용자 가 당 첨 되 었 는 지,아니면 계속 노력 하 는 지 등 입 니 다.어떻게 스 크 래 치 완성 을 판단 합 니까?여기 서 의 실현 방향 은 스 크 래 치 mMaskBitmap 의 픽 셀 정보 값 을 비동기 로 계산 하고 투명 픽 셀 개수 가 전체 픽 셀 개수 에서 차지 하 는 비례 를 계산 하 는 것 입 니 다.이 비례 가 일정한 한도 값 을 초과 할 때 우 리 는 스 크 래 치 가 완성 되 었 다 고 생각 합 니 다.왜 일정한 한도 값 을 넘 으 면 완성 된다 고 말 해 야 합 니까?이것 은 현실 생활 에서 스 크 래 치 와 마찬가지 로 스 크 래 치 를 완전히 깨끗하게 긁 지 않 아 도 결 과 를 얻 을 수 있 습 니 다.물론 이 비율 이 얼마 인지,우 리 는 동적 설정 으로 분리 해 야 한다.모니터 인터페이스 와 모니터 를 설치 한 API 를 추가 하면 된다.실현 코드 는 대체적으로 다음 과 같다.
private void onErase() {
int width = getWidth();
int height = getHeight();
new AsyncTask<Integer, Integer, Boolean>() {
@Override
protected Boolean doInBackground(Integer... params) {
int width = params[0];
int height = params[1];
int pixels[] = new int[width * height];
mMaskBitmap.getPixels(pixels, 0, width, 0, 0, width, height);// ,stride
float erasePixelCount = 0;//
float totalPixelCount = width * height;//
for (int pos = 0; pos < totalPixelCount; pos++) {
if (pixels[pos] == 0) {// 0
erasePixelCount++;
}
}
int percent = 0;
if (erasePixelCount >= 0 && totalPixelCount > 0) {
percent = Math.round(erasePixelCount * 100 / totalPixelCount);
publishProgress(percent);
}
return percent >= mMaxPercent;
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
mPercent = values[0];
onPercentUpdate();
}
@Override
protected void onPostExecute(Boolean result) {
super.onPostExecute(result);
if (result && !mIsCompleted) {// ,
mIsCompleted = true;
if (mEraseStatusListener != null) {
mEraseStatusListener.onCompleted(ScratchView.this);
}
}
}
}.execute(width, height);
}
/**
*
*
* @param listener
*/
public void setEraseStatusListener(EraseStatusListener listener) {
this.mEraseStatusListener = listener;
}
/**
*
*/
public static interface EraseStatusListener {
/**
*
*
* @param percent , 0, 100;
*/
public void onProgress(int percent);
/**
*
*
* @param view
*/
public void onCompleted(View view);
}
최종 효과 한번 볼 게 요.이 쯤 되면 완전한 스 크 래 치 효과 사용자 정의 컨트롤 이 완료 되 었 습 니 다.그러나 여기 서 여러분 에 게 공동으로 생각해 야 할 문제 가 하나 더 있 습 니 다.바로 스 크 래 치 의 완성 여 부 를 판단 하 는 데 있어 제 가 코드 에서 의 실현 방식 은 대량의 int 배열 을 만 들 것 입 니 다.그러면 결 과 는 바로 메모리 디 더 링 이 발생 할 것 입 니 다.
지금 은 저도 좋 은 방안 을 생각 하지 못 했 기 때문에 여러분 들 이 좋 은 생각 이 있 으 면 같이 교류 하 세 요.
총결산
처음으로 사용자 정의 컨트롤 이라는 유형의 글 을 썼 습 니 다.여러분 은 생각 을 이 루 었 는 지 모 르 시 겠 습 니까?사용자 정의 컨트롤 에 대해 서 는 글 만 보면 그 생각 을 알 수 있 고 소스 코드 와 결합 하여 디 버 깅 을 실천 하 는 동시에 글 을 보면 더욱 깊이 느 낄 수 있 습 니 다.소스 코드 가 필요 한 동 화 는https://github.com/D-clock/ScratchView에서 다운로드 할 수 있 습 니 다.이 어 사용자 정의 컨트롤(애니메이션)에 대한 글 도 많이 있 습 니 다.기대 하 세 요.
작가 님 의 공유 에 감 사 드 립 니 다.
이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 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에 따라 라이센스가 부여됩니다.