티스토리 뷰

Android/Android TIP

코딩 팁

이주성 2012. 10. 6. 00:25

Drawable을 Bitmap으로 변환   

  1. private int mTileSize = 24; // icon 사이즈
  2. private Bitmap[] mTileArray;
  3. public void loadTile (int key, Drawable tile) {
            //create a canvas using a blank bitmap
            Bitmap bitmap = Bitmap.createBitmap(mTileSize, mTileSize, Bitmap.Config.ARGB_8888);
            Canvas canvas = new Canvas (bitmap);
  4.         // Drawable을 Bitmap의 canvas에 그린다. 그러면 Drawable이 Bitmap에 그려진다.

            tile.setBounds (0, 0, mTileSize,mTileSize);
            tile.draw (canvas);
            mTileArray[key] = bitmap;

  5. }

또는 곧바로 Resource에서 만드는 방법도 있다.

  1.  
  2.  Resources res = context.getResources();
     BitmapFactory.Options opts = new BitmapFactory.Options();
     opts.inPreferredConfig = Bitmap.Config.ARGB_8888;
     int len = DRAWABLE_IDS.length;
     mCenterBackgroundBitmap[0] = BitmapFactory.decodeStream(
        res.openRawResource(R.drawable.btn_unlock_b_normal), null, opts);

Thread가 종료할때까지 무조건 기다리기#

main thread가 다른 thread가 끝날때까지 무조건 기다려야 할때가 있다. 이럴때는 Thrad.join()을 쓰면 되잖아?라고 말할수도 있지만 그건 틀린말이다. thread.join()일때 interruptedException이 발생하면 join중에 빠져나갈수가 있다. (일시정지상태에서 interruptedException이 발생하면 thread가 다시 실행상태가 된다.) 기다려야 되는데 말이다. 이럴때는 어떻게

할까? interruptedException이 발생하면 다시 while문으로 돌려서 thread.join()을 다시 실행해주면 된다.

  1. /    * Callback invoked when the Surface has been destroyed and must no longer
         * be touched. WARNING: after this method returns, the Surface/Canvas must
         * never be touched again!
         */
  2. public void surfaceDestroyed(SurfaceHolder holder) {
            // we have to tell thread to shut down & wait for it to finish, or else
            // it might touch the Surface after we return and explode

            boolean retry = true;
            thread.setRunning(false);
            while (retry) {
                try {
                    thread.join();
                    retry = false;
                } catch(InterruptedException e) {
                }
            }
        }

Custom Styleable#

아래는 namespace를 안쓰는 간단한 방법입니다. custom namespace를 쓰고 custom styleable을 쓸려면 이 링크를 참고하세요 .http://blog.pocketjourney.com/2008/05/02/android-tutorial-42-passing-custom-variables-via-xml-resource-files/

res/values/attrs.xml

  1. <resources>
      <declare-styleable name="TileViewj">
        <attr name="tileSize" format="integer" />
      </declare-styleable>
    </resources>

res/layout/main.layout

  1. <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent">
       
        <com.example.android.snake.TileView
             android:id="@+id/snake"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
                    tileSize="24"
                    />

src/com/example/android/snake/TileView.java

  1. public TileView(Context context, AttributeSet attrs) {
            super(context, attrs);
            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TileView);
            mTileSize = a.getInt(R.styleable.TileView_tileSize, 12);       
            a.recycle();
        }
  2.  

Bundle에 Class를 저장 (Parcelable)#

Activity가 Hide될때 onSaveInstanceState(Bundle bundle)함수와 onCreate(Bundle), onRestoreInstanceState (Bundle)함수를 이용해서 인스턴스의 현재상태를 저장한다.  Bundle클래스 설명서를 보면 int, long, String등 기본 데이타타입들을 위한 함수를 제공하고 있다. 그런데 과연 내가 만든 Custom Class는 어떻게 상태를 저장할까??? 이럴때를 대비해 Bundle클래서에서는 Parcelable 기능을 제공한다. 내가 만든 custom class는 Parcelable interface를 상속하여 구현하면 Bundle에 통채로 넣을수 있다. 한번 상속하여 구현해보자.

  1. public class Coordinate implements Parcelable{
            public int x = 0;
            public int y = 0;
           
            public Coordinate (int _x, int _y) {
                x = _x;
                y = _y;
            }
            private Coordinate (Parcel in) {
                x = in.readInt ();
                y = in.readInt ();
            }
  2.         public void writeToParcel (Parcel out, int flags) {
                out.writeInt(x);
                out.writeInt(y);
            }
            public int describeContents () {
                return 0;         }
            public final Parcelable.Creator<Coordinate> CREATOR = new Parcelable.Creator<Coordinate>() {
                public Coordinate createFromParcel (Parcel in) {
                    return new Coordinate (in);
                }
                public Coordinate[] newArray (int size) {
                    return new Coordinate[size]             }
            };
  3. }

