본문 바로가기

Android/Custom view attributes

Android, Handling Custom view attributes

Google에서 제공하는 커스텀뷰 예제 (LabelView.java)
이 예제를 통해서 커스텀뷰가 갖는 속성값을 레이아웃xml 파일에서 설정하는 방법을 확인한다.

1. 선언 : res/values/attr.xml 에서 커스텀속성 선언
2. 속성값설정: res/layout/main.xml (레이아웃파일)에서 속성설정(사용)
3. 속성값추출: 커스텀뷰 클래스의 생성자에서 커스텀 속성을 추출하여 클래스를 설정


LabelView.java
/*
 * Copyright (C) 2007 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


package com.example.android.apis.view;

// Need the following import to get access to the app resources, since this
// class is in a sub-package.
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;

import com.example.android.apis.R;


/**
 * Example of how to write a custom subclass of View. LabelView
 * is used to draw simple text views. Note that it does not handle
 * styled text or right-to-left writing systems.
 *
 */

public class LabelView extends View {
   
private Paint mTextPaint;
   
private String mText;
   
private int mAscent;

   
/**
     * Constructor.  This version is only needed if you will be instantiating
     * the object manually (not from a layout XML file).
     * @param context
     */

   
public LabelView(Context context) {
       
super(context);
        initLabelView
();
   
}

   
/**
     * Construct object, initializing with any attributes we understand from a
     * layout file. These attributes are defined in
     * SDK/assets/res/any/classes.xml.
     *
     * @see android.view.View#View(android.content.Context, android.util.AttributeSet)
     */

   
public LabelView(Context context, AttributeSet attrs) {
       
super(context, attrs);
        initLabelView
();

        /* 레이아웃 파일에 설정된 커스텀뷰의 속성은 다음과 같은 방법으로 추출하여 사용할 수 있다 */
       
TypedArray a = context.obtainStyledAttributes(attrsR.styleable.LabelView);

       
CharSequence s = a.getString(R.styleable.LabelView_text);
       
if (s != null) {
            setText
(s.toString());
       
}

       
// Retrieve the color(s) to be used for this view and apply them.
       
// Note, if you only care about supporting a single color, that you
       
// can instead call a.getColor() and pass that to setTextColor().
        setTextColor
(a.getColor(R.styleable.LabelView_textColor, 0xFF000000));

       
int textSize = a.getDimensionPixelOffset(R.styleable.LabelView_textSize, 0);
       
if (textSize > 0) {
            setTextSize
(textSize);
       
}

        a
.recycle();
   
}

   
private final void initLabelView() {
        mTextPaint
= new Paint();
        mTextPaint
.setAntiAlias(true);
        mTextPaint
.setTextSize(16);
        mTextPaint
.setColor(0xFF000000);
        setPadding
(3, 3, 3, 3);
   
}

   
/**
     * Sets the text to display in this label
     * @param text The text to display. This will be drawn as one line.
     */

   
public void setText(String text) {
        mText
= text;
        requestLayout
();
        invalidate
();
   
}

   
/**
     * Sets the text size for this label
     * @param size Font size
     */

   
public void setTextSize(int size) {
        mTextPaint
.setTextSize(size);
        requestLayout
();
        invalidate
();
   
}

   
/**
     * Sets the text color for this label.
     * @param color ARGB value for the text
     */

   
public void setTextColor(int color) {
        mTextPaint
.setColor(color);
        invalidate
();
   
}

   
/**
     * @see android.view.View#measure(int, int)
     */

   
@Override
   
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension
(measureWidth(widthMeasureSpec),
                measureHeight
(heightMeasureSpec));
   
}

   
/**
     * Determines the width of this view
     * @param measureSpec A measureSpec packed into an int
     * @return The width of the view, honoring constraints from measureSpec
     */

   
private int measureWidth(int measureSpec) {
       
int result = 0;
       
int specMode = MeasureSpec.getMode(measureSpec);
       
int specSize = MeasureSpec.getSize(measureSpec);

       
if (specMode == MeasureSpec.EXACTLY) {
           
// We were told how big to be
            result
= specSize;
       
} else {
           
// Measure the text
            result
= (int) mTextPaint.measureText(mText) + getPaddingLeft()
                   
+ getPaddingRight();
           
if (specMode == MeasureSpec.AT_MOST) {
               
// Respect AT_MOST value if that was what is called for by measureSpec
                result
= Math.min(result, specSize);
           
}
       
}

       
return result;
   
}

   
/**
     * Determines the height of this view
     * @param measureSpec A measureSpec packed into an int
     * @return The height of the view, honoring constraints from measureSpec
     */

   
