Fragment 템플릿 코드

|

Android Studio에서 자동으로 생성해주는 Fragment 템플릿 코드입니다. Factory Method 형태(Factory Method 디자인패턴이 아님)로 되어 있으며, Fragment가 attach 될 때 자동으로 OnFragmentInteractionListener가 등록되도록 되어 있습니다.


public class BlankFragment extends Fragment {

  private static final String ARG_PARAM1 = "param1";
  private static final String ARG_PARAM2 = "param2";

  private String mParam1;
  private String mParam2;

  private OnFragmentInteractionListener mListener;

  public BlankFragment() {
    // Required empty public constructor
  }

  public static BlankFragment newInstance(String param1, String param2) {
    BlankFragment fragment = new BlankFragment();
    Bundle args = new Bundle();
    args.putString(ARG_PARAM1, param1);
    args.putString(ARG_PARAM2, param2);
    fragment.setArguments(args);
    return fragment;
  }

  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    if (getArguments() != null) {
      mParam1 = getArguments().getString(ARG_PARAM1);
      mParam2 = getArguments().getString(ARG_PARAM2);
    }
  }

  @Override
  public View onCreateView(LayoutInflater inflater, ViewGroup container,
      Bundle savedInstanceState) {
    return inflater.inflate(R.layout.fragment_blank, container, false);
  }

  public void onButtonPressed(Uri uri) {
    if (mListener != null) {
      mListener.onFragmentInteraction(uri);
    }
  }

  @Override
  public void onAttach(Context context) {
    super.onAttach(context);
    if (context instanceof OnFragmentInteractionListener) {
      mListener = (OnFragmentInteractionListener) context;
    } else {
      throw new RuntimeException(context.toString()
          + " must implement OnFragmentInteractionListener");
    }
  }

  @Override
  public void onDetach() {
    super.onDetach();
    mListener = null;
  }

  public interface OnFragmentInteractionListener {

    // TODO: Update argument type and name
    void onFragmentInteraction(Uri uri);
  }
}

생성자에 매개변수가 많으면 Builder를 사용하자

|

보통 하나의 메소드가 가지는 매개변수의 개수를 최대 4개 정도까지가 사용하기 무난한 메소드라고 합니다. 그 이상이 될 경우, 해당 메소드를 사용하는 사용자에게 혼란이나 실수를 발생시키기 쉽기 때문에 매개변수를 줄이는 것을 권장합니다.

매개변수를 줄이는 방법으로는 보통 다음 3가지 방법이 있습니다.

  • 매개 변수를 쪼갤 수 있도록 메소드를 여러 개로 분할한다.
  • 매개 변수를 포함하는 헬퍼 클래스를 하나 생성해서 매소드의 파라메터로 헬퍼 클래스를 사용한다.
  • Builder를 사용한다.

여기서는 세 번째 방법인 Builder 사용법에 대한 예시를 알아봅니다.


매개변수가 많은 클래스 예시

public class Robot {

  private final String name;
  private final String nickName;
  private final String controlServerUrl;
  private final int width;
  private final int height;
  private final int x;
  private final int y;
  private final double orientation;

  public Robot(String name, String nickName, String controlServerUrl, int width, int height, int x,
      int y, double orientation) {
    this.name = name;
    this.nickName = nickName;
    this.controlServerUrl = controlServerUrl;
    this.width = width;
    this.height = height;
    this.x = x;
    this.y = y;
    this.orientation = orientation;
  }

  public Robot(String name, String nickName, String controlServerUrl, int width, int height, int x,
      int y) {
    this(name, nickName, controlServerUrl, width, height, x, y, 0.0f);
  }

  public Robot(String name, String nickName, String controlServerUrl, int width, int height) {
    this(name, nickName, controlServerUrl, width, height, 0, 0);
  }
}

이 경우 해당 클래스를 생성할 때 코드를 작성하거나 읽기가 어려워지며 버그가 발생하기 쉬워집니다.