그럼 이것들을 쓸때는 어떻게 쓸까??? 저장할때와 복구할때를 예를 들어서 써보자. Parceable을 쓰면 Custom class를 Bundle에 저장하는 방법이 이렇게 간단해진다. for문을 쓰고 복잡하던 코드가 이렇게 두줄로서 해결된다.

  1. private ArrayList<Coordinate> mSnakeTrail = new ArrayList<Coordinate>();
  2. public Bundle saveInstanceState (Bundle bundle) {
            bundle.putParcelableArrayList("coord", mSnakeTrail);
            bundle.putInt("direction", mDirection);
            return bundle;
            }   
        public void restoreInstaceState (Bundle bundle) {
            mSnakeTrail = bundle.getParcelableArrayList("coord");
            mDirection = bundle.getInt("direction");
        }

한글/영어 Table을 Quary시에 sort하기#

SQLite에서 sort에 관한 설명

Orientation#

아래의 주요 시스템 세팅 변경은 Activity 의 onConfigurationChanged으로 감지가 되어 실시간으로 프로그램이 대응하는 것이 가능합니다.

또는 OrientaionEventListener를 사용하는 방법도 있다. 그러나 onConfigurationChanged를 쓰는 것이 더 깔끔할듯...
orientation: 스크린이 세로에서 가로로 바뀌는 변경사항.
keyboardHidden: 키보드가 보여지거나 숨겨지는 변경사항 .
fontScale: 사용자가 원하는 폰트의 크기를 변경하는 것.
locale: 사용자가 언어 세팅을 변경하는것.
keyboard: 키보드의 종류가 바뀌는 사항

이를 위해서는 Manifest 파일의 해단 Activity 의 옆에 아래와 같이 android:configChanges 를 먼저 선언합니다.

          <activity android:name="RosaryList" android:configChanges="keyboardHidden|orientation"></activity>
          
          @Override
          public void onConfigurationChanged(Configuration newConfig) {
              super.onConfigurationChanged(newConfig);
              [ ... Update any UI based on resource values ... ]
              if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
                  [ ... React to different orientation ... ]
              }
              if (newConfig.keyboardHidden == Configuration.KEYBOARDHIDDEN_NO) {
                  [ ... React to changed keyboard visibility ... ]
              }
          }
          

singleInstance 와 singleTop#

PhoneApp는 대표적인 singleInstance 어플리케이션이다. 즉 한번에 한 instance만 뜨고 다른 어플에서 startActivity로 요청을 해도 2개의 instance가 뜨지 않는다. 그렇다면 2번째 startActivity를 요청하면 어떻게 반응할까? 그것은 바로 Activity.onNewIntent ()함수가 호출되어서 새로받은 Intent를 분석하여 새로운 View를 만들어 작동하는 것이다.

