블로그 이미지
매몰

모바일 어플리케이션 개발 1인 기업 고영진모바일입니다. 저와 함께 맛깔스러운 앱을 만들어 보아요~

Rss feed Tistory
개발/android 2015.04.19 00:30

버튼 누름 효과를 만드는 2가지 방법





버튼이면 당연히 있어야할 누름효과!

눌렸을때와 누르지않았을때가 같다면 과연 버튼일까?


당연하지만 그래서 잃혀지기 쉬운 기능

버튼의 누름효과!!


안드로이드에서 기본적인 버튼은 누름효과가 있다.

하지만 나만의 예쁜 버튼을 만들려고 이미지를 넣으면 이런 효과는 사라진다.


이때 누름효과가 생기도록 하는 2가지 방법이 있다.




첫번째는 xml의 selector를 이용하는 것이다.


res의 drawable 폴더에 button_selector.xml 파일을 만든다.

여기서 버튼의 일반이미지, 누름이미지를 다음과 같이 설정한다.


<?xml version="1.0" encoding="utf-8"?>

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

    <item android:state_pressed="false" android:drawable="@drawable/button" />

    <item android:state_pressed="true" android:drawable="@drawable/button_press" />

</selector>



그리고 layout의 버튼에 selector를 적용한다.


<Button android:id="@+id/Button"

android:layout_width="61dp" android:layout_height="35dp" 

android:background="@drawable/button_selector"></Button>




두번째는 자바코드로 넣는 것이다.


Button button = (Button)findViewById(R.id.Button);

button.setOnTouchListener(new View.OnTouchListener() {

@Override

public boolean onTouch(View v, MotionEvent event) {

switch(event.getAction()) {

case MotionEvent.ACTION_UP:

case MotionEvent.ACTION_CANCEL:

v.setBackgroundResource(R.drawable.button);

break;

case MotionEvent.ACTION_DOWN:

v.setBackgroundResource(R.drawable.button_press);

}

return false;

}

});





 

 



수제 앱 장인: 고영진


고영진모바일 1인기업 대표 겸 개발자 

  

     실패만 하고 있어도 꿈을 포기하지 않는 남자 

     제가 직접 경험하고 습득한 지식을 위주로 올릴게요

 





사업자 정보 표시
고영진모바일 | 고영진 | 서울특별시 관악구 낙성대동 서울대연구공원 SK상생혁신센터 | 사업자 등록번호 : 109-11-82076 | TEL : 010-9990-3674 | Mail : gyjmeba@hanmail.net | 통신판매신고번호 : 2010-서울강서-0217호 | 사이버몰의 이용약관 바로가기
  • 혼마 2015.08.06 13:42 신고 ADDR 수정/삭제 답글

    이 두가지 방법에 차이점이 무엇인지 알려주실수 있나요?

    • 대세를 따르지 않고 대세를 만드는 매몰 2015.08.22 11:19 신고 수정/삭제

      xml의 selector 방식은 기본적으로 두장의 이미지로만
      보통과 누름을 표현해요

      하지만 자바코드 방식은 두장의 이미지로 뿐만 아니라
      한장의 이미지로 크기를 바꾸거나 투명도를 바꾸거나 하는식으로
      이미지리소스를 줄일 수 있고 또 무한한 응용이 가능해요

      즉, 간단한 기본적인 버튼을 만들시에는 xml로,
      더 확장된 버튼을 만들고자 할때는 자바코드가 좋죠

개발/android 2015.02.23 10:00

오로지 기울기 센서로만 가로세로 모드 체크하기




단말기를 가로 또는 세로모드로 고정시키고도 

현재 가로로 눞여있는지 세로로 서있는지 알아봐야 할때가 있다.


이럴 때는 기울기 센서라고도 불리는 가속도 센서(Accelerometer Sensor)를 이용하면 간단하다.


아래는 세로모드에서 카메라로 사진을 찍었을 경우

단말기 상황(각도)에 맞게 사진을 회전시키는 예의 일부이다.

mDegrees 각도로 사진을 회전시키면 된다. 




private SensorManager mSensorManager;

private Sensor mAccSensor;

private float mDegrees;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);


mSensorManager = (SensorManager)getSystemService(Context.SENSOR_SERVICE);

mAccSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);


mSensorManager.registerListener(this, mAccSensor, SensorManager.SENSOR_DELAY_UI);


mDegrees = 0;

}


@Override

protected void onDestroy() {

super.onDestroy();

mSensorManager.unregisterListener(this);

}


@Override

