CustomView 만들기

|

사용자 정의 View

안드로이드에서 CustomView를 만드는 방법입니다.

custom_view.xml

먼저 레이아웃을 작성합니다.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:padding="10dp"
  android:orientation="horizontal">

  <ToggleButton
    android:id="@+id/button1"
    android:layout_width="0px"
    android:layout_height="wrap_content"
    android:layout_weight="1"
    android:textOff="Off"
    android:textOn="On" />

  <ToggleButton
    android:id="@+id/button2"
    android:layout_width="0px"
    android:layout_height="wrap_content"
    android:layout_weight="1"
    android:textOff="Off"
    android:textOn="On" />

  <ToggleButton
    android:id="@+id/button3"
    android:layout_width="0px"
    android:layout_height="wrap_content"
    android:layout_weight="1"
    android:textOff="Off"
    android:textOn="On" />

</LinearLayout>


attrs.xml에 속성 추가

그런 다음, CustomView에 대한 속성을 정의합니다. Java 코드 내에서 속성을 지정할 때는 굳이 이런 과정이 필요없지만, XML에서도 속성을 지정하고 싶을 때는 아래와 같이 attrs.xml에 원하는 속성을 지정해야 합니다. attrs.xml 파일은 values 폴더 아래에 위치합니다.

<?xml version="1.0" encoding="utf-8"?>
<resources>
  <declare-styleable name="custom_view">
    <attr format="integer" name="selected" />
  </declare-styleable>
</resources>


CustomView.java

package snowdeer.customviewexample;

import android.content.Context;
import android.content.res.TypedArray;
import android.support.annotation.AttrRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.widget.LinearLayout;
import android.widget.ToggleButton;

public class CustomView extends LinearLayout {

  ToggleButton[] buttons = new ToggleButton[3];
  int selectedId = 0;

  public CustomView(@NonNull Context context,
      @Nullable AttributeSet attrs,
      @AttrRes int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    inflateViews(context, attrs);
  }

  public CustomView(@NonNull Context context,
      @Nullable AttributeSet attrs) {
    super(context, attrs);
    inflateViews(context, attrs);
  }

  void inflateViews(Context context, AttributeSet attrs) {
    LayoutInflater inflater = (LayoutInflater) context
        .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    inflater.inflate(R.layout.custom_view, this);

    if (attrs != null) {
      TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.custom_view);
      selectedId = array.getInteger(0, 0);
      array.recycle();
    }
  }

  @Override
  protected void onFinishInflate() {
    super.onFinishInflate();

    buttons[0] = (ToggleButton) findViewById(R.id.button1);
    buttons[1] = (ToggleButton) findViewById(R.id.button2);
    buttons[2] = (ToggleButton) findViewById(R.id.button3);

    setSelected(selectedId);
  }

  public void setSelected(int id) {
    if ((id < 0) || (id > 3)) {
      return;
    }

    selectedId = id;
    for (ToggleButton btn : buttons) {
      btn.setChecked(false);
    }

    buttons[id].setChecked(true);
  }

  public int getSelectedId() {
    return selectedId;
  }

}


활용

이제 CustomView 클래스의 생성이 끝났기 때문에 원하는 곳에서 사용을 하면 됩니다. 예를 들어, activity_main.xml에서 사용을 한다면 다음과 같이 배치해주면 됩니다.

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

  <snowdeer.customviewexample.CustomView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:selected="1" />

</LinearLayout>

SocketAddress 및 SocketAddressFactory 예제

|

SocketAddress.h

#ifndef SNOWSOCKET_SOCKETADDRESS_H
#define SNOWSOCKET_SOCKETADDRESS_H

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cstring>
#include <memory>
#include <w32api/inaddr.h>

using namespace std;

class SocketAddress {
 public:
  SocketAddress(uint32_t address, uint16_t port) {
    getSockAddrIn()->sin_family = AF_INET;
    getSockAddrIn()->sin_addr.s_addr = htonl(address);
    getSockAddrIn()->sin_port = htons(port);
  }

  SocketAddress(string address, uint16_t port) {
    getSockAddrIn()->sin_family = AF_INET;
    getSockAddrIn()->sin_addr.s_addr = inet_addr(address.c_str());
    getSockAddrIn()->sin_port = htons(port);
  }

  SocketAddress(const sockaddr &sockAddr) {
    memcpy(&mSockAddr, &sockAddr, sizeof(sockaddr));
  }

  size_t size() const {
    return sizeof(sockaddr);
  }

 private:
  sockaddr mSockAddr;
  sockaddr_in *getSockAddrIn() {
    return reinterpret_cast<sockaddr_in *> (&mSockAddr);
  }
};

