2015. 1. 25. 21:02




선택적 인자가 많은 객체를 만들때 '점층적 생성자 패턴'을 사용한다. 필수인자만 받는 생성자 하나를 정의하고, 선택적 인자를 하나 받는 생성자를 추가하고, 선택적 인자를 두개받는 생성자를 하나 추가하는 식으로 점층적으로 인자의 갯수를 늘리는 식의 패턴이다. 예제 코드를 살펴보면,


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class NutiritionFacts {
    private final int servingSize;            // 필수
    private final int servings;                    // 필수
    private final int calories;                    // 선택
    private final int fat;                            // 선택
    private final int sodium;                    // 선택
    private final int carbohydrate;            // 선택
 
    public NutiritionFacts ( int servingSize, int servings ) {
        this( servingSize, servings, 0 );
    }
 
    public NutiritionFacts ( int servingSize, int servings, int calories ) {
        this( servingSize, servings, calories, 0 );
    }
 
    public NutiritionFacts ( int servingSize, int servings, int calories, int fat ) {
        this( servingSize, servings, calories, fat, 0 );
    }
 
    public NutiritionFacts ( int servingSize, int servings, int calories, int fat,
                            int sodium) {
        this( servingSize, servings, calories, fat, sodium, 0 );
    }
 
    public NutiritionFacts ( int servingSize, int servings, int calories, int fat,
                            int sodium, int carbohydrate ) {
        this( servingSize, servings, calories, fat, sodium, carbohydrate );
    }
}
cs


필수 항목을 제외하고는 설정하려는 인자를 순서에 맞게 호출하면 된다.

1
2
NutiritionFacts cocaCola = new NutiritionFacts(240810033527);
 
cs


이런 패턴의 문제점이 몇가지 있는데, 설정할 필요가 없는 필드에도 인자를 전달해야 하는 점과(예제에서는 0으로 전달해야한다.) 인자 수가 늘어나면 이 인자가 무슨 인자인지 저 인자가 무슨 인자인지 알 수 없는 경우가 생긴다.


<참고서적 : Effective Java 2/E>

Posted by 미뤽
2015. 1. 25. 20:26




카메라를 사용하는 어플을 만들어서 문제 없이 사용하고 있었는데 일부폰에서 카메라를 찍고 돌아왔을때 앱이 죽는 현상이 있었다. 특정 회사, 특정 폰에서 나타나는 현상이었는데 아무리 구글링을 해도 원인을 찾을 수가 없었다. 해결방법은 간단했다.


1
android:configChanges="orientation|screenSize"
cs


위 코드를 AndroidManifest.xml내에서 해당 activity에 추가해주면 된다. 어플리케이션에서 configuration이 바뀌는 경우가 있는데 (언어, 레이아웃방향, 사이즈, 글씨 크기 등등) 그때마다 핸드폰은 해당 activity를 다시 실행한다. 위 코드는 레이아웃 방향의 변화는 스크린 사이즈가 변화가 있어도 activity를 다시 실행하는(다시 그리는) 동작을 하지 말라는 코드다.


원인은 일부 폰에서 카메라 기능을 사용할 때의 레이아웃방향과 내가 만든 어플의 레이아웃방향이 다를때 일어나는 현상이었다. 보통은 카메라 기능을 사용하고 돌아왔을때 결과값만을 가지고 오기때문에 문제가 되지 않지만 일부폰에서는 그때 사용한 레이아웃방향까지 인식하여 자체적으로 configuration change가 일어난 상황으로 인식해 해당 activity를 재실행하다가 생기는 버그였다.(null 포인트가 잡힌다.) 위의 코드를 추가해주면 activity의 재실행을 막아주므로 해당 버그를 막을 수 있다.

Posted by 미뤽
2015. 1. 17. 21:56




 저번 글에서 ViewHolder에 대해서 구글링 하던중 흥미로운 글들을 몇개 발견해서 ViewHolder 2탄을 준비했다. 2탄의 주제는 'ViewHolder보다 더 효과적인 방법' 이다. 세가지 방법에 대해서 살펴볼 생각인데 '사실 확실히 좋다!'는 확신은 없다는 점을 알아주었으면 한다. ViewHolder패턴이 좋다고 생각한 이유도 기실 'findViewById()가 상당히 부담이 되는 작업이다' 라고 전문가가 말한것을 그대로 수용했기 때문이지 내가 이해했던 것이 아니기 때문이다.(그정도의 깊이가 아직은 없다. 아직은) 그럼 하나씩 살펴보도록 하겠다.


관련 글 링크 : http://www.kmshack.kr/android-%EC%9C%A0%EC%97%B0%EC%84%B1-%EC%9E%88%EB%8A%94-viewholder-pattern/


위 글에서 말하는 첫번째 방법은 ViewHolder에 유연성 있는 코딩을 말한다. 기존 ViewHolder패턴에서는 static class로 만들고 해당 아이템(ListView의 한 줄 )에서 사용할 View들을 ViewHolder의 멤버변수로 저장한다. 이렇게 되면 여러아이템이 다른 View들을 가지고 있다면 여러개의 ViewHolder를 만들어야하므로 이럴때 사용할 수 있는 유연성있는 ViewHolder가 필요하다는 말이다.(이런 경우가 많지는 않을거 같지만.) 방법을 살펴보면,


