착색 게임 으로 안 드 로 이 드 의 영역 그림 을 채 우 는 방법 을 설명 합 니 다.
최근 에 단체 에서 한 친구 가 단체 에서 불규칙 한 이미 지 를 채 우 는 것 을 우연히 보 았 는데 그 자체 가 배 우 는 것 을 좋아 하고 실 용적 인 태도 로 관련 자 료 를 찾 아 보 았 다.이런 착색 자료 에 대해 가장 좋 은 것 은 관련 app 을 검색 하 는 것 입 니 다.제 관찰 에 따 르 면 불규칙 한 이미 지 는 착색 게임 에 채 워 넣 는 것 이 많 지만 대체적으로 두 가지 로 나 눌 수 있 습 니 다.
PS 를 배 웠 다 면 더 이상 알 수 없 을 것 이 라 고 믿 습 니 다.예 를 들 어 하늘 을 그 리 려 면 맨 밑 에 푸 른 하늘 을 그 릴 수 있 고 윗 층 에 흰 구름 을 그 릴 수 있 으 며 윗 층 에 작은 새 를 그 릴 수 있다.그리고 3 층 이 겹 쳐 지면 작은 새 한 마리 가 하늘 을 날 고 있 는 그림 이다.
2.효과 와 분석
자,이제 오늘 의 효 과 를 보 겠 습 니 다.
ok,간단 한 착색 효 과 를 볼 수 있 습 니 다.사실은 원리 가 간단 합 니 다.우선,이 그림 은 실제 적 으로 7 층 으로 구성 되 어 있 습 니 다.
예 를 들 어 다음 그림.
만약 우리 가 이 그림 의 어느 위치 에 착색 을 해 야 한다 면,실제로는 한 층 의 불투명 한 구역 에 착색 을 해 야 한다.실제로
사용자 가 클릭 한(x,y)->어느 층 에 떨 어 진 불투명 영역 을 판단 합 니 다->그리고 이 층 의 불투명 영역 에 착색 합 니 다.
ok,이러한 원 리 는 명확 하 게 서술 할 수 있 습 니 다.사실은 매우 간단 합 니 다.이 원 리 를 바탕 으로 우 리 는 View 를 사용자 정의 한 다음 에 그림 을 그 릴 수 있 습 니 다.마지막 으로 상기 절차 에 따라 코드 를 작성 할 수 있 습 니 다.그러나 우 리 는 게 으 름 을 피 울 수 있 는 곳 이 있 습 니 다.사실은 우리 가 한 층 의 그림 을 그 릴 필요 가 없습니다.우 리 는 Drawable 을 이용 하여 그림 의 중첩 작업 을 완성 할 수 있 습 니 다.우 리 는 Drawable 을 Layer Drawable 이 라 고 부 릅 니 다.해당 하 는 xml 는 layer-list 입 니 다.우 리 는 Layer Drawable 을 사용 하여 우리 의 작업 을 크게 간소화 할 수 있 습 니 다.
3.인 코딩 과 실현
상술 한 것 은 이미 명확 하 게 묘사 되 었 으 니,제 가 다시 여러분 께 세분 화 해 드 리 겠 습 니 다.
layer-list 에서 우리 의 drawable 을 정의 합 니 다.
그리고 이 drawable 을 저희 View 의 배경 으로 삼 겠 습 니 다.
복사 onTouchEvent 방법
사용자 가 클릭 한 좌표 가 어느 층 의 불투명 한 위치 에 떨 어 졌 는 지 판단 하고 이 층 의 불투명 한 영역 색 을 바 꿉 니 다.
(1)layer-list
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:drawable="@drawable/eel_mask1"/>
<item
android:drawable="@drawable/eel_mask2"/>
<item
android:drawable="@drawable/eel_mask3"/>
<item
android:drawable="@drawable/eel_mask4"/>
<item
android:drawable="@drawable/eel_mask5"/>
<item
android:drawable="@drawable/eel_mask6"/>
<item
android:drawable="@drawable/eel_mask7"/>
</layer-list>
ok,그러면 우리 drawable 은 ok~~말 안 했 지만 layer-list 는 많은 일 을 할 수 있 으 니 지 켜 보 세 요.(2)보기 코드
package com.zhy.colour_app_01;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import java.util.Random;
/**
* Created by zhy on 15/5/14.
*/
public class ColourImageBaseLayerView extends View
{
private LayerDrawable mDrawables;
public ColourImageBaseLayerView(Context context, AttributeSet attrs)
{
super(context, attrs);
mDrawables = (LayerDrawable) getBackground();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
setMeasuredDimension(mDrawables.getIntrinsicWidth(), mDrawables.getIntrinsicHeight());
}
@Override
public boolean onTouchEvent(MotionEvent event)
{
final float x = event.getX();
final float y = event.getY();
if (event.getAction() == MotionEvent.ACTION_DOWN)
{
Drawable drawable = findDrawable(x, y);
if (drawable != null)
drawable.setColorFilter(randomColor(), PorterDuff.Mode.SRC_IN);
}
return super.onTouchEvent(event);
}
private int randomColor()
{
Random random = new Random();
int color = Color.argb(255, random.nextInt(256), random.nextInt(256), random.nextInt(256));
return color;
}
private Drawable findDrawable(float x, float y)
{
final int numberOfLayers = mDrawables.getNumberOfLayers();
Drawable drawable = null;
Bitmap bitmap = null;
for (int i = numberOfLayers - 1; i >= 0; i--)
{
drawable = mDrawables.getDrawable(i);
bitmap = ((BitmapDrawable) drawable).getBitmap();
try
{
int pixel = bitmap.getPixel((int) x, (int) y);
if (pixel == Color.TRANSPARENT)
{
continue;
}
} catch (Exception e)
{
continue;
}
return drawable;
}
return null;
}
}
ok,코드 도 간단 합 니 다.먼저 drawable 을 view 의 배경 으로 한 다음 에 구조 에서 drawable(Layer Drawable)을 가 져 옵 니 다.다음 에 onTouchEvent 를 복사 하고 사용자 가 클릭 한(x,y)을 캡 처 합 니 다.(x,y)에 따라 현재 클릭 한 층 이 어느 층 인지 찾 습 니 다.(비 투명 영역 에서 클릭 해 야 합 니 다)마지막 으로 setColorFilter 설정 을 통 해 색상 을 바 꾸 면 됩 니 다.easy 죠?마지막 으로 레이아웃 파일 을 붙 입 니 다.(3)레이아웃 파일
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
tools:context=".MainActivity">
<com.zhy.colour_app_01.ColourImageBaseLayerView
android:background="@drawable/eel"
android:layout_width="match_parent"
android:layout_centerInParent="true"
android:layout_height="match_parent"/>
</RelativeLayout>
4.경계 채 우기1.그림 의 충전 에는 두 가지 고전 알고리즘 이 있 습 니 다.
하 나 는 피 드 충전 법 이다.피 드 충전 법 이론 적 으로 임의의 구역 과 도형 을 채 울 수 있 지만 이런 알고리즘 은 대량의 반복 적 인 스 택 과 대규모 재 귀 가 존재 하여 충전 효율 을 낮 출 수 있다.
다른 하 나 는 스캐닝 라인 충전 법 이다.
메모:실제로 이미지 채 우 는 알고리즘 이 많 습 니 다.관심 이 있 으 면 Google 학술 지 를 찾 아 보 세 요.
ok,다음은 효과 도 를 살 펴 보 겠 습 니 다.
ok,이런 컬러 충전 이 전편 의 베이스 층 보다 소재 준비 에 있어 서 훨씬 쉬 운 것 을 볼 수 있 습 니 다~~~
2.원리 분석
먼저 원 리 를 약술 하고 우 리 는 클릭 할 때 클릭 점 의'색'을 얻 은 다음 에 우리 가 선택 한 알고리즘 에 따라 색 을 채 우 면 된다.
알고리즘 1:피 드 충전 법,4 연결/8 연결
알고리즘 소개:어떤 영역 을 빨간색 으로 채 우 겠 다 고 가정 합 니 다.
사용자 가 클릭 한 픽 셀 을 시작 으로 상하 좌우(8 연 결 된 왼쪽 위,왼쪽 아래,오른쪽 위,오른쪽 아래)로 색상 을 판단 하고 네 방향 에 있 는 색상 이 현재 클릭 한 픽 셀 과 일치 하면 색상 을 대상 색 으로 변경 합 니 다.그리고 이 과정 을 계속 하 겠 습 니 다.
ok,이것 은 재 귀적 인 과정 을 볼 수 있 습 니 다.1 개 에서 4 개,4 개 에서 16 개 까지 계속 연장 되 는 것 을 볼 수 있 습 니 다.만약 이러한 알고리즘 에 따른다 면,당신 은 이러한 코드 를 쓸 것 입 니 다.
/**
* @param pixels
* @param w
* @param h
* @param pixel
* @param newColor
* @param i
* @param j
*/
private void fillColor01(int[] pixels, int w, int h, int pixel, int newColor, int i, int j)
{
int index = j * w + i;
if (pixels[index] != pixel || i >= w || i < 0 || j < 0 || j >= h)
return;
pixels[index] = newColor;
//
fillColor01(pixels, w, h, pixel, newColor, i, j - 1);
//
fillColor01(pixels, w, h, pixel, newColor, i + 1, j);
//
fillColor01(pixels, w, h, pixel, newColor, i, j + 1);
//
fillColor01(pixels, w, h, pixel, newColor, i - 1, j);
}
코드 는 간단 하지만 실행 하면 StackOverflow Exception 이상 이 발생 합 니 다.이 이상 은 주로 대량의 재 귀 로 인 한 것 입 니 다.간단 하지만 모 바 일 기기 에서 이 방법 을 사용 하면 안 된다.그래서 저 는 이 방법 이 재 귀 깊이 가 너무 많은 것 이 아니 라 Stack 을 사용 하여 픽 셀 점 을 저장 하고 재 귀 깊이 와 횟수 를 줄 일 수 있다 고 생각 했 습 니 다.그래서 저 는 코드 를 다음 과 같은 방식 으로 바 꿀 수 있 습 니 다.
/**
* @param pixels
* @param w
* @param h
* @param pixel
* @param newColor
* @param i
* @param j
*/
private void fillColor(int[] pixels, int w, int h, int pixel, int newColor, int i, int j)
{
mStacks.push(new Point(i, j));
while (!mStacks.isEmpty())
{
Point seed = mStacks.pop();
Log.e("TAG", "seed = " + seed.x + " , seed = " + seed.y);
int index = seed.y * w + seed.x;
pixels[index] = newColor;
if (seed.y > 0)
{
int top = index - w;
if (pixels[top] == pixel)
{
mStacks.push(new Point(seed.x, seed.y - 1));
}
}
if (seed.y < h - 1)
{
int bottom = index + w;
if (pixels[bottom] == pixel)
{
mStacks.push(new Point(seed.x, seed.y + 1));
}
}
if (seed.x > 0)
{
int left = index - 1;
if (pixels[left] == pixel)
{
mStacks.push(new Point(seed.x - 1, seed.y));
}
}
if (seed.x < w - 1)
{
int right = index + 1;
if (pixels[right] == pixel)
{
mStacks.push(new Point(seed.x + 1, seed.y));
}
}
}
}
방법의 사상 도 비교적 간단 하 다.현재 픽 셀 을 창고 에 넣 은 다음 에 창고 에서 착색 한 다음 에 각각 네 가지 방향 을 판단 하고 조건 에 부합 하면 창고 에 들어간다(창고 가 비어 있 지 않 으 면 지속 적 으로 운행 한다).ok,이 방법 은 저도 뛰 어 보 려 고 했 습 니 다.네,이번 에는 틀 리 지 않 을 겁 니 다.하지만 속도 가 너무 느 려 요~~느 려 서 받 아들 일 수 없습니다.관심 이 있 으 면 시도 해 보 세 요.ANR 이 라면 기 다 려 보 세 요).이렇게 보면 첫 번 째 알고리즘 은 우리 가 고려 하지 않 고 사용 할 방법 이 없다.주요 원인 은 사각형 의 같은 색 구역 에 대해 모두 채 워 야 한다 고 가정 하 는 것 이 고 알고리즘 하 나 는 여전히 각종 스 택 이다.그래서 두 번 째 알고리즘 을 고려 합 니 다.
스캐닝 라인 충전 법
스캐너 피 드 충전 알고리즘 의 해석 과 스캐너 피 드 충전 알고리즘 을 참고 할 수 있 습 니 다.
알고리즘 사상:
피 드 점 을 저장 하기 위해 빈 스 택 을 초기 화하 고 피 드 점(x,y)을 스 택 에 넣 습 니 다.
스 택 이 비어 있 는 지 여 부 를 판단 합 니 다.스 택 이 비어 있 으 면 알고리즘 을 끝 냅 니 다.그렇지 않 으 면 스 택 꼭대기 요 소 를 현재 스 캔 라인 의 피 드 점(x,y)으로 꺼 내 고 y 는 현재 스 캔 라인 입 니 다.
피 드 점(x,y)에서 출발 하여 현재 스캐닝 라인 을 따라 왼쪽,오른쪽 두 방향 으로 경계 까지 채 웁 니 다.구간 의 왼쪽,오른쪽 점 좌 표를 각각 xLeft 와 xRight 로 표시 합 니 다.
현재 스 캔 라인 과 인접 한 y-1 과 y+1 두 개의 스 캔 라인 이 구간[xLeft,xRight]에 있 는 픽 셀 을 각각 검사 하고 xRight 부터 xLeft 방향 으로 검색 합 니 다.스 캔 구간 이 AAABAAC(A 는 피 드 점 색상)이 라 고 가정 하면 B 와 C 앞의 A 를 피 드 점 으로 스 택 에 누 른 다음(2)단계 로 돌아 갑 니 다.
상기 참고 문헌[4]을 참고 하여 수정 을 했 습 니 다.글[4]에서 알고리즘 을 설명 하고 테스트 에 문제 가 있어 서 수정 을 했 습 니 다.
이 알고리즘 을 볼 수 있 습 니 다.기본적으로 한 줄 한 줄 착색 되 어 있 습 니 다.그러면 큰 덩어리 에 착색 이 필요 한 구역 의 효율 이 알고리즘 보다 훨씬 높 습 니 다.
ok,알고리즘 에 대한 절차 가 모호 하 다 고 생각 합 니 다.잠시 후에 저희 코드 를 참조 할 수 있 습 니 다.알고리즘 을 선택 한 후 인 코딩 을 시작 합 니 다.
3.인 코딩 실현
우리 코드 에 경계 색 을 도 입 했 습 니 다.설정 하면 착색 경 계 는 이 경계 색 을 참고 합 니 다.그렇지 않 으 면 피 드 색 과 일치 하지 않 으 면 경계 색 이 됩 니 다.
(1)구조 방법 과 측정
public class ColourImageView extends ImageView
{
private Bitmap mBitmap;
/**
*
*/
private int mBorderColor = -1;
private boolean hasBorderColor = false;
private Stack<Point> mStacks = new Stack<Point>();
public ColourImageView(Context context, AttributeSet attrs)
{
super(context, attrs);
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ColourImageView);
mBorderColor = ta.getColor(R.styleable.ColourImageView_border_color, -1);
hasBorderColor = (mBorderColor != -1);
L.e("hasBorderColor = " + hasBorderColor + " , mBorderColor = " + mBorderColor);
ta.recycle();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int viewWidth = getMeasuredWidth();
int viewHeight = getMeasuredHeight();
// , view
setMeasuredDimension(viewWidth,
getDrawable().getIntrinsicHeight() * viewWidth / getDrawable().getIntrinsicWidth());
L.e("view's width = " + getMeasuredWidth() + " , view's height = " + getMeasuredHeight());
// drawable, view bitmap
BitmapDrawable drawable = (BitmapDrawable) getDrawable();
Bitmap bm = drawable.getBitmap();
mBitmap = Bitmap.createScaledBitmap(bm, getMeasuredWidth(), getMeasuredHeight(), false);
}
이미지 뷰 계승 을 선택 한 것 을 볼 수 있 습 니 다.그림 을 src 로 설정 하면 됩 니 다.구조 방법 에서 사용자 정의 경계 색 을 가 져 옵 니 다.당연히 설정 하지 않 아 도 됩 니 다~~
재 작성 측정 의 목적 은 View 와 같은 크기 의 Bitmap 를 얻 기 위해 서 입 니 다.
이제 클릭 입 니 다.
4.onTouchEvent
@Override
public boolean onTouchEvent(MotionEvent event)
{
final int x = (int) event.getX();
final int y = (int) event.getY();
if (event.getAction() == MotionEvent.ACTION_DOWN)
{
//
fillColorToSameArea(x, y);
}
return super.onTouchEvent(event);
}
/**
* x,y ,
*
* @param x
* @param y
*/
private void fillColorToSameArea(int x, int y)
{
Bitmap bm = mBitmap;
int pixel = bm.getPixel(x, y);
if (pixel == Color.TRANSPARENT || (hasBorderColor && mBorderColor == pixel))
{
return;
}
int newColor = randomColor();
int w = bm.getWidth();
int h = bm.getHeight();
// bitmap
int[] pixels = new int[w * h];
bm.getPixels(pixels, 0, w, 0, 0, w, h);
//
fillColor(pixels, w, h, pixel, newColor, x, y);
// bitmap
bm.setPixels(pixels, 0, w, 0, 0, w, h);
setImageDrawable(new BitmapDrawable(bm));
}
볼 수 있 습 니 다.우 리 는 onTouchEvent 에서(x,y)를 얻 은 다음 에 변 경 된 좌 표를 얻 을 수 있 습 니 다.클릭 점 색상 을 획득 하여 전체 bitmap 의 픽 셀 그룹 을 획득 합 니 다.
이 배열 의 색 을 바 꿉 니 다.
그리고 bitmap 에 다시 설정 하고 ImageView 에 다시 설정 합 니 다.
포 인 트 는 fillColor 를 통 해 배열 의 색 을 바 꾸 는 것 입 니 다.
/**
* @param pixels
* @param w
* @param h
* @param pixel
* @param newColor
* @param i
* @param j
*/
private void fillColor(int[] pixels, int w, int h, int pixel, int newColor, int i, int j)
{
// 1: (x, y) ;
mStacks.push(new Point(i, j));
// 2: ,
// , (x, y),
// y ;
while (!mStacks.isEmpty())
{
/**
* 3: (x, y) , 、 ,
* 。 、 xLeft xRight;
*/
Point seed = mStacks.pop();
//L.e("seed = " + seed.x + " , seed = " + seed.y);
int count = fillLineLeft(pixels, pixel, w, h, newColor, seed.x, seed.y);
int left = seed.x - count + 1;
count = fillLineRight(pixels, pixel, w, h, newColor, seed.x + 1, seed.y);
int right = seed.x + count;
/**
* 4:
* y - 1 y + 1 [xLeft, xRight] ,
* xRight xLeft , AAABAAC(A ),
* B C A , (2) ;
*/
// y-1
if (seed.y - 1 >= 0)
findSeedInNewLine(pixels, pixel, w, h, seed.y - 1, left, right);
// y+1
if (seed.y + 1 < h)
findSeedInNewLine(pixels, pixel, w, h, seed.y + 1, left, right);
}
}
내 가 이 알고리즘 의 네 가지 절 차 를 이 방법 에 명확 하 게 표시 한 것 을 볼 수 있다.자,마지막 으로 의존 하 는 세부 적 인 방법 입 니 다.
/**
*
*
* @param pixels
* @param pixel
* @param w
* @param h
* @param i
* @param left
* @param right
*/
private void findSeedInNewLine(int[] pixels, int pixel, int w, int h, int i, int left, int right)
{
/**
*
*/
int begin = i * w + left;
/**
*
*/
int end = i * w + right;
boolean hasSeed = false;
int rx = -1, ry = -1;
ry = i;
/**
* end begin, (AAABAAAB, B A )
*/
while (end >= begin)
{
if (pixels[end] == pixel)
{
if (!hasSeed)
{
rx = end % w;
mStacks.push(new Point(rx, ry));
hasSeed = true;
}
} else
{
hasSeed = false;
}
end--;
}
}
/**
* ,
*
* @return
*/
private int fillLineRight(int[] pixels, int pixel, int w, int h, int newColor, int x, int y)
{
int count = 0;
while (x < w)
{
//
int index = y * w + x;
if (needFillPixel(pixels, pixel, index))
{
pixels[index] = newColor;
count++;
x++;
} else
{
break;
}
}
return count;
}
/**
* ,
*
* @return
*/
private int fillLineLeft(int[] pixels, int pixel, int w, int h, int newColor, int x, int y)
{
int count = 0;
while (x >= 0)
{
//
int index = y * w + x;
if (needFillPixel(pixels, pixel, index))
{
pixels[index] = newColor;
count++;
x--;
} else
{
break;
}
}
return count;
}
private boolean needFillPixel(int[] pixels, int pixel, int index)
{
if (hasBorderColor)
{
return pixels[index] != mBorderColor;
} else
{
return pixels[index] == pixel;
}
}
/**
*
*
* @return
*/
private int randomColor()
{
Random random = new Random();
int color = Color.argb(255, random.nextInt(256), random.nextInt(256), random.nextInt(256));
return color;
}
ok,여기까지 코드 소개 완료~~~마지막 으로 레이아웃 파일 을 붙 여 주세요~~
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:zhy="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
tools:context=".MainActivity">
<com.zhy.colour_app_01.ColourImageView
zhy:border_color="#FF000000"
android:src="@drawable/image_007"
android:background="#33ff0000"
android:layout_width="match_parent"
android:layout_centerInParent="true"
android:layout_height="match_parent"/>
</RelativeLayout>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="ColourImageView">
<attr name="border_color" format="color|reference"></attr>
</declare-styleable>
</resources>
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 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에 따라 라이센스가 부여됩니다.