enum을 문자열로 변경하는 방법

|

Java에서 enum으로 정의한 값을 문자열로 변경하는 방법은 여러가지가 있습니다.


toString 메소드를 오버라이딩

public enum LogLevel {

  VERB {
    @Override
    public String toString() {
      return "Verbose";
    }
  },
  INFO {
    @Override
    public String toString() {
      return "Info";
    }
  },
  DEBUG {
    @Override
    public String toString() {
      return "Debug";
    }
  },
  WARN {
    @Override
    public String toString() {
      return "Warning";
    }
  },
  ERROR {
    @Override
    public String toString() {
      return "Error";
    }
  }
}

public static void main(String[] args) {
  System.out.println(LogLevel.INFO);
}


name 메소드 사용하는 방법

public enum LogLevel {
  VERB,
  INFO,
  DEBUG,
  WARN,
  ERROR,
}

public static void main(String[] args) {
  System.out.println(LogLevel.INFO.name());
}


반대로 문자열을 이용해서 Enum을 생성하는 방법

public enum LogLevel {
  VERB,
  INFO,
  DEBUG,
  WARN,
  ERROR,
}

public static void main(String[] args) {
  LogLevel ll = Enum.valueOf(LogLevel.class, "ERROR")
  System.out.println(ll.name());
}

ROS 2.0 Crystal 설치 방법(apt 이용)

|

ROS 2.0 Crystal 버전부터는 Ubuntu의 apt install 명령어를 이용해서 더욱 더 간편하게 설치할 수 있게 되었습니다.

다음 포스팅은 Ubuntu 18.04 기준이며, 포맷 후 처음 설치하는 과정입니다.


Locale 설정

sudo locale-gen en_US en_US.UTF-8
sudo update-locale LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8
export LANG=en_US.UTF-8


GPG Key 설치

sudo apt update && sudo apt install curl gnupg2 lsb-release
curl http://repo.ros2.org/repos.key | sudo apt-key add -
sudo sh -c 'echo "deb [arch=amd64,arm64] http://packages.ros.org/ros2/ubuntu `lsb_release -cs` main" > /etc/apt/sources.list.d/ros2-latest.list'


vcs-tool 설치

sudo sh -c 'echo "deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main" > /etc/apt/sources.list.d/ros-latest.list'
sudo apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 0xB01FA116
sudo apt-get update
sudo apt-get install python3-vcstool


기본 컴포넌트 설치

sudo apt update && sudo apt install -y \
  build-essential \
  cmake \
  git \
  python3-colcon-common-extensions \
  python3-pip \
  wget

sudo apt install -y libpython3-dev

python3 -m pip install -U \
  argcomplete \
  flake8 \
  flake8-blind-except \
  flake8-builtins \
  flake8-class-newline \
  flake8-comprehensions \

  flake8-deprecated \
  flake8-docstrings \
  flake8-import-order \
  flake8-quotes \
  git+https://github.com/lark-parser/lark.git@0.7d \
  pytest-repeat \
  pytest-rerunfailures \
  pytest \
  pytest-cov \
  pytest-runner \
  setuptools

sudo apt install --no-install-recommends -y \
  libasio-dev \
  libtinyxml2-dev


ROS 2 패키지 설치

export CHOOSE_ROS_DISTRO=crystal  # or bouncy or ardent
sudo apt update

sudo apt install ros-$CHOOSE_ROS_DISTRO-desktop
sudo apt install ros-$CHOOSE_ROS_DISTRO-ros-base

그리고 ROS2 실행 환경을 만들려면

source /opt/ros/$CHOOSE_ROS_DISTRO/setup.bash

와 같은 명령어를 실행하면 되며, .bashrc 등에 위 명령어를 추가할 수도 있습니다.


OpenSplice 및 RTI Connext 설치

옵션입니다. ROS2는 기본적으로 fast-rtps를 사용하기 때문에 OpenSplice나 RTI Connext는 추가적으로 설치할 필요는 없습니다. 하지만 경고 메시지 등이 거슬려서 설치를 할 경우에는 아래와 같은 명령어를 이용해서 설치할 수 있습니다.

sudo apt update
sudo apt install ros-$ROS_DISTRO-rmw-opensplice-cpp # for OpenSplice
sudo apt install ros-$ROS_DISTRO-rmw-connext-cpp # for RTI Connext (requires license agreement)

Custom Tab Widget 사용 예제

|

drawable/tab_focused.xml

<shape xmlns:android="http://schemas.android.com/apk/res/android"
  android:padding="10dp"
  android:shape="rectangle">
  <gradient
    android:angle="-90"
    android:endColor="#ffcc00"
    android:startColor="#ffcc00"/>
</shape>


drawable/tab_pressed.xml