private int measureHeight(int measureSpec) {
       
int result = 0;
       
int specMode = MeasureSpec.getMode(measureSpec);
       
int specSize = MeasureSpec.getSize(measureSpec);

        mAscent
= (int) mTextPaint.ascent();
       
if (specMode == MeasureSpec.EXACTLY) {
           
// We were told how big to be
            result
= specSize;
       
} else {
           
// Measure the text (beware: ascent is a negative number)
            result
= (int) (-mAscent + mTextPaint.descent()) + getPaddingTop()
                   
+ getPaddingBottom();
           
if (specMode == MeasureSpec.AT_MOST) {
               
// Respect AT_MOST value if that was what is called for by measureSpec
                result
= Math.min(result, specSize);
           
}
       
}
       
return result;
   
}

   
/**
     * Render the text
     *
     * @see android.view.View#onDraw(android.graphics.Canvas)
     */

   
@Override
   
protected void onDraw(Canvas canvas) {
       
super.onDraw(canvas);
        canvas
.drawText(mText, getPaddingLeft(), getPaddingTop() - mAscent, mTextPaint);
   
}
}

다음과 같은 방법으로 xml 레이아웃파일에 설정된 속성의 값을 추출하는 것을 확인할 수 있다.

TypedArray a=context.obtainStyledAttributes(attrs, R.styleable.LabelView); 
CharSequences=a.getString(R.styleable.LabelView_text); 

커스텀뷰 클래스에서 레이아웃파일의 속성을 사용하기 위해서는 일단 사용할 속성을 선언해주어야 한다.
상위 클래스에 이미 선언되어 있는 속성은 다시 이곳에 선언하지 않아도 사용할 수 있으며,
비표준 속성을 선언할 때는 format속성을 함께 사용하여 속성의 형식을 표현해야 한다.

res/values/attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2007 The Android Open Source Project

     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at

          http://www.apache.org/licenses/LICENSE-2.0

     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     See the License for the specific language governing permissions and
     limitations under the License.
-->


<resources>
   
<!-- These are the attributes that we want to retrieve from the theme
         in app/PreferencesFromCode.java -->

   
<declare-styleable name="TogglePrefAttrs">
       
<attr name="android:preferenceLayoutChild" />
   
</declare-styleable>

   
<!-- These are the attributes that we want to retrieve from the theme
         in view/Gallery1.java -->

   
<declare-styleable name="Gallery1">
       
<attr name="android:galleryItemBackground" />
   
</declare-styleable>

     
<declare-styleable name="LabelView">
       
<attr name="text" format="string" />
       
<attr name="textColor" format="color" />
       
<attr name="textSize" format="dimension" />
   
</declare-styleable>
</resources>


커스텀 속성은 위처럼 선언하고 format속성을 사용하여 자료의 형식을 지정해주고 있다.


위와같이 선언된 속성은 주 레이아웃파일에서 다음과 같이 커스텀 뷰의 속성으로 설정할 수 있다.

res/layout/custom_view_1.xml
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2007 The Android Open Source Project

     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at

          http://www.apache.org/licenses/LICENSE-2.0

     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     See the License for the specific language governing permissions and
     limitations under the License.
-->


<!-- Demonstrates defining custom views in a layout file. -->

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
       
xmlns:app="http://schemas.android.com/apk/res/com.example.android.apis.view"
       
android:orientation="vertical"
       
android:layout_width="match_parent"
       
android:layout_height="wrap_content">

   
<com.example.android.apis.view.LabelView
           
android:background="@drawable/red"
           
android:layout_width="match_parent"
           
android:layout_height="wrap_content"
           
app:text="Red"/>

   
<com.example.android.apis.view.LabelView
           
android:background="@drawable/blue"
           
android:layout_width="match_parent"
           
android:layout_height="wrap_content"
           
app:text="Blue" app:textSize="20dp"/>

   
<com.example.android.apis.view.LabelView
           
android:background="@drawable/green"
           
android:layout_width="match_parent"
           
android:layout_height="wrap_content"
           
app:text="Green" app:textColor="#ffffffff" />

</LinearLayout>