public void onSensorChanged(SensorEvent event) {

switch(event.sensor.getType()) {

case Sensor.TYPE_ACCELEROMETER:

float x = event.values[0];

float y = event.values[1];

//회전시킬 각도 저장

//단말기마다 x,y 기준 수치는 다를 수 있음

if(x > 5 && y < 5)

mDegrees = 0;    //세로로 슴

else if(x < -5 && y > -5)

mDegrees = 180;  //세로로 뒤집힘

else if(x > -5 && y > 5)

mDegrees = -90;  //가로 왼쪽으로 눞힘

else if(x < 5 && y < -5)

mDegrees = 90;   //가로 오른쪽으로 눞힘

break;

}

}




 

 



수제 앱 장인: 고영진


고영진모바일 1인기업 대표 겸 개발자 

  

     실패만 하고 있어도 꿈을 포기하지 않는 남자 

     제가 직접 경험하고 습득한 지식을 위주로 올릴게요

 





사업자 정보 표시
고영진모바일 | 고영진 | 서울특별시 관악구 낙성대동 서울대연구공원 SK상생혁신센터 | 사업자 등록번호 : 109-11-82076 | TEL : 010-9990-3674 | Mail : gyjmeba@hanmail.net | 통신판매신고번호 : 2010-서울강서-0217호 | 사이버몰의 이용약관 바로가기
개발/android 2014.01.20 17:32

안드로이드 앱 런칭후 데이터베이스 변경시 주의할점


안드로이드 데이터베이스



단말기의 로컬 데이터베이스를 사용한 앱을 개발하다 보면 한번쯤은 겪게 되는것이 있다

바로 출시후 테이블 또는 필드추가, 내용추가변경이다


나도 처음에는 별생각없이 출시했다가 업데이트를 해야 하는데 디비를 바꾸어야 하는 상황이 있었다. 


그래서 다 아시다시피 생성자에서의 버전을 전숫자보다 크게 넣고 onUpgrade() 에서 업데이트를 했다...


그런데 디비 버전이 한번이 아니라 여러번 바뀔경우 

만약 사용자가 디비 1버전의 앱에서 사용하다가 오랬동안 사용을 안하고 곧바로 디비 3버전의 앱을 업데이트 받아 사용할경우 2버전의 디비변경사항도 적용해주어야 함을 늦게서야 깨달았다ㅎㅎ


당시에는 분명 디비 변경을 해줫는데 왜 그러나... 하고 엄청 심각했었는데 원인을 알고 나니 좀 허무했다ㅎㅎ


어잿든 아래와 같이 스위치문에 전버전숫자를 넣고 break를 뺀 구조로 해결했다~ 

이렇게 하면 어떤 디비 버전에서도 전 변경사항이 모두 적용됨을 확인했다^^


switch(oldVersion) {

case 1:

//2 버전의 변경내용...

case 2:

//3버전의 변경내용...

case 3:

//4버전의 변경내용...

}





끝으로 혹시나해서 이해를 돕고자 당시 문제가 되었던 그앱의 해결된 소스를 약간 변경해서 올려봐요~



private static final int DATABASE_VERSION = 4;    //업데이트 할 경우 숫자를 하나씩 올려준다


private static class DatabaseHelper extends SQLiteOpenHelper {

DatabaseHelper(Context context) {

          super(context, DATABASE_NAME, null, DATABASE_VERSION);

      }


public void onCreate(SQLiteDatabase db) {

db.execSQL("create table " + TABLE_NAME + "("

+ FIELD_ROWID + " integer primary key , "

+ FIELD_HOLESIZE + " integer , "

+ FIELD_SPEED + " integer , "

+ FIELD_VOLUME + " float , "

+ FIELD_MEDIAVOLUME + " float , "

+ FIELD_LANGUAGE + " boolean , "

+ FIELD_HIGHESTSCORE + " long , "

+ FIELD_MODE + " integer , "

+ FIELD_DLG + " text);");


//...

}


@Override

public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {


ContentValues args;

switch(oldVersion) {

case 1:

  //필드 추가

db.execSQL("alter table " + TABLE_NAME + " add " + FIELD_COMBO + " integer");


        //내용 추가

  args = new ContentValues();

args.put(FIELD_LOCATION, location);

        db.insert(TABLE_FAVORITE, null, args)


case 2:

  //필드 추가

db.execSQL("alter table " + TABLE_NAME + " add " + FIELD_OBJECT + " integer");


 //내용 변경

 args = new ContentValues();

     args.put(FIELD_APPWIDGET_UPDATECOUNT, "");

     db.update(TABLE_NAME, args, FIELD_ROWID + "=" + STATE_ROWID, null);


case 3:

 //필드 추가

db.execSQL("alter table " + TABLE_NAME + " add " + FIELD_MEDIAVOLUME + " float");


 //테이블 추가

 db.execSQL("create table " + TABLE_NAME_1 + "("

+ FIELD_ROWID + " integer primary key , "

+ FIELD_HOLESIZE + " integer , "

+ FIELD_SPEED + " integer , "

+ FIELD_VOLUME + " float , "

+ FIELD_MODE + " integer , "

+ FIELD_DLG + " text);");

    }


}


}






 

 