<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
  <item>
    <shape android:shape="rectangle">
      <solid android:color="#FFcccccc"/>
    </shape>
  </item>

  <item
    android:bottom="0dp"
    android:left="-3dp"
    android:right="-3dp"
    android:top="-3dp">
    <shape
      android:shape="rectangle">
      <padding
        android:bottom="15dp"
        android:left="10dp"
        android:right="10dp"
        android:top="15dp"/>
      <stroke
        android:color="#FF00ccff"
        android:width="2dp"/>
      <solid android:color="#00000000"/>
    </shape>
  </item>
</layer-list>


drawable/tab_selected.xml

<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
  <item>
    <shape android:shape="rectangle">
      <solid android:color="#FFefefef"/>
    </shape>
  </item>

  <item
    android:bottom="0dp"
    android:left="-3dp"
    android:right="-3dp"
    android:top="-3dp">
    <shape
      android:shape="rectangle">
      <padding
        android:bottom="15dp"
        android:left="10dp"
        android:right="10dp"
        android:top="15dp"/>
      <stroke
        android:color="#FF00ccff"
        android:width="2dp"/>
      <solid android:color="#00000000"/>
    </shape>
  </item>
</layer-list>


drawable/tab_unselected.xml

<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
  <item>
    <shape android:shape="rectangle">
      <solid android:color="#FFefefef"/>
    </shape>
  </item>

  <item
    android:bottom="0dp"
    android:left="-3dp"
    android:right="-3dp"
    android:top="-3dp">
    <shape
      android:shape="rectangle">
      <padding
        android:bottom="15dp"
        android:left="10dp"
        android:right="10dp"
        android:top="15dp"/>
      <stroke
        android:color="#FF00ccff"
        android:width="2dp"/>
      <solid android:color="#00000000"/>
    </shape>
  </item>
</layer-list>


drawable/tab_indicator.xml

<selector xmlns:android="http://schemas.android.com/apk/res/android">

  <!-- Non focused states -->
  <item android:drawable="@drawable/tab_unselected" android:state_focused="false" android:state_pressed="false" android:state_selected="false"/>
  <item android:drawable="@drawable/tab_selected" android:state_focused="false" android:state_pressed="false" android:state_selected="true"/>

  <!-- Focused states -->
  <item android:drawable="@drawable/tab_focused" android:state_focused="true" android:state_pressed="false" android:state_selected="false"/>
  <item android:drawable="@drawable/tab_focused" android:state_focused="true" android:state_pressed="false" android:state_selected="true"/>

  <!-- Pressed -->
  <item android:drawable="@drawable/tab_pressed" android:state_pressed="true" android:state_selected="true"/>
  <item android:drawable="@drawable/tab_pressed" android:state_pressed="true"/>

</selector>


tab_menu.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
  android:background="@drawable/tab_indicator"
  android:orientation="vertical">

  <TextView
    android:id="@+id/textView"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center_vertical|center_horizontal"
    android:textSize="12sp"/>

</LinearLayout>


main_activity.xml

<?xml version="1.0" encoding="utf-8"?>
<< xmlns:app="http://schemas.android.com/apk/res-auto"
  xmlns:tools="http://schemas.android.com/tools"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:orientation="horizontal"
  tools:context=".MainActivity">

  <LinearLayout
    android:layout_width="0px"
    android:layout_height="wrap_content"
    android:layout_weight="1"
    android:orientation="vertical">
    <TabHost
      android:id="@+id/tab_host"
      android:layout_width="match_parent"
      android:layout_height="match_parent">

      <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <TabWidget
          android:id="@android:id/tabs"
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          android:theme="@style/MenuTabStyle"/>

        <FrameLayout
          android:id="@android:id/tabcontent"
          android:layout_width="match_parent"
          android:layout_height="wrap_content">

          <LinearLayout
            android:id="@+id/tab1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">
            <Button
              android:id="@+id/clearBtn"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:text="Clear"/>

          </LinearLayout>

          <LinearLayout
            android:id="@+id/tab2"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">

            <Button
              android:id="@+id/addRectBtn"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:text="Add Rect"/>


          </LinearLayout>

          <LinearLayout
            android:id="@+id/tab3"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">

            <Button
              android:id="@+id/addLineBtn"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:text="Add Line"/>

          </LinearLayout>
        </FrameLayout>
      </LinearLayout>

    </TabHost>

  </LinearLayout>

  <FrameLayout
    android:id="@+id/canvas_view"
    android:layout_width="0px"
    android:layout_height="match_parent"
    android:layout_weight="5"/>


</LinearLayout>


MainActivity.java

public class MainActivity extends AppCompatActivity {

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    TabHost tabHost = (TabHost) findViewById(R.id.tab_host);
    tabHost.setup();
    setNewTab(tabHost, "Normal", R.id.tab1);
    setNewTab(tabHost, "Rect", R.id.tab2);
    setNewTab(tabHost, "Line", R.id.tab3);