그럼 내가 만드는 Activity를 singleInstance로 만들고 onNewIntent로 반응하고 싶으면 어떻게 해야할까? 아래의 코드는 LocalActivityManager.startActivity의 코드이다. AndroidMenifest.xml파일에 띄우는 activity에 launchMode="singleInstance"를 쓰거나 addFlag (Intent.FLAG_ACTIVITY_SINGLE_TOP)을 쓰면 새로 activity를 띄우지 않고 재사용하면서 onNewIntent()를 호출해준다.

  1. if (aInfo.launchMode != ActivityInfo.LAUNCH_MULTIPLE || <-- launchMode가 singleInstance이고
    (intent.getFlags()&Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0) { <-- flag가 FLAG_ACTIVITY_SINGLE_TOP이면

    // The activity wants onNewIntent() called.
    ArrayList<Intent> intents = new ArrayList<Intent>(1);
    intents.add(intent);
    if (localLOGV) Log.v(TAG, r.id + ": new intent");
    mActivityThread.performNewIntents(r, intents);
    r.intent = intent;
    moveToState(r, mCurState);
    if (mSingleMode) {
    mResumed = r;
    }
    return r.window;

ActivityGroup과 optionMenu#

  1. public boolean onCreateOptionsMenu(Menu menu) {
    final int orientation = Email.getInstance().getOrienation();
    switch (orientation) {
    case Configuration.ORIENTATION_PORTRAIT:
    if (mMailboxId < 0) {
    getMenuInflater().inflate(R.menu.message_list_option_smart_folder, menu);
    } else {
    getMenuInflater().inflate(R.menu.message_list_option, menu);
    }
    return true;
    case Configuration.ORIENTATION_LANDSCAPE:
    return super.onCreateOptionsMenu(menu);
    }
    return false;
    }

  1. public boolean onCreateOptionsMenu(Menu menu) {
    if (mParent != null) {
    return mParent.onCreateOptionsMenu(menu);
    }
    return true;
    }

Timer#

안드로이드 SDK에서 일정시간 후에 일어나는 혹은 주기적인 작업처리 방법에 대해서 알아보겠습니다. 이러한 Timing 작업을 처리하기위해 Handler와 AlarmManager를 사용할 수 있는데 둘 간의 차이점을 잘알고 사용하셔야 합니다.

먼 저 AlarmManager는 현재 특정 어플리케이션이 실행되고 있지 않더라도 특정 시점에 해당 어플의 코드가 실행되도록 할때 사용됩니다. 현재 내 어플리케이션의 Activity가 보여지고 실행되고 있는 상황에서 타이밍 작업을 할때에는 Handler를 사용하는 것이 바람직합니다. AlarmManager는 단말이 슬립모드에 들어가있을 경우에도 단말을 깨워서 작업처리를 할 때 사용할 수 있습니다. (RTC의 Interrupt를 사용한다고 생각하시면 됩니다.)

거의 대부분 UI상에서의 타이밍 작업은 Handler로 이루어집니다. Handler는 특정 시간후에 작업을 처리하기 위해서 사용되는 경우와 UI 쓰레드상에서의 코드 실행을 위해서 사용되는 경우가 있습니다. 두번째 특징에 있어서 Handler는 java.util.Timer와도 구분된다고 할 수 있습니다. 대부분의 View등 UI와 관련된 클래스는 UI 쓰레드에서 호출해주어야만 정상 작동합니다. 특정 시간후에 일어나는 작업이 메인 UI와 관련된 함수들을 호출한다면 Handler를 사용해야 합니다.

가장 편하게 사용할 수 있는 것은 Runnable을 통해 작업을 처리하는 것입니다.

Handler mHandler = new Handler();
mHandler.postDelayed(new Runnable() {
//Do Something
}, 3000);

일정시간후에 특정 View에 작업을 하는 경우 해당 View의 Thread로 Runnable을 보내기 위해서는 View의 postDelayed함수를 사용해도 비슷한 역할을 합니다.

View.postDelayed(Runnable action, long delayMillis)

AlarmManager는 현재 실행되고 있지 않더라도 어플을 다시 불러와 실행시켜야할 경우에 사용됩니다.

final Intent intent = new Intent(context, MyService.class);

final PendingIntent pending = PendingIntent.getService(context, 0, intent, 0);
Calendar cal = nextScheduleTime();
AlarmManager am = (AlarmManager)context.getSystemService(ALARM_SERVICE);
am.setRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), 5 * 60 * 1000, pending);

AlarmManager 는 일정 시간후에 해당 Intent를 Broadcast해줄 수 있게 설정할수 있습니다. 위의 예제에서는 특정 시간에 MyService라는 서비스를 Start하도록 코딩되어있습니다. 실행되는 시간은 Calendar 클래스를 통해서 편리하게 시간을 설정하실 수도 있습니다. 백그라운드에서 주기적으로 동작하는 어플리케이션에서도 AlarmManager를 통해 잘 설정해주어야 합니다.

  1. TimerTask task = new TimerTask () {
  2. public void run () {
  3. //Do someting
  4. }
  5. }
  6. Timer timer = new Timer ();
  7. timer.schedule (task, 5000);

DIP를 Pixel로 변환#

그래픽 관련 메소드는 대부분 Pixel을 인자로 받는다. 그렇다고 상수를 항상 Pixel로 정의하지 말자. 상수를 DIP로 정의하고 변환해서 쓰자. layout.xml도 항상 DIP로 선언하자.

TypedValue api를 사용한 방법

  1. private static final float GESTURE_THRESHOLD_DIP = 16.0f; //상수 정의
    mGestureThreshold = TypedValue.applyDimension(
    TypedValue.COMPLEX_UNIT_DIP,
    GESTURE_THRESHOLD_DIP,
    getResources().getDisplayMetrics());

TypedValue api를 사용안하고 직접계산하기

  1. private static final float GESTURE_THRESHOLD_DIP = 16.0f; //상수 정의
    final float scale = getContext().getResources().getDisplayMetrics().density;
    mGestureThreshold = (int) (GESTURE_THRESHOLD_DIP * scale + 0.5f);

Dimensions 리소스 이용 #

res/values/dimension.xml

  1. <resources>
  2. <dimem name="length">20dip</dimem>
  3. </resources>

java code

  1. int length = getResources().getDimensionPixelSize(R.dimen.length);


'Android > Android TIP' 카테고리의 다른 글

TextUtils.isEmpty Mockup 만들기 (JUnit)  (0) 2018.04.12
Implicit Intent broadcast using a custom permission  (0) 2018.04.10
Processing Ordered Broadcasts  (0) 2013.08.21
DP and SP  (0) 2013.07.04
Android TIP  (0) 2012.09.28
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/11   »
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
글 보관함