수제 앱 장인: 고영진


고영진모바일 1인기업 대표 겸 개발자 

  

     실패만 하고 있어도 꿈을 포기하지 않는 남자 

     제가 직접 경험하고 습득한 지식을 위주로 올릴게요

 





사업자 정보 표시
고영진모바일 | 고영진 | 서울특별시 관악구 낙성대동 서울대연구공원 SK상생혁신센터 | 사업자 등록번호 : 109-11-82076 | TEL : 010-9990-3674 | Mail : gyjmeba@hanmail.net | 통신판매신고번호 : 2010-서울강서-0217호 | 사이버몰의 이용약관 바로가기
  • 박정기 2014.01.21 17:21 신고 ADDR 수정/삭제 답글

    좋은 정보 감사합니다.^^ 저도 이 문제로 골치아팠었는데 break문 유무에 따라서 쉽게 해결될수가 있네요. 자주 들릴게요~~

개발/android 2013.10.04 10:45

큰 배경이미지 사용시 OutOfMemoryError의 확실한 해결법

안드로이드 앱을 만들때 배경이미지를 넣어야 하는 경우 OutOfMemoryError에 직면하는 경우가 많다.. 


특히 사이즈가 큰 배경이미지 일수록... 자주 발생한다...


즉, OutOfMemoryError는 이름처럼 이미지를 로딩할때 메모리가 부족해서 발생하는것이다...


이럴경우 그원인이 되는 이미지 사이즈 자체를 줄이거나 로딩할때 아래와 같이 사이즈를 줄여서 불러오면 일단 해결은 된다.



 


BitmapFactory.Options options = new BitmapFactory.Options();

options.inSampleSize = 2;


Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.opening, options);





하지만 이방법은 치명적 단점이 있다.


사이즈를 줄이므로 이미지가 선명하지 못하고 깨져서 보이며, 보통 LinearLayout과 같은 배경으로 많이 쓰이는 view에는 setBackgroundDrawable() 뿐이라서 bitmap을 Drawable로 변환해야 하는 번거로움이 있다...


이 두가지 단점을 한번에 해결하는 방법은 과연 없을까?


있다. 나도 항상 위와 같은 방법을 쓰다가 영 마음에 안들어서 여러 테스트를 거쳐 알아냈다...

물론 나중에 안거지만 다른분들 포스팅을 보니 이미 그렇게 사용하고 계시는 분들이 있으셨다..

역시 세상에는 코딩신님들이 많은것 같다ㅎㅎ 난 무한 샆질끝에 알아냈다고 좋아했는데ㅎㅎㅎ


방법은 무지 간단하다...





LinearLayout layout = (LinearLayout)findViewById(R.id.BgLayout);


layout.setBackgroundDrawable(new BitmapDrawable(getResources(), BitmapFactory.decodeResource(getResources(), R.drawable.opening)));





이렇게 로딩을 해서 바로 집어넣고 onDestroy()에서 메모리를 풀어주면 된다...





@Override

public void onDestroy() {

recycleView(findViewById(R.id.BgLayout));

}


private void recycleView(View view) {

if(view != null) {

Drawable bg = view.getBackground();

if(bg != null) {

bg.setCallback(null);

((BitmapDrawable)bg).getBitmap().recycle();

view.setBackgroundDrawable(null);

}

}

    }





참고로


xml에서 android:background="@drawable/opening" 로 바로 넣거나 layout.setBackgroundResource(R.drawable.opening) 또는  getResources().getDrawable(R.drawable.opening) 로 이미지를 가져오면 원본을 그대로 사용하는것이라서 메모리도 많이 잡아먹고 recycle를 하게 되면 다음번 실행때 에러가 난다...


결론은...


반드시 위와 같이 new로 복사본을 새로 생성해서 사용해야 메모리도 적게 먹고 recycle도 할수 있어서 OutOfMemoryError를 예방할 수 있다....