    tabHost.setOnTabChangedListener(new OnTabChangeListener() {
      @Override
      public void onTabChanged(String tabId) {
        //TODO

      }
    });
  }

  private void setNewTab(TabHost host, String title, int contentID) {
    TabHost.TabSpec tabSpec = host.newTabSpec(title);
    tabSpec.setIndicator(getTabIndicator(title));
    tabSpec.setContent(contentID);
    host.addTab(tabSpec);
  }

  private View getTabIndicator(String title) {
    View view = LayoutInflater.from(getApplicationContext()).inflate(R.layout.tab_menu, null);
    TextView tv = view.findViewById(R.id.textView);
    tv.setText(title);
    return view;
  }
}

Reflection 과 Annotation 같이 사용하기

|

Reflection을 이용하면 Runtime 환경에서 Java의 Class와 메소드, 파라메터 등의 정보를 얻을 수 있습니다. 파라메터의 경우 데이터 타입과 이름을 얻을 수 있지만, 파라메터의 이름의 경우에는 실제로 선언한 변수명이 아니라 arg1, arg2 등과 같은 변형된 변수명으로 얻게 되어 있습니다. 이는 컴파일되는 도중 컴파일러가 적당한 이름으로 변수명을 변경해버리기 때문인데, Java 버전을 바꾸거나 컴파일 옵션을 수정해서 개발자가 지정한 변수명을 얻을 수는 있습니다.

하지만 기본적으로는 arg1 과 같이 사람이 알아보기 힘든 이름이 되어 버리는데 Annotation을 적절하게 이용하면 이 문제를 조금은 더 수월(?)하게 해결할 수 있습니다.


ParamName1.java

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
public @interface ParamName1 {

  String name();
}


ParamName2.java

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
public @interface ParamName2 {

  String name();
}

## Annotation 적용 예제
public class SpeakText extends Action {
    String text;

    @ParamName1(name="text")
    public SpeakText(String text) {
        super("SampleClass(" + text + ")");
        this.text = text;
    }

    @ParamName1(name="name")
    @ParamName2(name="text")
    public SpeakText(String name, String text) {
        super(name);
        this.text = text;
    }
}

## Annotation 정보를 획득하는 예제
    Class c = actionClassMap.get(key);

      Constructor[] cons = c.getDeclaredConstructors();

      for (int i = 0; i < cons.length; i++) {
        Log(cons[i].getName() + ", " + cons[i].toString());

        Annotation[] anns = cons[i].getAnnotations();

        try {
          ParamName1 p = (ParamName1) cons[i].getAnnotation(ParamName1.class);

          if (p != null) {
            Log("   -- Anno : " + p.name());
          }
        } catch (Exception e) {
          e.printStackTrace();
        }

        try {
          ParamName2 p = (ParamName2) cons[i].getAnnotation(ParamName2.class);

          if (p != null) {
            Log("   -- Anno : " + p.name());
          }
        } catch (Exception e) {
          e.printStackTrace();
        }
        

Local Service 예제

|

SnowService.java

package com.snowdeer.local_service;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;

public class SnowService extends Service {

  int num = 0;

  public int getNumber() {
    num++;
    return num;
  }

  public class LocalBinder extends Binder {
    SnowService getService() {
      return SnowService.this;
    }
  }

  final Binder binder = new LocalBinder();

  @Override
  public IBinder onBind(Intent intent) {
    Log.i("snowdeer", "[snowdeer] SnowService - onBind()");
    return binder;
  }

  @Override
  public boolean onUnbind(Intent intent) {
    Log.i("snowdeer", "[snowdeer] SnowService - onUnbind()");
    return super.onUnbind(intent);
  }
}


MainActivity.java

package com.snowdeer.local_service;

import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import com.snowdeer.local_service.SnowService.LocalBinder;

public class MainActivity extends AppCompatActivity {

  SnowService snowService = null;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    Log.i("snowdeer", "[snowdeer] onCreate");

    Intent i = new Intent(MainActivity.this, SnowService.class);
    startService(i);

    bindService(i, mConnection, BIND_AUTO_CREATE);
  }

  @Override
  protected void onDestroy() {
    super.onDestroy();
    unbindService(mConnection);
    Log.i("snowdeer", "[snowdeer] onDestroy");
  }

  ServiceConnection mConnection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
      Log.i("snowdeer", "[snowdeer] onServiceConnected");

      snowService = ((LocalBinder)service).getService();

      Log.i("snowdeer", "[snowdeer] num: " + snowService.getNumber());
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
      Log.i("snowdeer", "[snowdeer] onServiceDisconnected");
    }
  };
}