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");
    }
  };
}

Pycharm CE 버전을 이용한 Django 개발하기

|

Pycharm 유료 버전은 Django를 정식으로 지원하지만, CE(Community Edition) 버전은 지원하지 않습니다. 하지만, 사용자 설정을 통해서 CE버전에서도 Django 빌드 및 디버깅을 할 수 있습니다.


프로젝트 생성

먼저 터미널에서 다음 명령어를 이용해서 Django 프로젝트를 생성합니다. (프로젝트 생성은 그냥 콘솔에서 하는게 더 편합니다.)

django-admin.py startproject sample


Pycharm에서 해당 프로젝트 열기

위에서 생성한 프로젝트 폴더를 Pycharm을 통해서 엽니다.


실행 환경 구성

바로 실행할 수 없기 때문에 아래 메뉴에 접근해서 실행 환경을 구성해야 합니다.

Run > Edit Configurations

실행 환경 창이 뜨면 좌측 상단의 + 버튼을 눌러서 새로운 항목을 추가합니다. Python 항목을 선택합니다.

  • Name: runserver(아무거나 원하는대로 정하면 된다.)
  • Script Path: /Users/snowdeer/Workspace/python/django/sample/manage.py
  • Parameters: runserver

Java Reflection 예제

|

Java 언어에서 제공하는 Reflection 기능을 이용해서 각 클래스의 생성자 정보 등을 출력하는 예제입니다.


Main.java

package snowdeer.reflection;

public class Main {

  public static void main(String[] args) {
    ReflectionTest test = new ReflectionTest();
    test.start();
  }
}


ReflectionTest.java

package snowdeer.reflection;

import java.lang.reflect.Constructor;
import java.lang.reflect.Parameter;

public class ReflectionTest {

  public void start() {
    Class c = SampleClass.class;

    Log("1) Class Info");
    Log("   - " + c.toString());

    Log("2) Class Constructors & Parameters");
    Constructor[] cons = c.getDeclaredConstructors();
    for (int i = 0; i < cons.length; i++) {
      Log("   - " + cons[i].toString());

      Parameter[] params = cons[i].getParameters();
      for (int j = 0; j < params.length; j++) {
        Log("      > " + params[j].getType());
      }
    }
  }

  void Log(String text) {
    System.out.println(text);
  }
}


SampleClass.java

package snowdeer.reflection;

public class SampleClass {

  int id;
  String name;
  long startPos;
  float speed;

  public SampleClass(int id, String name, long startPos, float speed) {
    init(id, name, startPos, speed);
  }

  public SampleClass(int id, String name, long startPos) {
    init(id, name, startPos, 0);
  }

  public SampleClass(int id, String name) {
    init(id, name, 100, 0);
  }

  public SampleClass(int id) {
    init(id, "SampleClass", 100, 0);
  }

  public SampleClass() {
    init(1, "SampleClass", 100, 0);
  }

  void init(int id, String name, long startPos, float speed) {
    this.id = id;
    this.name = name;
    this.startPos = startPos;
    this.speed = speed;
  }
}


결과 화면

1) Class Info
   - class snowdeer.reflection.SampleClass
2) Class Constructors & Parameters
   - public snowdeer.reflection.SampleClass()
   - public snowdeer.reflection.SampleClass(int)
      > int
   - public snowdeer.reflection.SampleClass(int,java.lang.String)
      > int
      > class java.lang.String
   - public snowdeer.reflection.SampleClass(int,java.lang.String,long)
      > int
      > class java.lang.String
      > long
   - public snowdeer.reflection.SampleClass(int,java.lang.String,long,float)
      > int
      > class java.lang.String
      > long
      > float