도움이 되셨다면 구독해주세요~ 유용하고 좋은글 많이 올릴게요~ㅎㅎ






 

 매몰: 고영진 대표/개발자


 고영진모바일 1인기업의 하나뿐인 사람. 

 그밖에 동반자 노트북,스마트폰 

     실패만 하고 있어도 꿈을 포기하지 않는 남자. 

     제가 직접 경험하고 습득한 지식을 위주로 올릴게요. 







사업자 정보 표시
고영진모바일 | 고영진 | 서울특별시 관악구 낙성대동 서울대연구공원 SK상생혁신센터 | 사업자 등록번호 : 109-11-82076 | TEL : 010-9990-3674 | Mail : gyjmeba@hanmail.net | 통신판매신고번호 : 2010-서울강서-0217호 | 사이버몰의 이용약관 바로가기
  • yonoo88 2013.12.14 22:56 신고 ADDR 수정/삭제 답글

    오 이 문제 때문에 고생하다가 메니페스트에 android:largeHeap="true" 로 줘서 문제를 해결했었는데 그래도 간간히 아웃오브메모리 에러 신고가 들어와서 어케하나 싶었는데 한번 해봐야겠네요

  • yonoo88 2013.12.16 02:28 신고 ADDR 수정/삭제 답글

    layout.setBackgroundDrawable(new BitmapDrawable(getResources(),

    이 부분에서 setBackgroundDrawable 이게 가운데 줄그어져서 나오는데 어케해야하죠?

  • 건프 2014.02.10 10:32 신고 ADDR 수정/삭제 답글

    몇가지 잘못된게 있군요 일단은 setbackground 는 젤리빈이후부터 사용가능입니다.
    4.0 아샌부터가 아닙니다..
    그리고 저렇게 내부이미지 리소스를 리사이클하게되면 다시 다른곳에서 사용할때
    널포인터 입섹션날겁니다. 내부이미지 리소스는 리사이클하지 않는게 원칙입니다.

    • 대세를 따르지 않고 대세를 만드는 매몰 2014.02.11 10:02 신고 수정/삭제

      좋은지적 감사합니다~
      setbackground는 젤리빈 이후가 맞아요ㅎㅎ
      그리고 context.getResources().getDrawable(R.drawable.leftarrow) 등과 같이 내부리소스를 그대로 사용하는 경우에는 다른곳에서 널포인트가 되지만 본문에서처럼 new BitmapDrawable(getResources(), BitmapFactory.decodeResource(getResources(), R.drawable.opening)) 식으로 내부리소스를 로딩해 복사본을 생성하면 로딩된 복사본만 리사이클되기 때문에 원칙에 어긋나지 않습니다..
      저도 처음에 널포인트를 경험하고 여러 테스트를 거처 찾아낸 방법이에요ㅎㅎ 한번 해보시고 문제가 될시에는 다시 말씀해주세요~

  • 혼마 2015.08.06 13:39 신고 ADDR 수정/삭제 답글

    안녕하세요. 안드로이드 초보 개발자입니다.
    우선 좋은글 감사드립니다. 덕분에 사이즈가 큰 이미지를 배경화면으로 넣는 문제를 해결하였습니다.
    그런데 한가지 질문이 있습니다.
    1.2MB짜리 이미지를 배경으로 심었는데 처음에는 무리없이 잘 돌아갔습니다.
    그런데 앱 개발과정중에 몇번씩 빌드하고 실행하다보니 다른 액티비티가 많이 생긴 후 건프님 말씀처럼 널포인터 익셉션이 일어났습니다.
    소스 일부 첨부하겠습니다.
    LinearLayout loginLayout = (LinearLayout) findViewById(R.id.login_layout);
    loginLayout.setBackground(new BitmapDrawable(getResources(), BitmapFactory.decodeResource(getResources(), R.drawable.login_background)));

    @Override
    public void onDestroy() {
    super.onDestroy();
    recycleView(findViewById(R.id.login_layout));
    }

    private void recycleView(View view) {
    if (view != null) {
    Drawable bg = view.getBackground();
    if (bg != null) {
    bg.setCallback(null);
    ((BitmapDrawable) bg).getBitmap().recycle();
    view.setBackgroundDrawable(null);
    }
    }
    }

    에러 코드입니다.
    08-06 13:30:46.883 23782-23782/com.honma.mytestapp E/AndroidRuntime﹕ FATAL EXCEPTION: main
    Process: com.honma.mytestapp, PID: 23782
    java.lang.RuntimeException: Unable to destroy activity {com.honma.mytestapp/com.honma.mytestapp.IntroActivity}: java.lang.NullPointerException
    at android.app.ActivityThread.performDestroyActivity(ActivityThread.java:3851)
    at android.app.ActivityThread.handleDestroyActivity(ActivityThread.java:3869)
    at android.app.ActivityThread.access$1500(ActivityThread.java:170)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1357)
    at android.os.Handler.dispatchMessage(Handler.java:102)
    at android.os.Looper.loop(Looper.java:146)
    at android.app.ActivityThread.main(ActivityThread.java:5635)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:515)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1291)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1107)
    at dalvik.system.NativeStart.main(Native Method)
    Caused by: java.lang.NullPointerException
    at com.honma.mytestapp.IntroActivity.recycleView(IntroActivity.java:62)
    at com.honma.mytestapp.IntroActivity.onDestroy(IntroActivity.java:54)
    at android.app.Activity.performDestroy(Activity.java:5752)
    at android.app.Instrumentation.callActivityOnDestroy(Instrumentation.java:1123)
    at android.app.ActivityThread.performDestroyActivity(ActivityThread.java:3833)
                at android.app.ActivityThread.handleDestroyActivity(ActivityThread.java:3869)
                at android.app.ActivityThread.access$1500(ActivityThread.java:170)
                at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1357)
                at android.os.Handler.dispatchMessage(Handler.java:102)
                at android.os.Looper.loop(Looper.java:146)
                at android.app.ActivityThread.main(ActivityThread.java:5635)
                at java.lang.reflect.Method.invokeNative(Native Method)
                at java.lang.reflect.Method.invoke(Method.java:515)
                at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1291)
                at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1107)
                at dalvik.system.NativeStart.main(Native Method)

    • 대세를 따르지 않고 대세를 만드는 매몰 2015.08.22 12:25 신고 수정/삭제

      에러코드를 보면 ((BitmapDrawalbe)bg).getBitmap().recycle(); 에서
      널 포인트가 잡힌 듯 하네요

      이는 getBitmap() 에서 bitmap 객체가 널이기 때문인것으로 보여요
      전체 소스가 어떤지 알수 없어 정확히는 모르겠지만
      이부분만 본다면 bitmap을 recycle()하기 전 또는 후에 다른곳에서
      널을 줬을 가능성이 있을것 같아요

      이를 미연에 방지하기 위해서
      Bitmap bitmap = ((BitmapDrawalbe)bg).getBitmap();
      if(bitmap != null)
      bitmap.recycle();
      요런식으로 고쳐 써 보심이 어떨까 싶네요

      알구... 역시 저는 아직 멀었나 봅니다ㅎㅎ
      찾지못한 버그에 항상 마음이 아프네요ㅠㅠ

  • 혼마 2015.08.06 16:00 신고 ADDR 수정/삭제 답글

    아 그리고 한가지 더 있습니다.
    안드로이드 5.0.0 버전에서 1.2M 크기의 이미지를 배경화면으로 설정하기 위해 위의 소스를 이용했으나, 적용되지 않았습니다.
    또한 원래 4.4.4 버전에서 처음에는 됐으나 나중에는 로드되지 않는 현상이 발생했습니다.
    혹시 이미지 크기가 너무 커서 그런건가요? 아니라면 다른 이유가 있을지 궁금합니다.

    • 대세를 따르지 않고 대세를 만드는 매몰 2015.08.22 14:32 신고 수정/삭제

      제가 보기에는 버전도 무시할 순 없지만
      기기별 문제가 더 크지 않나 싶어요

      여러종류의 단말기로 개발을 하다 보면 버전이 같아도
      큰이미지가 괞찮은 폰이 있고 바로 죽는 폰도 있었죠

      그래서 저도 용량이 큰 통이미지를 그대로 잘 쓰진 않아요
      잘게 부셔서 여러 뷰에 담죠ㅎㅎ

      어잿든 혼마님께서 지직해 주신 내용을 바탕으로
      살펴보고 더 보안도록 해보겠습니다

      하지만 어디까지나 프로그래머의 역량은
      퀄러터를 최대한 유지한체 리소스를 최소화 하는 것이에요
      작은 이미지를 써서 최대한 퀄러티있게 보이게 하는 방법이
      더 좋은 방법이죠~

  • 찾는이 2015.12.31 15:46 신고 ADDR 수정/삭제 답글

    안녕하세요! 메모리 관련 오류찾다가 들리게되었습니다. 좋은글이라 후에 참조할까 싶어 링크를 가져가도 될까 해서 덧글 남겼습니다!

    조금은 제게 난이도가 있어보여서 일단은 이미지 사이즈를 줄이는것으로 해결을 봐두었습니다. 아무쪼록 필요하면 또 들리겠습니다^^

TOTAL 69,581 TODAY 10