sp단위로 인한 참사

3454 단어
내 동료 중 한 명이 다음과 같은 컨트롤을 사용자 정의했습니다.
 
public class CustomTextView extends TextView implements OnClickListener,
		OnFocusChangeListener {

	public CustomTextView(Context context) {
		super(context);
		init(context);
	}

	public CustomTextView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		init(context);
	}

	public CustomTextView(Context context, AttributeSet attrs) {
		super(context, attrs);
		init(context);
	}

	private void init(Context context) {
                 setTextSize(context.getResources().getDimension(R.dimen.dimen_30sp));
		setOnClickListener(this);
		setOnFocusChangeListener(this);
	}



	@Override
	public void onClick(View v) {
	}


	@Override
	public void onFocusChange(View v, boolean hasFocus) {
	}

    
}

 
 
컨트롤을 정의할 때 컨트롤의 텍스트 크기를 30sp로 정의합니다. 어울리기 위해 30sp는dimens에서 시작합니다.xml 파일에서 가져옵니다.당시 이 컨트롤을 썼을 때 화면 해상도는 320x480, 밀도는 160dpi, 30sp=30dp=30px였는데 나중에 480x640의 해상도에 맞게 240의 화면이 필요했다. 이때 30sp=30dp=45px였다.
 
그러나 결과는 예상을 크게 벗어났다. 240dpi의 화면에서 이 컨트롤러의 글씨체는 45px가 아니라 67.5px로 우리의 요구에 부합되지 않았다.그 중 한 동료가 오랫동안 괴로워하며 나에게 가르침을 청했다. 그래서 나는 setTextSize 함수가 무슨 짓을 했는지 의심했다. 그래서 원본 코드를 보고 문득 깨달았다.
    /**
     * Set the default text size to the given value, interpreted as "scaled
     * pixel" units.  This size is adjusted based on the current density and
     * user font size preference.
     *
     * @param size The scaled pixel size.
     *
     * @attr ref android.R.styleable#TextView_textSize
     */
    @android.view.RemotableViewMethod
    public void setTextSize(float size) {
        setTextSize(TypedValue.COMPLEX_UNIT_SP, size);
    }

    /**
     * Set the default text size to a given unit and value.  See {@link
     * TypedValue} for the possible dimension units.
     *
     * @param unit The desired dimension unit.
     * @param size The desired size in the given units.
     *
     * @attr ref android.R.styleable#TextView_textSize
     */
    public void setTextSize(int unit, float size) {
        Context c = getContext();
        Resources r;

        if (c == null)
            r = Resources.getSystem();
        else
            r = c.getResources();

        setRawTextSize(TypedValue.applyDimension(
            unit, size, r.getDisplayMetrics()));
    }

 
ApplyDimension 함수를 살펴보겠습니다.
public static float applyDimension(int unit, float value,
                                       DisplayMetrics metrics)
    {
        switch (unit) {
        case COMPLEX_UNIT_PX:
            return value;
        case COMPLEX_UNIT_DIP:
            return value * metrics.density;
        case COMPLEX_UNIT_SP:
            return value * metrics.scaledDensity;
        case COMPLEX_UNIT_PT:
            return value * metrics.xdpi * (1.0f/72);
        case COMPLEX_UNIT_IN:
            return value * metrics.xdpi;
        case COMPLEX_UNIT_MM:
            return value * metrics.xdpi * (1.0f/25.4f);
        }
        return 0;
    }

원래 setTextSize 함수에 대응하는 단위 자체가 sp였다.240밀도에서 30sp=45px, setTextSize 함수 내부에 scaleDensity를 곱해야 한다면 setTextSize(30sp)의 실제 설정 크기는 30spx1sp=45x1이다.5=67.5px.
 
따라서 사용자 정의 컨트롤을 할 때 컨트롤의 텍스트 크기를 설정할 때 조심해야 한다. 그렇지 않으면 반나절을 해도 문제의 문제점을 찾을 수 없다.
 
따라서 가장 좋은 방법은 setTextSize(TypedValue.COMPLEX UNIT PX,getResources()를 사용하는 것이다.getDimension(R.dimen.dimen_30sp));

좋은 웹페이지 즐겨찾기