JavaBeans 패턴 적용

고전적으로 사용하던 JavaBeans 패턴을 이용해서 각 매개변수의 setter를 생성해주는 방법도 있습니다.

package builder;

public class Robot {

  private String name;
  private String nickName;
  private String controlServerUrl;
  private int width;
  private int height;
  private int x;
  private int y;
  private double orientation;

  public void setName(String name) {
    this.name = name;
  }

  public void setNickName(String nickName) {
    this.nickName = nickName;
  }

  public void setControlServerUrl(String controlServerUrl) {
    this.controlServerUrl = controlServerUrl;
  }

  public void setWidth(int width) {
    this.width = width;
  }

  public void setHeight(int height) {
    this.height = height;
  }

  public void setX(int x) {
    this.x = x;
  }

  public void setY(int y) {
    this.y = y;
  }

  public void setOrientation(double orientation) {
    this.orientation = orientation;
  }
}

이 경우 인스턴스를 생성하기 위해서는 다음과 같은 코드를 사용합니다.

Robot robot = new Robot();
robot.setName("SuperBot");
robot.setNickName("HiBot");
robot.setHeight(50);
robot.setWidth(30);
robot.setX(10);
robot.setY(20);
robot.setOrientation(250.0f);

하나의 인스턴스 생성을 위해 메소드를 여러 번 호출해야 하기 때문에 객체가 완전히 생성하기 전까지 일관성(Consistency)을 보장받지 못하게 되었습니다. 또한 더 이상 final 키워드를 사용할 수 없기 때문에, 불변성을 갖지 못해 Thread Safety를 보장할 수가 없습니다.


Builder 패턴 적용

Builder 패턴을 적용하면 다음과 같습니다.

public class Robot {

  private final String name;
  private final int width;
  private final int height;
  private final int x;
  private final int y;
  private final double orientation;

  public static class Builder {

    private final String name;  // 필수 매개변수

    private int width = 0;
    private int height = 0;
    private int x = 0;
    private int y = 0;
    private double orientaiton = 0.0f;

    public Builder(String name) {
      this.name = name;
    }

    public Builder width(int value) {
      width = value;
      return this;
    }

    public Builder height(int value) {
      height = value;
      return this;
    }

    public Builder x(int value) {
      x = value;
      return this;
    }

    public Builder y(int value) {
      y = value;
      return this;
    }

    public Builder orientation(double value) {
      orientaiton = value;
      return this;
    }

    public Robot build() {
      return new Robot(this);
    }
  }

  private Robot(Builder builder) {
    name = builder.name;
    width = builder.width;
    height = builder.height;
    x = builder.x;
    y = builder.y;
    orientation = builder.orientaiton;
  }
}


사용 예제는 다음과 같습니다.

Robot robot = new Robot.Builder("SuperBot").height(30).width(20).x(100).y(100)
        .orientation(250.0f).build();

Mojave에서 pygame이 제대로 수행안되는 현상 해결하기

|

Mojave에서 pygame의 충돌

Sierra에서는 아무런 문제가 없었는데 Mojave로 업그레이드한 뒤 python3의 pygame이 제대로 호환되지 않는 문제가 있습니다. 이는 brew를 이용해서 설치한 python3에서 발생하는 문제이기 때문에, 공식 홈페이지를 통해 python3를 다운로드하면 해결이 됩니다.

ArrayList 정렬하는 방법

|

오름차순 정렬 예제

다음 예제는 숫자를 오름차순으로 정렬하는 예제입니다.

List<Integer> list = new ArrayList<>();

list.add(5);
list.add(3);
list.add(7);
list.add(6);
list.add(10);
list.add(4);

Collections.sort(list, new Comparator<Integer>() {
  @Override
  public int compare(Integer o1, Integer o2) {
    if (o1 < o2) {
      return -1;
    } else if (o1 > o2) {
      return 1;
    }
    return 0;
  }
});