1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static class ViewHolder {
    public static <T extends View> T get( View view , int id ) {
        SparseArray<View> viewHolder = ( SparseArray<View> ) view.getTag();
        if( viewHolder == null) {
            viewHolder = new SparseArray<View>();
            view.setTag( viewHolder );
        }
        View childView = viewHolder.get( id );
        if ( childView == null ) {
            childView = view.findViewById( id );
            viewHolder.put( id, childView );
        }
        return (  T ) childView;
}
cs


위 코드에서 보다시피 더이상 ViewHolder에 맴버변수가 없다. 대신 ViewHolder class내부에 viewHolder SparseArray에 사용할 뷰들을 넣고 나중에 해당 뷰들을 꺼내 쓴다. getView()에서 사용방법은 아래코드와 같다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Override
public View getView( int position, View convertView, ViewGroup parent ) {
    if ( convertView == null ) {
        convertView = LayoutInflater.from( context ).inflate( R.layout.list_item,
             parent, false );
    }
    ImageView imageView = ViewHolder.get( convertView, R.id.imageview );
    TextView textView = ViewHolder.get( convertView, R.id.textview );
 
    ListItem item = getItem( position );
    
    imageView.setImageResource( item.getImage() );
    textView.setTet( item.getText() ) ;
 
    return convertView;
}
cs





관련글 링크 : http://www.jayway.com/2013/11/06/viewholder-vs-holderview/


두번째 글에서 말하는 방법은, ListView의 각각의 Item에서 하는일을 그 뷰가 알아서 하도록 하는 방법이다. 성능적인 면에서의 차이는 잘 모르겠지만 ListItem에 사용할 각각의 Item들을 Adapter의 getView() 메소드에서 선언, 처리 하던 방식을 사용하지 않고, Adapter에서는 기존처럼 findViewById()의 사용만 피하면서 해당 아이템의 holderViewclass 객체를 불러오면 View의 선언이나 사용은 전부 holderView클래스 내부에서 처리하는 방법이다. 사용법을 살펴보자.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 
@Override
public View getView(int i, View convertView, ViewGroup viewGroup) {
    HolderView holderView;
    // Important to not just null check, but rather to a instanceof
    // since we might get any subclass of view here.
    if (convertView instanceof HolderView) {
        holderView = (HolderView) convertView;
    } else {
        holderView = new HolderView(mContext);
    }
    holderView.bind(new Digit(i));
 
    return holderView;
}
cs


Adapter내의 getView()메소드는 정말 간단하다. holderView객체를 찾거나 생성한후 데이터를 보낸다. 객체 내부를 살펴보면


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
 
public class HolderView extends GridLayout {
 
    private TextView mDigitDigit;
    private String mDigitText;
 
    public HolderView(Context context, AttributeSet attrs) {
        super(context, attrs);
        View v = LayoutInflater.from(context).inflate(R.layout.list_detail, this);
        mDigitDigit = (TextView) v.findViewById(R.id.list_detail_digit);
        mDigitText = context.getResources().getString(R.string.list_detail_digit);
    }
 
    public void bind(Digit digit) {
        mDigitDigit.setText(String.format(mDigitText, digit));
    }
}
cs


객체가 생성되는 순가 해당아이템에서 사용할 View들을 찾아 연결해주고 bind()메소드를 통해서 들어오는 값을 HolderView내부의 View와 연결해서 Ui에 나타낸다. 직관적인 코드라서 관리가 더 용이하고 코드를 이해하기도 좋다고 한다(?)




관련글 링크 : http://www.slideshare.net/bs_yagi/potato01-no-more-viewholder


위 링크에서 소개하는 마지막 방법은 두번째 방법과 흡사하다. Adapter내부에서 하던 View를 연결하는 작업을 ViewHolder내부에서 객체 생성시에 한다. 비슷한 방법이지만 소개하는 이유는 몇몇 특징 때문인데 한번 살펴보면


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class ViewHolder {
    public final ImageView thumbImage;
    public final TextView titleText;
    public final TextView readText;
    public final TextView userText;
    public final View root;
 
    public ViewHolder( View root ) {
        thumbImage = ( ImageView ) root.findViewById( R.id.thumb_image ) ;
        titleText = ( ImageView ) root.findViewById( R.id.title_text ) ;
        readText = ( ImageView ) root.findViewById( R.id.read_text ) ;
        userText = ( ImageView ) root.findViewById( R.id.user_text ) ;
        this.root = root;
    }
}
cs


1
2
3
4
5
6
7
8
@Override
public View getView( int position, View convertView, ViewGroup parent ) {
    if ( convertView == null ) {
        convertView = LayoutInflater.inflate(R.layout.list_view_item, null );
        ViewHolder holder = new ViewHolder ( convertView );
        convertView.setTag( holder );
    }
}
cs


코드를 보면 다른 코드들과는 다른 2가지를 볼 수 있는데 첫번째가 ViewHolder class가 static class가 아니라는 점과 멤버변수들을 final로 선언했다는 점이다. 기존 사용법에서 ViewHolder를 static으로 선언한 이유는 enclosing instance에 연결되는 경우를 막기위해서 인데 내부클래스로 ViewHolder를 하면 enclosing instance에러는 나지 않는다. 두번째 다른점인 final로 선언한 이유는 사실 아직 정확히 이해하지 못했다...(해당사이트 내용들이 일본말인것도 한 이유...) 우선 final에 대해서 다시 한번 처음부터 공부해봐야겠다.

Posted by 미뤽