using SocketAddressPtr = shared_ptr<SocketAddress>;

#endif //SNOWSOCKET_SOCKETADDRESS_H


SocketAddressFactory.h

#ifndef SNOWSOCKET_SOCKETADDRESSFACTORY_H
#define SNOWSOCKET_SOCKETADDRESSFACTORY_H

#include <cstdio>
#include <netdb.h>
#include "SocketAddress.h"

using namespace std;

class SocketAddressFactory {
 public:
  static SocketAddressPtr createIPv4SocketAddress(const string &address) {
    string host, service;

    auto pos = address.find_last_of(':');
    if (pos != string::npos) {
      host = address.substr(0, pos);
      service = address.substr(pos + 1);
    } else {
      host = address;
      service = "0";
    }

    addrinfo hint;
    memset(&hint, 0, sizeof(hint));
    hint.ai_family = AF_INET;

    addrinfo *_result = nullptr;
    int error = getaddrinfo(host.c_str(), service.c_str(), &hint, &_result);
    addrinfo *result = _result;

    if (error != 0 && _result != nullptr) {
      freeaddrinfo(result);

      return nullptr;
    }

    while (!_result->ai_addr && _result->ai_next) {
      _result = _result->ai_next;
    }

    if (!_result->ai_addr) {
      freeaddrinfo(result);

      return nullptr;
    }

    auto toRet = make_shared<SocketAddress>(*_result->ai_addr);

    freeaddrinfo(result);

    return toRet;
  }
};

#endif //SNOWSOCKET_SOCKETADDRESSFACTORY_H

arecord 및 aplay 사용방법

|

alsa-utils 설치

사운드를 녹음하는 arecord와 사운드를 재생하는 aplay 프로그램은 alsa-utils에 포함되어 있습니다.

sudo apt-get install alsa-utils

명령어로 두 프로그램을 설치할 수 있습니다.


arecord 사용법

arecord는 다음과 같이 사용할 수 있습니다.

arecord -t raw -c 1 -D plughw:1,0 -f S16_LE -d 5 -r 16000 test.pcm

-t 옵션은 타입으로 다음과 같은 타입을 가집니다.

  • voc
  • wav
  • raw
  • au

-c 옵션은 채널 개수를 의미합니다.

-D 옵션은 디바이스 이름을 의미하며, 위 예제에서는 plughw:1,0이라는 디바이스 이름을 사용했지만, 라즈베리파이에서는 보통 default라는 이름으로 세팅되어 있습니다.

-f 옵션은 포맷(format)을 의미하며 보통 S16_LE 값을 사용하면 됩니다.

-d는 ‘duration’을 의미합니다. 위와 같이 사용하면 5초 동안 녹음합니다.

-r 옵션은 ‘sample-rate’ 값입니다.


aplay 사용법

위에서 녹음한 파일을 재생할 때는 다음과 같은 옵션으로 재생하면 됩니다.

aplay -t raw -c 1 -f S16_LE -r 16000 test.pcm

옵션이 의미하는 바는 arecord와 같습니다.

만약 pcm 파일이 아닌 일반 wav 파일이면 그냥

aplay filename

으로 실행해도 됩니다. wav 파일 안에 헤더 정보가 들어있기 때문입니다.

nano 사용법

|

Nano

리눅스 쉘 기반에서 사용할 수 있는 텍스트 편집기 프로그램 중 하나입니다. 개인적으로는 vi 보다 사용하기 편리한 것 같아서 애용하고 있습니다.


nano 실행 방법

명령어 설명
nano memo.txt memo.txt 파일을 Open
nano -B memo.txt 저장 전에 이전 파일을 ~.filename 으로 백업함
nano -m memo.txt 마우스를 이용해서 Cursor 이동 지원
nano -55 memo.txt memo.txt 파일의 55번 라인부터 시작


편집 모드에서 단축키

편집 모드에서는 방향키나 Ins, Del, Enter 등 다양한 키들을 사용할 수 있습니다.

단축키 설명
Ctrl + O 또는 F3 저장
Ctrl + X 또는 F2 nano 종료 및 저장
Ctrl + W 또는 F6 검색
Ctrl + \ 검색 및 Replace
Ctrl + G 도움말 실행


편집에 관련된 단축키

단축키 설명
Ctrl + K 또는 F9 현재 Line 또는 선택한 텍스트 잘라내기
Ctrl + U 또는 F10 붙여넣기
Ctrl + Y 또는 Page Up 이전 화면
Ctrl + V 또는 Page Down 다음 화면
Ctrl + - 원하는 Line으로 이동
Alt + ( 현재 문단의 시작으로
Alt + ) 현재 문단의 끝으로
Alt + \ 파일의 처음으로
Alt + / 파일의 끝으로

