맞춤 뷰에서 가장 중요한 부분은 디자인입니다. 맞춤 그리기 복잡할 수 있습니다 이 문서 에서는 가장 일반적인 작업을 다룹니다.
자세한 내용은 드로어블 개요
onDraw() 재정의
맞춤 뷰를 그릴 때 가장 중요한 단계는
onDraw()
메서드를 사용하여 축소하도록 요청합니다. onDraw()
의 매개변수는 다음과 같습니다.
Canvas
뷰에서 자신을 그리는 데 사용할 수 있는 객체를 정의합니다. Canvas
클래스
텍스트, 선, 비트맵 및 기타 여러 그래픽을 그리기 위한 메서드를 정의합니다.
프리미티브입니다. onDraw()
에서 이러한 메서드를 사용하여
커스텀 사용자 인터페이스 (UI)를 제공합니다.
먼저
Paint
객체.
다음 섹션에서는 Paint
에 관해 자세히 설명합니다.
그리기 객체 만들기
이
android.graphics
프레임워크는 그리기를 다음 두 영역으로 나눕니다.
- 그릴 내용(
Canvas
에서 처리) - 그리기 방법(
Paint
에서 처리)
예를 들어 Canvas
는 선을 그리는 메서드를 제공합니다.
Paint
는 이 선의 색상을 정의하는 메서드를 제공합니다.
Canvas
에는 직사각형을 그리는 메서드가 있습니다. Paint
는 직사각형을 색상으로 채울지 또는 비워 둘지를 정의합니다.
Canvas
는 화면에 그릴 수 있는 도형을 정의합니다.
Paint
는 각 도형의 색상, 스타일, 글꼴 등을 정의합니다.
있습니다.
무언가를 그리기 전에 하나 이상의 Paint
객체를 만듭니다. 이
다음 예는 init
라는 메서드에서 이 작업을 실행합니다. 이 메서드는
Java의 생성자로부터 호출되지만 다음 위치에서 인라인으로 초기화될 수 있습니다.
있습니다.
Kotlin
@ColorInt private var textColor // Obtained from style attributes. @Dimension private var textHeight // Obtained from style attributes. private val textPaint = Paint(ANTI_ALIAS_FLAG).apply { color = textColor if (textHeight == 0f) { textHeight = textSize } else { textSize = textHeight } } private val piePaint = Paint(Paint.ANTI_ALIAS_FLAG).apply { style = Paint.Style.FILL textSize = textHeight } private val shadowPaint = Paint(0).apply { color = 0x101010 maskFilter = BlurMaskFilter(8f, BlurMaskFilter.Blur.NORMAL) }
자바
private Paint textPaint; private Paint piePaint; private Paint shadowPaint; @ColorInt private int textColor; // Obtained from style attributes. @Dimension private float textHeight; // Obtained from style attributes. private void init() { textPaint = new Paint(Paint.ANTI_ALIAS_FLAG); textPaint.setColor(textColor); if (textHeight == 0) { textHeight = textPaint.getTextSize(); } else { textPaint.setTextSize(textHeight); } piePaint = new Paint(Paint.ANTI_ALIAS_FLAG); piePaint.setStyle(Paint.Style.FILL); piePaint.setTextSize(textHeight); shadowPaint = new Paint(0); shadowPaint.setColor(0xff101010); shadowPaint.setMaskFilter(new BlurMaskFilter(8, BlurMaskFilter.Blur.NORMAL)); ... }
개체를 미리 만드는 것은 중요한 최적화 작업입니다. 조회수:
자주 다시 그려지고 많은 그리기 객체는 많은 비용이 드는 초기화를 필요로 합니다.
onDraw()
메서드 내에서 그리기 객체 크게 만들기
성능이 저하되고 UI가 느려질 수 있습니다.
레이아웃 이벤트 처리
맞춤 뷰를 올바르게 그리려면 크기를 확인해야 합니다. 복잡한 맞춤 뷰의 크기에 따라 여러 레이아웃 계산을 수행해야 할 때가 많습니다. 화면에 표시되는 영역과 형태를 알 수 있습니다. 운영 체제의 크기를 가정해서는 안 됩니다. 볼 수 있습니다. 한 앱에서만 뷰를 사용하더라도 해당 앱은 다양한 화면 크기, 여러 화면 밀도 및 다양한 가로세로 처리 세로 모드와 가로 모드에서 모두 비율을 지원합니다.
View
측정을 처리할 수 있는 방법이 많지만 대부분은
재정의됩니다. 뷰의 크기를 특별히 제어할 필요가 없는 경우
다음 메서드를 재정의합니다.
onSizeChanged()
뷰에 처음 할당될 때 onSizeChanged()
가 호출됩니다.
어떤 이유로든 뷰의 크기가 변경되는 경우 이 메서드를 다시 호출합니다. 계산
위치, 크기 및 뷰의 크기와 관련된 기타 값을
onSizeChanged()
: 그릴 때마다 다시 계산하는 대신
다음 예에서 onSizeChanged()
는 뷰가
는 차트의 경계 직사각형과
텍스트 라벨 및 기타 시각적 요소를 포함합니다.
뷰에 크기가 할당되면 Layout Manager는 뷰의 크기가
뷰의 패딩을 포함합니다. 패딩 값을 처리할 때는
뷰의 크기에 따라 달라집니다. 다음은 onSizeChanged()
의 스니펫으로,
방법은 다음과 같습니다.
Kotlin
private val showText // Obtained from styled attributes. private val textWidth // Obtained from styled attributes. override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) { super.onSizeChanged(w, h, oldw, oldh) // Account for padding. var xpad = (paddingLeft + paddingRight).toFloat() val ypad = (paddingTop + paddingBottom).toFloat() // Account for the label. if (showText) xpad += textWidth.toFloat() val ww = w.toFloat() - xpad val hh = h.toFloat() - ypad // Figure out how big you can make the pie. val diameter = Math.min(ww, hh) }
자바
private Boolean showText; // Obtained from styled attributes. private int textWidth; // Obtained from styled attributes. @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); // Account for padding. float xpad = (float)(getPaddingLeft() + getPaddingRight()); float ypad = (float)(getPaddingTop() + getPaddingBottom()); // Account for the label. if (showText) xpad += textWidth; float ww = (float)w - xpad; float hh = (float)h - ypad; // Figure out how big you can make the pie. float diameter = Math.min(ww, hh); }
뷰의 레이아웃 매개변수를 더 세밀하게 제어하려면 다음과 같이 구현합니다.
onMeasure()
이 메서드의 매개변수는
View.MeasureSpec
뷰의 상위 요소가 원하는 뷰의 크기를 알려주는 값과
그 크기가 하드 최대값이든 단순한 제안이든 상관없습니다. 최적화의 차원에서
이러한 값은 압축된 정수로 저장되며
View.MeasureSpec
: 각 정수에 저장된 정보를 압축해제합니다.
다음은 onMeasure()
구현의 예입니다. 이
차트가 최대한 커지도록 영역을 충분히 넓히려고 합니다.
를 라벨로 지정합니다.
Kotlin
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { // Try for a width based on your minimum. val minw: Int = paddingLeft + paddingRight + suggestedMinimumWidth val w: Int = View.resolveSizeAndState(minw, widthMeasureSpec, 1) // Whatever the width is, ask for a height that lets the pie get as big as // it can. val minh: Int = View.MeasureSpec.getSize(w) - textWidth.toInt() + paddingBottom + paddingTop val h: Int = View.resolveSizeAndState(minh, heightMeasureSpec, 0) setMeasuredDimension(w, h) }
자바
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // Try for a width based on your minimum. int minw = getPaddingLeft() + getPaddingRight() + getSuggestedMinimumWidth(); int w = resolveSizeAndState(minw, widthMeasureSpec, 1); // Whatever the width is, ask for a height that lets the pie get as big as it // can. int minh = MeasureSpec.getSize(w) - (int)textWidth + getPaddingBottom() + getPaddingTop(); int h = resolveSizeAndState(minh, heightMeasureSpec, 0); setMeasuredDimension(w, h); }
이 코드에는 다음과 같은 세 가지 중요 참고 사항이 있습니다.
- 계산 시 보기의 패딩을 고려합니다. 언급했듯이 이 작업은 뷰의 책임입니다.
- 도우미 메서드
resolveSizeAndState()
는 최종 너비 및 높이 값을 만드는 데 사용됩니다. 이 도우미는 적절한View.MeasureSpec
값을 뷰에 필요한 크기를onMeasure()
에 전달된 값에 추가합니다. onMeasure()
에는 반환값이 없습니다. 대신 를 호출하여 결과를 전달합니다.setMeasuredDimension()
이 메서드를 호출하는 것은 필수입니다. 이 호출을 생략하면View
클래스에서 런타임 예외가 발생합니다.
그리기
객체 생성 및 측정 코드를 정의한 후에는
onDraw()
모든 뷰는 onDraw()
를 다르게 구현합니다.
그러나 대부분의 뷰에서 공유하는 몇 가지 일반적인 작업이 있습니다.
- 다음을 사용하여 텍스트 그리기
drawText()
다음을 호출하여 서체 지정setTypeface()
을 호출하여 텍스트 색상을 지정합니다.setColor()
입니다. - 다음을 사용하여 기본 도형 그리기
drawRect()
,drawOval()
, 및drawArc()
입니다.setStyle()
- 다음을 사용하여 더 복잡한 도형 그리기:
Path
클래스에 대해 자세히 알아보세요.Path
에 선과 곡선을 추가하여 도형 정의 객체를 만들고, 도형을 사용하여 도형을 그립니다.drawPath()
입니다. 기본 도형과 마찬가지로 경로에 윤곽선을 표시하거나 색상을 채우거나 둘 다 적용할 수 있습니다.setStyle()
에 따라 다름 -
LinearGradient
를 만들어 그라데이션 채우기 정의 객체입니다. 전화걸기setShader()
을 사용하여 색이 채워진 도형에LinearGradient
를 사용합니다. - 다음을 사용하여 비트맵 그리기
drawBitmap()
다음 코드는 텍스트와 선, 도형을 혼합하여 그립니다.
Kotlin
private val data = mutableListOf<Item>() // A list of items that are displayed. private var shadowBounds = RectF() // Calculated in onSizeChanged. private var pointerRadius: Float = 2f // Obtained from styled attributes. private var pointerX: Float = 0f // Calculated in onSizeChanged. private var pointerY: Float = 0f // Calculated in onSizeChanged. private var textX: Float = 0f // Calculated in onSizeChanged. private var textY: Float = 0f // Calculated in onSizeChanged. private var bounds = RectF() // Calculated in onSizeChanged. private var currentItem: Int = 0 // The index of the currently selected item. override fun onDraw(canvas: Canvas) { super.onDraw(canvas) canvas.apply { // Draw the shadow. drawOval(shadowBounds, shadowPaint) // Draw the label text. drawText(data[currentItem].label, textX, textY, textPaint) // Draw the pie slices. data.forEach {item -> piePaint.shader = item.shader drawArc( bounds, 360 - item.endAngle, item.endAngle - item.startAngle, true, piePaint ) } // Draw the pointer. drawLine(textX, pointerY, pointerX, pointerY, textPaint) drawCircle(pointerX, pointerY, pointerRadius, textPaint) } } // Maintains the state for a data item. private data class Item( var label: String, var value: Float = 0f, @ColorInt var color: Int = 0, // Computed values. var startAngle: Float = 0f, var endAngle: Float = 0f, var shader: Shader )
자바
private List<Item> data = new ArrayList<Item>(); // A list of items that are displayed. private RectF shadowBounds; // Calculated in onSizeChanged. private float pointerRadius; // Obtained from styled attributes. private float pointerX; // Calculated in onSizeChanged. private float pointerY; // Calculated in onSizeChanged. private float textX; // Calculated in onSizeChanged. private float textY; // Calculated in onSizeChanged. private RectF bounds; // Calculated in onSizeChanged. private int currentItem = 0; // The index of the currently selected item. protected void onDraw(Canvas canvas) { super.onDraw(canvas); // Draw the shadow. canvas.drawOval( shadowBounds, shadowPaint ); // Draw the label text. canvas.drawText(data.get(currentItem).label, textX, textY, textPaint); // Draw the pie slices. for (int i = 0; i < data.size(); ++i) { Item it = data.get(i); piePaint.setShader(it.shader); canvas.drawArc( bounds, 360 - it.endAngle, it.endAngle - it.startAngle, true, piePaint ); } // Draw the pointer. canvas.drawLine(textX, pointerY, pointerX, pointerY, textPaint); canvas.drawCircle(pointerX, pointerY, pointerRadius, textPaint); } // Maintains the state for a data item. private class Item { public String label; public float value; @ColorInt public int color; // Computed values. public int startAngle; public int endAngle; public Shader shader; }
그래픽 효과 적용
Android 12 (API 수준 31)에서는
RenderEffect
클래스가 포함되어 있습니다. 블러, 색상 필터,
Android 셰이더 효과 등
View
객체 및
계층 구조를 제공합니다 효과를 연쇄 효과로 결합할 수 있습니다. 연쇄 효과는
혼합 효과를 적용할 수 있습니다. 이 기능 지원
기기 처리 능력에 따라 달라집니다.
또한 기본 레이어에 효과를
RenderNode
:
다음을 호출하여 View
View.setRenderEffect(RenderEffect)
입니다.
RenderEffect
객체를 구현하려면 다음을 실행합니다.
view.setRenderEffect(RenderEffect.createBlurEffect(radiusX, radiusY, SHADER_TILE_MODE))
프로그래밍 방식으로 뷰를 생성하거나 XML 레이아웃에서 뷰를 확장할 수 있습니다.
뷰 결합 또는
findViewById()