for (int i = 0; i < list.size(); i++) {
  System.out.println(list.get(i));
}


수정된 버전 1

데이터 타입이 단순한 Integer 형태이기 때문에 비교하는 구문을 아래와 같이 수정할 수도 있습니다.

List<Integer> list = new ArrayList<>();

list.add(5);
list.add(3);
list.add(7);
list.add(6);
list.add(10);
list.add(4);

Collections.sort(list, new Comparator<Integer>() {
  @Override
  public int compare(Integer o1, Integer o2) {
    return (o1 - o2);
  }
});

for (int i = 0; i < list.size(); i++) {
  System.out.println(list.get(i));
}


수정된 버전 2

또한 Java에서 제공하는 대부분의 데이터 타입들은 compareTo 인터페이스를 구현해놓았기 때문에 아래와 같은 코드도 사용가능합니다.

List<Integer> list = new ArrayList<>();

list.add(5);
list.add(3);
list.add(7);
list.add(6);
list.add(10);
list.add(4);

Collections.sort(list, new Comparator<Integer>() {
  @Override
  public int compare(Integer o1, Integer o2) {
    return o1.compareTo(o2);
  }
});

for (int i = 0; i < list.size(); i++) {
  System.out.println(list.get(i));
}


Comparable 인터페이스 구현

가장 간편한건 아래의 예제와 같이 Comparable 인터페이스를 구현하는 방법입니다.

public class Data implements Comparable {

  private final int value;

  public Data(int value) {
    this.value = value;
  }

  public final int getValue() {
    return this.value;
  }

  @Override
  public int compareTo(Object o) {
    return getValue() - ((Data) o).getValue();
  }
}

이 경우는 아래와 같이 Collections.sort() 메소드에 별도의 매개변수없이 간편하게 정렬을 할 수 있습니다.

List<Data> list = new ArrayList<>();

list.add(new Data(3));
list.add(new Data(5));
list.add(new Data(4));
list.add(new Data(2));

Collections.sort(list);


파일명으로 오름 차순 정렬 예제

  public void refresh(String path) {
    mList.clear();
    File directory = new File(path);
    File[] files = directory.listFiles();

    for (int i = 0; i < files.length; i++) {
      mList.add(files[i]);
    }

    Comparator<File> cmpAsc = (o1, o2) -> o1.getName().compareTo(o2.getName());
    Collections.sort(mList, cmpAsc);

    notifyDataSetChanged();
  }


Child Node 각도에 따라 정렬하는 예제

각도는 12시 방향을 원점으로 해서 반시계 방향으로 구했습니다.

  void sort() {
    Collections.sort(childList, (o1, o2) -> {

      int px = left() + width() / 2;
      int py = top + height() / 2;

      int cx1 = o1.left() + o1.width() / 2;
      int cy1 = o1.top() + o1.height() / 2;

      int cx2 = o2.left() + o2.width() / 2;
      int cy2 = o2.top() + o2.height() / 2;

      int dx1 = cx1 - px;
      int dy1 = cy1 - py;

      int dx2 = cx2 - px;
      int dy2 = cy2 - py;

      double degree1 = Math.atan2(dx1, dy1);
      double degree2 = Math.atan2(dx2, dy2);

      if (degree1 < degree2) {
        return -1;
      } else if (degree1 > degree2) {
        return 1;
      } else {
        return 0;
      }
    });
  }

Fragment 추가 및 삭제 예제

|
void addLogViewerFragment() {
  logViewerFragment = new LogViewerFragment();
  FragmentManager fragmentManager = getSupportFragmentManager();
  FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
  fragmentTransaction.add(R.id.logviewer_layout, logViewerFragment).commit();
}
  
void removeLogviewerFragment() {
  FragmentManager fragmentManager = getSupportFragmentManager();
  FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
  fragmentTransaction.remove(logViewerFragment).commit();
  logViewerFragment = null;
}