소켓 주소

|

모든 패킷에는 보내는 주소와 받는 주소가 필요합니다. 특히, 전송 계층의 패킷은 추가로 발신지와 수신지의 포트도 필요합니다. 이러한 정보는 sockaddr 구조체에 정의되어 있습니다.

sockaddr 구조체

MacOS 용 sockaddr

struct sockaddr {
  __uint8_t sa_len;        /* total length */
  sa_family_t sa_family;    /* [XSI] address family */
  char sa_data[14];    /* [XSI] addr value (actually larger) */
};

Cygwin 용 sockaddr

struct sockaddr {
  sa_family_t sa_family;    /* address family, AF_xxx	*/
  char sa_data[14];    /* 14 bytes of protocol address	*/
};

sa_family는 주소의 종류를 나타내는 상수값입니다. sa_data 필드에 바이트(Byte) 형태로 주소값이 저장됩니다.

sa_data에 들어갈 값은 sa_family 포맷에 따라 다양한 형태로 저장될 수 있기 때문에, 좀 더 편리하게 작성한 다음 캐스팅(Casting)을 통해 데이터를 변환하는 것을 추천합니다.

IPv4 패킷용 주소를 만들기 위해서는 sockaddr_in 자료형을 사용할 수 있습니다.


sockaddr_in 구조체

MacOS 용 sockaddr_in

struct sockaddr_in {
  __uint8_t sin_len;
  sa_family_t sin_family;
  in_port_t sin_port;
  struct in_addr sin_addr;
  char sin_zero[8];
};

Cygwin용 sockaddr_in

struct sockaddr_in {
  sa_family_t sin_family;    /* Address family		*/
  in_port_t sin_port;    /* Port number			*/
  struct in_addr sin_addr;    /* Internet address		*/

  /* Pad to size of `struct sockaddr'. */
  unsigned char __pad[__SOCK_SIZE__ - sizeof(short int)
      - sizeof(unsigned short int) - sizeof(struct in_addr)];
};

sin_family는 메모리 레이아웃상 sockaddrsa_family와 같은 기능을 합니다.

sin_addr는 4바이트의 IPv4 주소입니다. 소켓 라이브러리마다 조금씩 다르며, 어떤 플랫폼에서는 단순히 4바이트 정수이기도 합니다. 이 때문에 여러 플랫폼에서 여러 구조체를 유니온으로 감싸둔 구조체로 값을 지정할 수 있게 해 놓았습니다.

sin_len은 플랫폼에 따라 존재하기도 하는 변수인데 구조체의 길이를 지정하는 변수입니다. 다음과 같은 형태로 사용하면 됩니다.

addr.sin_len = sizeof(addr);

바이트 주소 체계 변환

IP 주소를 바이트 형태로 묶어서 사용할 때 서버/클라이언트간 바이트 순서 체계가 다를 수 있습니다. 이런 문제를 방지하게 위해서 바이트 값들은 네트워크 순서 쳬계로 변경해서 사용해야 합니다.

uint16_t htons(uint16_t hostshort);
uint32_t htonl(uint32_t hostlong);

htons()는 부호 없는 16비트 정수를 받아서 네트워크 바이트 순서로 변환합니다. htonl()은 32비트 정수에 대해서 변환을 수행합니다.

반대의 경우는

uint16_t ntohs(uint16_t networkshort);
uint32_t ntohl(uint32_t networklong);


예제 코드

다음과 같은 코드 형태로 사용할 수 있습니다.

Server Address

  struct sockaddr_in server_address;
  memset(server_address.sin_zero, 0, sizeof(server_address.sin_zero));
  server_address.sin_family = AF_INET;
  server_address.sin_addr.s_addr = htonl(INADDR_ANY);
  server_address.sin_port = htons(80);
  server_address.sin_len = sizeof(server_address);

여기서 INADDR_ANY는 서버로 들어오는 모든 데이터를 송수신하기위한 상수값입니다.


Client Address

  struct sockaddr_in client_address;
  memset(client_address.sin_zero, 0, sizeof(client_address.sin_zero));
  client_address.sin_family = AF_INET;
  client_address.sin_port = htons(80);
  client_address.sin_addr.s_addr = inet_addr("192.168.0.10");
  client_address.sin_len = sizeof(client_address);

inet_addr() 함수의 경우, htonl()을 사용할 필요 없이 문자열의 값을 long 값으로 변환해주는 함수입니다.